Implemented a primitive decorator, allowing groups of primitives to be easily resized.

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@2027 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavier
2013-02-08 22:05:15 +00:00
parent 73fa01c7b9
commit 2bed00fd25
21 changed files with 1566 additions and 124 deletions

View File

@@ -44,6 +44,61 @@ QUndoStack &CustomElementPart::undoStack() const {
return(elementScene() -> undoStack());
}
/// @return this primitive as a QGraphicsItem
QGraphicsItem *CustomElementPart::toItem() {
return(dynamic_cast<QGraphicsItem *>(this));
}
/**
This method is called by the decorator when it manages only a single
primitive. This brings the possibility to implement custom behaviour, such
as text edition, points edition or specific resizing.
The default implementation does nothing.
*/
void CustomElementPart::setDecorator(ElementPrimitiveDecorator *decorator) {
Q_UNUSED(decorator)
}
/**
This method is called by the decorator when it manages only a single
primitive and it received a mouse press event.
The implementation should return true if the primitive accepts the event, false otherwise.
The default implementation returns false.
*/
bool CustomElementPart::singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
return(false);
}
/**
This method is called by the decorator when it manages only a single
primitive and it received a mouse move event.
The implementation should return true if the primitive accepts the event, false otherwise.
The default implementation returns false.
*/
bool CustomElementPart::singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
return(false);
}
/**
This method is called by the decorator when it manages only a single
primitive and it received a mouse release event.
The implementation should return true if the primitive accepts the event, false otherwise.
The default implementation returns false.
*/
bool CustomElementPart::singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
return(false);
}
/**
This method is called by the decorator when it manages only a single
primitive and it received a mouse double click event.
The implementation should return true if the primitive accepts the event, false otherwise.
The default implementation returns false.
*/
bool CustomElementPart::singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *) {
return(false);
}
/**
Helper method to map points in CustomElementPart::handleUserTransformation()
@param initial_selection_rect Selection rectangle when the movement started, in scene coordinates

View File

@@ -21,8 +21,10 @@
#include <QtXml>
#include <QImage>
class CustomElement;
class QETElementEditor;
class ElementPrimitiveDecorator;
class ElementScene;
class QETElementEditor;
/**
This abstract class represents a primitive of the visual representation of an
electrical element. The Element, FixedElement and CustomElement classes do not
@@ -95,6 +97,14 @@ class CustomElementPart {
/// @return the name that will be used as XML tag when exporting the primitive
virtual QString xmlName() const = 0;
virtual QGraphicsItem *toItem();
virtual void setDecorator(ElementPrimitiveDecorator *);
virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
protected:
QList<QPointF> mapPoints(const QRectF &, const QRectF &, const QList<QPointF> &);
};

View File

@@ -684,3 +684,104 @@ void ChangeInformationsCommand::undo() {
void ChangeInformationsCommand::redo() {
editor_scene_ -> setInformations(new_informations_);
}
/**
Constructor
@param scene Modified ElementScene
@param parent Parent QUndoCommand
*/
ScalePartsCommand::ScalePartsCommand(ElementScene *scene, QUndoCommand * parent) :
ElementEditionCommand(scene, 0, parent),
first_redo(true)
{
}
/**
Destructor
*/
ScalePartsCommand::~ScalePartsCommand() {
}
/**
Undo the scaling operation
*/
void ScalePartsCommand::undo() {
scale(new_rect_, original_rect_);
}
/**
Redo the scaling operation
*/
void ScalePartsCommand::redo() {
if (first_redo) {
first_redo = false;
return;
}
scale(original_rect_, new_rect_);
}
/**
@return the element editor/scene the command should take place on
*/
ElementScene *ScalePartsCommand::elementScene() const {
return(editor_scene_);
}
/**
Set \a primitives as the list of primitives to be scaled by this command
*/
void ScalePartsCommand::setScaledPrimitives(const QList<CustomElementPart *> &primitives) {
scaled_primitives_ = primitives;
adjustText();
}
/**
@return the list of primitives to be scaled by this command
*/
QList<CustomElementPart *> ScalePartsCommand::scaledPrimitives() const {
return(scaled_primitives_);
}
/**
Define the transformation applied by this command
@param original_rect Bounding rectangle for all scaled primitives before the operation
@param original_rect Bounding rectangle for all scaled primitives after the operation
*/
void ScalePartsCommand::setTransformation(const QRectF &original_rect, const QRectF &new_rect) {
original_rect_ = original_rect;
new_rect_ = new_rect;
}
/**
@return the transformation applied by this command. The returned rectangles
are the bounding rectangles for all scaled primitives respectively before
and after the operation.
*/
QPair<QRectF, QRectF> ScalePartsCommand::transformation() {
return(QPair<QRectF, QRectF>(original_rect_, new_rect_));
}
/**
Apply the scaling operation from \a before to \a after.
*/
void ScalePartsCommand::scale(const QRectF &before, const QRectF &after) {
if (!scaled_primitives_.count()) return;
if (before == after) return;
if (!before.width() || !before.height()) return; // cowardly flee division by zero FIXME?
foreach (CustomElementPart *part_item, scaled_primitives_) {
part_item -> startUserTransformation(before);
part_item -> handleUserTransformation(before, after);
}
}
/**
Generate the text describing what this command does exactly.
*/
void ScalePartsCommand::adjustText() {
if (scaled_primitives_.count() == 1) {
setText(QObject::tr("redimensionnement %1", "undo caption -- %1 is the resized primitive type name").arg(scaled_primitives_.first() -> name()));
} else {
setText(QObject::tr("redimensionnement de %1 primitives", "undo caption -- %1 always > 1").arg(scaled_primitives_.count()));
}
}

View File

@@ -391,4 +391,42 @@ class ChangeInformationsCommand : public ElementEditionCommand {
/// New information
QString new_informations_;
};
/**
This command scales primitives when editing an electrical element.
*/
class ScalePartsCommand : public ElementEditionCommand {
// constructors, destructor
public:
ScalePartsCommand(ElementScene * = 0, QUndoCommand * = 0);
virtual ~ScalePartsCommand();
private:
ScalePartsCommand(const ScalePartsCommand &);
// methods
public:
virtual void undo();
virtual void redo();
ElementScene *elementScene() const;
void setScaledPrimitives(const QList<CustomElementPart *> &);
QList<CustomElementPart *> scaledPrimitives() const;
void setTransformation(const QRectF &, const QRectF &);
QPair<QRectF, QRectF> transformation();
protected:
void scale(const QRectF &before, const QRectF &after);
void adjustText();
// attributes
private:
/// List of moved primitives
QList<CustomElementPart *> scaled_primitives_;
/// original rect items fit in
QRectF original_rect_;
/// new rect items should fit in
QRectF new_rect_;
/// Prevent the first call to redo()
bool first_redo;
};
#endif

View File

@@ -0,0 +1,614 @@
#include "elementprimitivedecorator.h"
#include "elementscene.h"
#include "customelementpart.h"
#include "editorcommands.h"
#include "qet.h"
#include <QPainter>
#include <QDebug>
#include <QGraphicsSceneHoverEvent>
#include <QStyleOptionGraphicsItem>
#include <QGraphicsScene>
#include <QTransform>
/**
Constructor
@param parent Parent QGraphicsItem
*/
ElementPrimitiveDecorator::ElementPrimitiveDecorator(QGraphicsItem *parent):
QGraphicsObject(parent)
{
init();
}
/**
Destructor
*/
ElementPrimitiveDecorator::~ElementPrimitiveDecorator() {
}
/**
@return the internal bouding rect, i.e. the smallest rectangle containing
the bounding rectangle of every selected item.
*/
QRectF ElementPrimitiveDecorator::internalBoundingRect() const {
if (!decorated_items_.count() || !scene()) return(QRectF());
QRectF rect = getSceneBoundingRect(decorated_items_.first() -> toItem());
foreach (CustomElementPart *item, decorated_items_) {
rect = rect.united(getSceneBoundingRect(item -> toItem()));
}
return(rect);
}
/**
@return the outer bounds of the decorator as a rectangle.
*/
QRectF ElementPrimitiveDecorator::boundingRect() const {
const qreal additional_margin = 2.5;
QRectF rect = effective_bounding_rect_;
rect.adjust(-additional_margin, -additional_margin, additional_margin, additional_margin);
return(rect);
}
/**
Paint the contents of an item in local coordinates, using \a painter, with
respect to \a option and
@param option The option parameter provides style options for the item, such
as its state, exposed area and its level-of-detail hints.
@param The widget argument is optional. If provided, it points to the
widget that is being painted on; otherwise, it is 0. For cached painting,
widget is always 0.
*/
void ElementPrimitiveDecorator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
Q_UNUSED(option)
Q_UNUSED(widget)
painter -> save();
// paint the original bounding rect
painter -> setPen(Qt::DashLine);
//QGraphicsItemGroup::paint(painter, option, widget);
painter -> drawRect(modified_bounding_rect_);
drawSquares(painter, option, widget);
// uncomment to draw the real bouding rect (=adjusted internal bounding rect)
// painter -> setBrush(QBrush(QColor(240, 0, 0, 127)));
// painter -> drawRect(boundingRect());
painter -> restore();
}
/**
@param items the new list of items this decorator is suposed to manipulate.
*/
void ElementPrimitiveDecorator::setItems(const QList<CustomElementPart *> &items) {
if (CustomElementPart *single_item = singleItem()) {
if (items.count() == 1 && items.first() == single_item) {
// no actual change
goto end_setItems;
}
// break any connection between the former single selected item (if any) and
// the decorator
single_item -> setDecorator(0);
if (QGraphicsObject *single_object = dynamic_cast<QGraphicsObject *>(single_item)) {
disconnect(single_object, 0, this, 0);
}
}
decorated_items_ = items;
// when only a single primitive is selected, the decorator behaves specially
// to enable extra features, such as text edition, internal points movements,
// etc.
if (CustomElementPart *single_item = singleItem()) {
single_item -> setDecorator(this);
}
end_setItems:
adjust();
show();
grabKeyboard();
}
/**
@param items the new list of items this decorator is suposed to manipulate.
*/
void ElementPrimitiveDecorator::setItems(const QList<QGraphicsItem *> &items) {
QList<CustomElementPart *> primitives;
foreach (QGraphicsItem *item, items) {
if (CustomElementPart *part_item = dynamic_cast<CustomElementPart *>(item)) {
primitives << part_item;
}
}
setItems(primitives);
}
/**
@return the list of items this decorator is supposed to manipulate
*/
QList<CustomElementPart *> ElementPrimitiveDecorator::items() const {
return(decorated_items_);
}
/**
@return the list of items this decorator is supposed to manipulate
*/
QList<QGraphicsItem *> ElementPrimitiveDecorator::graphicsItems() const {
QList<QGraphicsItem *> list;
foreach (CustomElementPart *part_item, decorated_items_) {
if (QGraphicsItem *item = dynamic_cast<QGraphicsItem *>(part_item)) {
list << item;
}
}
return(list);
}
/**
Adjust the visual decorator according to the currently assigned items.
It is notably called by setItems().
*/
void ElementPrimitiveDecorator::adjust() {
saveOriginalBoundingRect();
modified_bounding_rect_ = original_bounding_rect_;
adjustEffectiveBoundingRect();
}
/**
Handle events generated when the mouse hovers over the decorator.
@param event Object describing the hover event.
*/
void ElementPrimitiveDecorator::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
QList<QRectF> rects = getResizingSquares();
QPointF pos = event -> pos();
if (rects.at(QET::ResizeFromTopLeftCorner).contains(pos) || rects.at(QET::ResizeFromBottomRightCorner).contains(pos)) {
setCursor(Qt::SizeFDiagCursor);
} else if (rects.at(QET::ResizeFromTopRightCorner).contains(pos) || rects.at(QET::ResizeFromBottomLeftCorner).contains(pos)) {
setCursor(Qt::SizeBDiagCursor);
} else if (rects.at(QET::ResizeFromTopCenterCorner).contains(pos) || rects.at(QET::ResizeFromBottomCenterCorner).contains(pos)) {
setCursor(Qt::SizeVerCursor);
} else if (rects.at(QET::ResizeFromMiddleLeftCorner).contains(pos) || rects.at(QET::ResizeFromMiddleRightCorner).contains(pos)) {
setCursor(Qt::SizeHorCursor);
} else if (internalBoundingRect().contains(pos)) {
setCursor(Qt::SizeAllCursor);
} else {
setCursor(Qt::ArrowCursor);
}
}
/**
Handle event generated when mouse buttons are pressed.
@param event Object describing the mouse event
*/
void ElementPrimitiveDecorator::mousePressEvent(QGraphicsSceneMouseEvent *event) {
qDebug() << Q_FUNC_INFO << event << zValue();
QList<QRectF> rects = getResizingSquares();
QPointF pos = event -> pos();
current_operation_square_ = resizingSquareAtPos(pos);
bool accept = false;
if (current_operation_square_ != QET::NoOperation) {
accept = true;
} else {
if (internalBoundingRect().contains(pos)) {
if (CustomElementPart *single_item = singleItem()) {
bool event_accepted = single_item -> singleItemPressEvent(this, event);
if (event_accepted) {
event -> ignore();
return;
}
}
current_operation_square_ = QET::MoveArea;
accept = true;
}
}
if (accept) {
if (current_operation_square_ > QET::NoOperation) {
first_pos_ = latest_pos_ = mapToScene(rects.at(current_operation_square_).center());
} else {
first_pos_ = decorated_items_.at(0) -> toItem() -> scenePos();
latest_pos_ = event -> scenePos();
mouse_offset_ = event -> scenePos() - first_pos_;
}
startMovement();
event -> accept();
} else {
event -> ignore();
}
}
/**
Handle events generated when mouse buttons are double clicked.
@param event Object describing the mouse event
*/
void ElementPrimitiveDecorator::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
//QGraphicsObject::mouseDoubleClickEvent(event);
if (CustomElementPart *single_item = singleItem()) {
bool event_accepted = single_item -> singleItemDoubleClickEvent(this, event);
if (event_accepted) {
event -> ignore();
return;
}
}
}
/**
Handle event generated when the mouse is moved and the decorator is the mouse grabber item.
@param event Object describing the mouse event
@see QGraphicsScene::mouseGrabberItem()
*/
void ElementPrimitiveDecorator::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
QList<QRectF> rects = getResizingSquares();
QPointF scene_pos = event -> scenePos();
QPointF movement = scene_pos - latest_pos_;
// snap the movement to a non-visible grid when scaling selection
if (mustSnapToGrid(event)) {
// We now want some point belonging to the selection to be snapped to this rounded position
if (current_operation_square_ > QET::NoOperation) {
// real, non-rounded movement from the mouse press event
QPointF global_movement = scene_pos - first_pos_;
// real, rounded movement from the mouse press event
QPointF rounded_global_movement = snapConstPointToGrid(global_movement);
// rounded position of the current mouse move event
QPointF rounded_scene_pos = first_pos_ + rounded_global_movement;
// when scaling the selection, consider the center of the currently dragged resizing rectangle
QPointF current_position = mapToScene(rects.at(current_operation_square_).center());
// determine the final, effective movement
movement = rounded_scene_pos - current_position;
} else if (current_operation_square_ == QET::MoveArea) {
// when moving the selection, consider the position of the first selected item
QPointF current_position = scene_pos - mouse_offset_;
QPointF rounded_current_position = snapConstPointToGrid(current_position);
movement = rounded_current_position - decorated_items_.at(0) -> toItem() -> scenePos();
} else {
if (CustomElementPart *single_item = singleItem()) {
bool event_accepted = single_item -> singleItemMoveEvent(this, event);
if (event_accepted) {
event -> ignore();
return;
}
}
}
}
QRectF bounding_rect = modified_bounding_rect_;
applyMovementToRect(current_operation_square_, movement, modified_bounding_rect_);
if (modified_bounding_rect_ != bounding_rect) {
adjustEffectiveBoundingRect();
}
latest_pos_ = event -> scenePos();
if (current_operation_square_ == QET::MoveArea) {
translateItems(movement);
} else {
scaleItems(original_bounding_rect_, modified_bounding_rect_);
}
}
/**
Handle event generated when a mouse buttons are releaseis moved and the
decorator is the mouse grabber item.
@param event Object describing the mouse event
@see QGraphicsScene::mouseGrabberItem()
*/
void ElementPrimitiveDecorator::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
Q_UNUSED(event)
ElementEditionCommand *command = 0;
if (current_operation_square_ > QET::NoOperation) {
ScalePartsCommand *scale_command = new ScalePartsCommand();
scale_command -> setScaledPrimitives(items());
scale_command -> setTransformation(
mapToScene(original_bounding_rect_).boundingRect(),
mapToScene(modified_bounding_rect_).boundingRect()
);
command = scale_command;
} else if (current_operation_square_ == QET::MoveArea) {
QPointF movement = mapToScene(modified_bounding_rect_.topLeft()) - mapToScene(original_bounding_rect_.topLeft());
if (!movement.isNull()) {
MovePartsCommand *move_command = new MovePartsCommand(movement, 0, graphicsItems());
command = move_command;
}
} else {
if (CustomElementPart *single_item = singleItem()) {
bool event_accepted = single_item -> singleItemReleaseEvent(this, event);
if (event_accepted) {
event -> ignore();
return;
}
}
}
if (command) {
emit(actionFinished(command));
}
if (current_operation_square_ != QET::NoOperation) {
adjust();
}
current_operation_square_ = QET::NoOperation;
}
/**
Initialize an ElementPrimitiveDecorator
*/
void ElementPrimitiveDecorator::init() {
//setAcceptedMouseButtons(Qt::LeftButton);
grid_step_x_ = grid_step_y_ = 1;
setAcceptHoverEvents(true);
}
/**
Save the original bounding rectangle.
*/
void ElementPrimitiveDecorator::saveOriginalBoundingRect() {
original_bounding_rect_ = internalBoundingRect();
}
/**
Adjust the effective bounding rect. This method should be called after the
modified_bouding_rect_ attribute was modified.
*/
void ElementPrimitiveDecorator::adjustEffectiveBoundingRect() {
prepareGeometryChange();
effective_bounding_rect_ = modified_bounding_rect_ | effective_bounding_rect_;
update();
}
/**
Start a movement (i.e. either a move or scaling operation)
*/
void ElementPrimitiveDecorator::startMovement() {
adjust();
foreach(CustomElementPart *item, decorated_items_) {
qDebug() << Q_FUNC_INFO << "starting movement with rect" << original_bounding_rect_ << "i.e. in scene coordinates " << mapToScene(original_bounding_rect_).boundingRect();
item -> startUserTransformation(mapToScene(original_bounding_rect_).boundingRect());
}
}
/**
Apply the movement described by \a movement_type and \a movement to \a rect.
*/
void ElementPrimitiveDecorator::applyMovementToRect(int movement_type, const QPointF &movement, QRectF &rect) {
qreal new_value;
QPointF new_point;
switch (movement_type) {
case QET::MoveArea:
rect.translate(movement.x(), movement.y());
break;
case QET::ResizeFromTopLeftCorner:
new_point = rect.topLeft() + movement;
rect.setTopLeft(new_point);
break;
case QET::ResizeFromTopCenterCorner:
new_value = rect.top() + movement.y();
rect.setTop(new_value);
break;
case QET::ResizeFromTopRightCorner:
new_point = rect.topRight() + movement;
rect.setTopRight(new_point);
break;
case QET::ResizeFromMiddleLeftCorner:
new_value = rect.left() + movement.x();
rect.setLeft(new_value);
break;
case QET::ResizeFromMiddleRightCorner:
new_value = rect.right() + movement.x();
rect.setRight(new_value);
break;
case QET::ResizeFromBottomLeftCorner:
new_point = rect.bottomLeft() + movement;
rect.setBottomLeft(new_point);
break;
case QET::ResizeFromBottomCenterCorner:
new_value = rect.bottom() + movement.y();
rect.setBottom(new_value);
break;
case QET::ResizeFromBottomRightCorner:
new_point = rect.bottomRight() + movement;
rect.setBottomRight(new_point);
break;
}
}
CustomElementPart *ElementPrimitiveDecorator::singleItem() const {
if (decorated_items_.count() == 1) {
return(decorated_items_.first());
}
return(0);
}
/**
Translated the managed items by the \a movement
*/
void ElementPrimitiveDecorator::translateItems(const QPointF &movement) {
if (!decorated_items_.count()) return;
foreach(QGraphicsItem *qgi, graphicsItems()) {
// this is a naive, proof-of-concept implementation; we actually need to take
// the grid into account and create a command object in mouseReleaseEvent()
qgi -> moveBy(movement.x(), movement.y());
}
}
/**
Scale the managed items, provided they originally fit within \a
original_rect and they should now fit \a new_rect
*/
void ElementPrimitiveDecorator::scaleItems(const QRectF &original_rect, const QRectF &new_rect) {
if (!decorated_items_.count()) return;
if (original_rect == new_rect) return;
if (!original_rect.width() || !original_rect.height()) return; // cowardly flee division by zero FIXME?
QRectF scene_original_rect = mapToScene(original_rect).boundingRect();
QRectF scene_new_rect = mapToScene(new_rect).boundingRect();
qDebug() << Q_FUNC_INFO << "from " << original_rect << " to " << new_rect;
qDebug() << Q_FUNC_INFO << "from " << scene_original_rect << " to " << scene_new_rect << "(scene coordinates)";
foreach(CustomElementPart *item, decorated_items_) {
item -> handleUserTransformation(scene_original_rect, scene_new_rect);
}
}
/**
@return the bounding rectangle of \a item, in scene coordinates
*/
QRectF ElementPrimitiveDecorator::getSceneBoundingRect(QGraphicsItem *item) const {
if (!item) return(QRectF());
return(item -> mapRectToScene(item -> boundingRect()));
}
/**
Draw all known resizing squares using \a painter.
@param option The option parameter provides style options for the item, such
as its state, exposed area and its level-of-detail hints.
@param The widget argument is optional. If provided, it points to the
widget that is being painted on; otherwise, it is 0. For cached painting,
widget is always 0.
*/
void ElementPrimitiveDecorator::drawSquares(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
foreach (QRectF rect, getResizingSquares()) {
drawResizeSquare(painter, option, widget, rect);
}
}
/**
Draw the provided resizing square \a rect using \a painter.
@param option The option parameter provides style options for the item, such
as its state, exposed area and its level-of-detail hints.
@param The widget argument is optional. If provided, it points to the
widget that is being painted on; otherwise, it is 0. For cached painting,
widget is always 0.
*/
void ElementPrimitiveDecorator::drawResizeSquare(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QRectF &rect) {
QColor inner(0xFF, 0xFF, 0xFF);
QColor outer(0x00, 0x61, 0xFF);
if (decorated_items_.count() > 1) {
outer = QColor(0x1A, 0x5C, 0x14);
}
drawGenericSquare(painter, option, widget, rect, inner, outer);
}
/**
Draw a generic square \a rect using \a painter.
@param inner Color used to fill the square
@param outer Color usd to draw the outline
@param option The option parameter provides style options for the item, such
as its state, exposed area and its level-of-detail hints.
@param The widget argument is optional. If provided, it points to the
widget that is being painted on; otherwise, it is 0. For cached painting,
widget is always 0.
*/
void ElementPrimitiveDecorator::drawGenericSquare(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget, const QRectF &rect, const QColor &inner, const QColor &outer) {
Q_UNUSED(option)
Q_UNUSED(widget)
// 1.0px will end up to level_of_details px once rendered
// qreal level_of_details = option->levelOfDetailFromTransform(painter -> transform());
painter -> save();
painter -> setBrush(QBrush(inner));
QPen square_pen(QBrush(outer), 2.0, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
square_pen.setCosmetic(true);
painter -> setPen(square_pen);
painter -> drawRect(rect);
painter -> restore();
}
/**
@return A list containing all known resizing squares, based on the
modified_bounding_rect_ attribute. They are ordered following
QET::OperationAreas, so it is possible to use positive values from this enum
to fetch the corresponding square in the list.
@see QET::OperationAreas
*/
QList<QRectF> ElementPrimitiveDecorator::getResizingSquares() {
QRectF primitive_rect = modified_bounding_rect_;
QRectF half_primitive_rect1 = primitive_rect;
half_primitive_rect1.setHeight(half_primitive_rect1.height() / 2.0);
QRectF half_primitive_rect2 = primitive_rect;
half_primitive_rect2.setWidth(half_primitive_rect2.width() / 2.0);
QList<QRectF> rects;
rects << getGenericSquare(primitive_rect.topLeft());
rects << getGenericSquare(half_primitive_rect2.topRight());
rects << getGenericSquare(primitive_rect.topRight());
rects << getGenericSquare(half_primitive_rect1.bottomLeft());
rects << getGenericSquare(half_primitive_rect1.bottomRight());
rects << getGenericSquare(primitive_rect.bottomLeft());
rects << getGenericSquare(half_primitive_rect2.bottomRight());
rects << getGenericSquare(primitive_rect.bottomRight());
/// TODO cache the rects instead of calculating them again and again?
return(rects);
}
/**
@return the square to be drawn to represent \a position
*/
QRectF ElementPrimitiveDecorator::getGenericSquare(const QPointF &position) {
const qreal square_half_size = 0.5;
return(
QRectF(
position.x() - square_half_size,
position.y() - square_half_size,
square_half_size * 2.0,
square_half_size * 2.0
)
);
}
/**
@return the index of the square containing the \a position point or -1 if
none matches.
*/
int ElementPrimitiveDecorator::resizingSquareAtPos(const QPointF &position) {
QList<QRectF> rects = getResizingSquares();
int current_square = QET::NoOperation;
for (int i = 0 ; i < rects.count() ; ++ i) {
if (rects.at(i).contains(position)) {
current_square = i;
}
}
return(current_square);
}
/**
Round the coordinates of \a point so it is snapped to the grid defined by the
grid_step_x_ and grid_step_y_ attributes.
*/
QPointF ElementPrimitiveDecorator::snapConstPointToGrid(const QPointF &point) const {
return(
QPointF(
qRound(point.x() / grid_step_x_) * grid_step_x_,
qRound(point.y() / grid_step_y_) * grid_step_y_
)
);
}
/**
Round the coordinates of \a point so it is snapped to the grid defined by the
grid_step_x_ and grid_step_y_ attributes.
*/
void ElementPrimitiveDecorator::snapPointToGrid(QPointF &point) const {
point.rx() = qRound(point.x() / grid_step_x_) * grid_step_x_;
point.ry() = qRound(point.y() / grid_step_y_) * grid_step_y_;
}
/**
@return whether the current operation should take the grid into account
according to the state of the provided \a event
*/
bool ElementPrimitiveDecorator::mustSnapToGrid(QGraphicsSceneMouseEvent *event) {
return(!(event -> modifiers() & Qt::ControlModifier));
}

View File

@@ -0,0 +1,90 @@
#ifndef ELEMENTPRIMITIVEDECORATOR_H
#define ELEMENTPRIMITIVEDECORATOR_H
#include <QGraphicsObject>
class ElementEditionCommand;
class ElementScene;
class CustomElementPart;
/**
This class represents a decorator rendered above selected items so users
can manipulate (move, resize, ...) them.
The implementation considers four kinds of bounding rects:
- the actual, effective bounding rect as returned by the boundingRect() method
- the original bounding rect, i.e. the rect containing all selected items at
the beginning of operations (or after a command object was generated)
- the new bounding rect, after the user moved or resized items
- the former bounding rect, due to implementation details
*/
class ElementPrimitiveDecorator : public QGraphicsObject {
Q_OBJECT
public:
ElementPrimitiveDecorator(QGraphicsItem * = 0);
virtual ~ElementPrimitiveDecorator();
enum { Type = UserType + 2200 };
// methods
QRectF internalBoundingRect() const;
virtual QRectF boundingRect () const;
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0);
virtual int type() const { return Type; }
void setItems(const QList<QGraphicsItem *> &);
void setItems(const QList<CustomElementPart *> &);
QList<CustomElementPart *> items() const;
QList<QGraphicsItem *> graphicsItems() const;
public slots:
void adjust();
signals:
void actionFinished(ElementEditionCommand *);
protected:
void hoverMoveEvent(QGraphicsSceneHoverEvent *);
void mousePressEvent(QGraphicsSceneMouseEvent *);
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
void mouseMoveEvent(QGraphicsSceneMouseEvent *);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
QPointF snapConstPointToGrid(const QPointF &) const;
void snapPointToGrid(QPointF &) const;
bool mustSnapToGrid(QGraphicsSceneMouseEvent *);
private:
void init();
void saveOriginalBoundingRect();
void adjustEffectiveBoundingRect();
void startMovement();
void applyMovementToRect(int, const QPointF &, QRectF &);
CustomElementPart *singleItem() const;
void translateItems(const QPointF &);
void scaleItems(const QRectF &, const QRectF &);
QRectF getSceneBoundingRect(QGraphicsItem *) const;
void drawSquares(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
void drawResizeSquare(QPainter *, const QStyleOptionGraphicsItem *, QWidget *, const QRectF &);
void drawGenericSquare(QPainter *, const QStyleOptionGraphicsItem *, QWidget *, const QRectF &, const QColor &, const QColor &);
QList<QRectF> getResizingSquares();
QRectF getGenericSquare(const QPointF &);
int resizingSquareAtPos(const QPointF &);
// attributes
private:
QList<CustomElementPart *> decorated_items_;
QRectF effective_bounding_rect_; ///< actual, effective bounding rect -- never shrinks
QRectF original_bounding_rect_; ///< original bounding rect
QRectF modified_bounding_rect_; ///< new bounding rect, after the user moved or resized items
/**
Index of the square leading the current operation (resizing, etc.) or -1 if no
operation is occurring, -2 for a move operation.
*/
int current_operation_square_;
int grid_step_x_; ///< Grid horizontal step
int grid_step_y_; ///< Grid horizontal step
QPointF first_pos_; ///< First point involved within the current resizing operation
QPointF latest_pos_; ///< Latest point involved within the current resizing operation
QPointF mouse_offset_; ///< Offset between the mouse position and the point to be snapped to grid when moving selection
};
#endif

View File

@@ -17,6 +17,7 @@
*/
#include "elementscene.h"
#include "qetelementeditor.h"
#include "elementprimitivedecorator.h"
#include <cmath>
#include "partline.h"
#include "partrectangle.h"
@@ -44,17 +45,22 @@ ElementScene::ElementScene(QETElementEditor *editor, QObject *parent) :
_hotspot(15, 35),
internal_connections(false),
qgi_manager(this),
element_editor(editor)
element_editor(editor),
decorator_(0)
{
setItemIndexMethod(NoIndex);
current_polygon = NULL;
setGrid(1, 1);
initPasteArea();
undo_stack.setClean();
decorator_lock_ = new QMutex(QMutex::NonRecursive);
connect(&undo_stack, SIGNAL(indexChanged(int)), this, SLOT(managePrimitivesGroups()));
connect(this, SIGNAL(selectionChanged()), this, SLOT(managePrimitivesGroups()));
}
/// Destructeur
ElementScene::~ElementScene() {
delete decorator_lock_;
}
/**
@@ -187,25 +193,7 @@ void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
break;
case Normal:
default:
QList<QGraphicsItem *> selected_items = selectedItems();
if (!selected_items.isEmpty()) {
// mouvement de souris realise depuis le dernier press event
QPointF mouse_movement = e -> scenePos() - moving_press_pos;
// application de ce mouvement a la fsi_pos enregistre dans le dernier press event
QPointF new_fsi_pos = fsi_pos + mouse_movement;
// snap eventuel de la nouvelle fsi_pos
if (mustSnapToGrid(e)) snapToGrid(new_fsi_pos);
// difference entre la fsi_pos finale et la fsi_pos courante = mouvement a appliquer
QPointF current_fsi_pos = selected_items.first() -> scenePos();
QPointF final_movement = new_fsi_pos - current_fsi_pos;
foreach(QGraphicsItem *qgi, selected_items) {
qgi -> moveBy(final_movement.x(), final_movement.y());
}
}
QGraphicsScene::mouseMoveEvent(e);
}
} else if (behavior == Polygon && current_polygon != NULL) {
temp_polygon = current_polygon -> polygon();
@@ -263,16 +251,6 @@ void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
case Normal:
default:
QGraphicsScene::mousePressEvent(e);
// gestion des deplacements de parties
if (!selectedItems().isEmpty()) {
fsi_pos = selectedItems().first() -> scenePos();
moving_press_pos = e -> scenePos();
moving_parts_ = true;
} else {
fsi_pos = QPoint();
moving_press_pos = QPoint();
moving_parts_ = false;
}
}
} else QGraphicsScene::mousePressEvent(e);
}
@@ -358,12 +336,6 @@ void ElementScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
case Normal:
default:
// detecte les deplacements de parties
if (!selectedItems().isEmpty() && moving_parts_) {
QPointF movement = selectedItems().first() -> scenePos() - fsi_pos;
if (!movement.isNull()) {
undo_stack.push(new MovePartsCommand(movement, this, selectedItems()));
}
}
QGraphicsScene::mouseReleaseEvent(e);
moving_parts_ = false;
}
@@ -657,7 +629,7 @@ QRectF ElementScene::borderRect() const {
*/
QRectF ElementScene::sceneContent() const {
qreal adjustment = 5.0;
return(itemsBoundingRect().unite(borderRect()).adjusted(-adjustment, -adjustment, adjustment, adjustment));
return(elementContentBoundingRect(items()).unite(borderRect()).adjusted(-adjustment, -adjustment, adjustment, adjustment));
}
/**
@@ -666,7 +638,7 @@ QRectF ElementScene::sceneContent() const {
l'element.
*/
bool ElementScene::borderContainsEveryParts() const {
return(borderRect().contains(itemsBoundingRect()));
return(borderRect().contains(elementContentBoundingRect(items())));
}
/**
@@ -800,7 +772,10 @@ void ElementScene::slot_delete() {
// efface tout ce qui est selectionne
undo_stack.push(new DeletePartsCommand(this, selected_items));
// removing items does not trigger QGraphicsScene::selectionChanged()
emit(partsRemoved());
emit(selectionChanged());
}
/**
@@ -825,7 +800,7 @@ void ElementScene::slot_editSizeHotSpot() {
hotspot_editor -> setElementHeight(static_cast<uint>(height() / 10));
hotspot_editor -> setHotspot(hotspot());
hotspot_editor -> setOldHotspot(hotspot());
hotspot_editor -> setPartsRect(itemsBoundingRect());
hotspot_editor -> setPartsRect(elementContentBoundingRect(items()));
hotspot_editor -> setPartsRectEnabled(true);
hotspot_editor -> setReadOnly(is_read_only);
dialog_layout -> addWidget(hotspot_editor);
@@ -1021,6 +996,19 @@ void ElementScene::slot_sendBackward() {
emit(partsZValueChanged());
}
/**
@return the list of primitives currently present on the scene.
*/
QList<CustomElementPart *> ElementScene::primitives() const {
QList<CustomElementPart *> primitives_list;
foreach (QGraphicsItem *item, items()) {
if (CustomElementPart *primitive = dynamic_cast<CustomElementPart *>(item)) {
primitives_list << primitive;
}
}
return(primitives_list);
}
/**
@param include_terminals true pour inclure les bornes, false sinon
@return les parties de l'element ordonnes par zValue croissante
@@ -1032,7 +1020,13 @@ QList<QGraphicsItem *> ElementScene::zItems(bool include_terminals) const {
// enleve les bornes
QList<QGraphicsItem *> terminals;
foreach(QGraphicsItem *qgi, all_items_list) {
if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
if (
qgi -> type() == ElementPrimitiveDecorator::Type ||
qgi -> type() == QGraphicsRectItem::Type
) {
all_items_list.removeAt(all_items_list.indexOf(qgi));
}
else if (qgraphicsitem_cast<PartTerminal *>(qgi)) {
all_items_list.removeAt(all_items_list.indexOf(qgi));
terminals << qgi;
}
@@ -1089,9 +1083,12 @@ void ElementScene::reset() {
@return le boundingRect de ces parties, exprime dans les coordonnes de la
scene
*/
QRectF ElementScene::elementContentBoundingRect(const ElementContent &content) {
QRectF ElementScene::elementContentBoundingRect(const ElementContent &content) const {
QRectF bounding_rect;
foreach(QGraphicsItem *qgi, content) {
// skip non-primitives QGraphicsItems (paste area, selection decorator)
if (qgi -> type() == ElementPrimitiveDecorator::Type) continue;
if (qgi -> type() == QGraphicsRectItem::Type) continue;
bounding_rect |= qgi -> sceneBoundingRect();
}
return(bounding_rect);
@@ -1225,7 +1222,7 @@ ElementContent ElementScene::loadContent(const QDomDocument &xml_document, QStri
ElementContent ElementScene::addContent(const ElementContent &content, QString *error_message) {
Q_UNUSED(error_message);
foreach(QGraphicsItem *part, content) {
addItem(part);
addPrimitive(part);
}
return(content);
}
@@ -1249,11 +1246,20 @@ ElementContent ElementScene::addContentAtPos(const ElementContent &content, cons
// ajoute les parties avec le decalage adequat
foreach(QGraphicsItem *part, content) {
part -> setPos(part -> pos() + offset);
addItem(part);
addPrimitive(part);
}
return(content);
}
/**
Add a primitive to the scene by wrapping it within an
ElementPrimitiveDecorator group.
*/
void ElementScene::addPrimitive(QGraphicsItem *primitive) {
if (!primitive) return;
addItem(primitive);
}
/**
Initialise la zone de collage
*/
@@ -1300,3 +1306,61 @@ bool ElementScene::mustSnapToGrid(QGraphicsSceneMouseEvent *e) {
bool ElementScene::zValueLessThan(QGraphicsItem *item1, QGraphicsItem *item2) {
return(item1-> zValue() < item2 -> zValue());
}
/**
Ensure the decorator is adequately shown, hidden or updated so it always
represents the current selection.
*/
void ElementScene::managePrimitivesGroups() {
if (!decorator_lock_ -> tryLock()) return;
if (!decorator_) {
decorator_ = new ElementPrimitiveDecorator();
connect(decorator_, SIGNAL(actionFinished(ElementEditionCommand*)), this, SLOT(stackAction(ElementEditionCommand *)));
addItem(decorator_);
decorator_ -> hide();
}
QList<QGraphicsItem *> selected_items;
foreach (QGraphicsItem *item, items()) {
if (item -> type() == ElementPrimitiveDecorator::Type) continue;
if (item -> type() == QGraphicsRectItem::Type) continue;
if (item -> isSelected()) {
selected_items << item;
}
}
/// TODO export the above code to a proper method
// should we hide the decorator?
if (!selected_items.count()) {
decorator_ -> hide();
} else {
decorator_ -> setZValue(1000000);
decorator_ -> setPos(0, 0);
decorator_ -> setItems(selected_items);
}
decorator_lock_ -> unlock();
}
/**
Push the provided \a command on the undo stack.
*/
void ElementScene::stackAction(ElementEditionCommand *command) {
if (command -> elementScene()) {
if (command -> elementScene() != this) return;
} else {
command -> setElementScene(this);
}
if (!command -> elementView()) {
foreach (QGraphicsView *view, views()) {
if (ElementView *element_view = dynamic_cast<ElementView *>(view)) {
command -> setElementView(element_view);
break;
}
}
}
undoStack().push(command);
}

View File

@@ -23,6 +23,9 @@
#include "orientationsetwidget.h"
#include "qgimanager.h"
#include "elementcontent.h"
class CustomElementPart;
class ElementEditionCommand;
class ElementPrimitiveDecorator;
class QETElementEditor;
class PartLine;
class PartRectangle;
@@ -95,6 +98,9 @@ class ElementScene : public QGraphicsScene {
/// Variables to handle copy/paste with offset
QString last_copied_;
/// Decorator item displayed when at least one item is selected
ElementPrimitiveDecorator *decorator_;
///< Size of the horizontal grid step
int x_grid;
///< Size of the vertical grid step
@@ -123,6 +129,7 @@ class ElementScene : public QGraphicsScene {
virtual QRectF boundingRectFromXml(const QDomDocument &);
virtual void fromXml(const QDomDocument &, const QPointF & = QPointF(), bool = true, ElementContent * = 0);
virtual void reset();
virtual QList<CustomElementPart *> primitives() const;
virtual QList<QGraphicsItem *> zItems(bool = false) const;
virtual ElementContent selectedContent() const;
virtual void getPasteArea(const QRectF &);
@@ -149,15 +156,17 @@ class ElementScene : public QGraphicsScene {
virtual void endCurrentBehavior(const QGraphicsSceneMouseEvent *);
private:
QRectF elementContentBoundingRect(const ElementContent &);
QRectF elementContentBoundingRect(const ElementContent &) const;
bool applyInformations(const QDomDocument &, QString * = 0);
ElementContent loadContent(const QDomDocument &, QString * = 0);
ElementContent addContent(const ElementContent &, QString * = 0);
ElementContent addContentAtPos(const ElementContent &, const QPointF &, QString * = 0);
void addPrimitive(QGraphicsItem *);
void initPasteArea();
void snapToGrid(QPointF &);
bool mustSnapToGrid(QGraphicsSceneMouseEvent *);
static bool zValueLessThan(QGraphicsItem *, QGraphicsItem *);
QMutex *decorator_lock_;
public slots:
void slot_move();
@@ -183,6 +192,8 @@ class ElementScene : public QGraphicsScene {
void slot_raise();
void slot_lower();
void slot_sendBackward();
void managePrimitivesGroups();
void stackAction(ElementEditionCommand *);
signals:
/**

View File

@@ -29,7 +29,7 @@ PartArc::PartArc(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene
_angle(-90),
start_angle(0)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -24,7 +24,7 @@
@param scene La scene sur laquelle figure ce cercle
*/
PartCircle::PartCircle(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : QGraphicsEllipseItem(parent, scene), CustomElementGraphicPart(editor) {
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -24,7 +24,7 @@
@param scene La scene sur laquelle figure cette ellipse
*/
PartEllipse::PartEllipse(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : QGraphicsEllipseItem(parent, scene), CustomElementGraphicPart(editor) {
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -32,7 +32,7 @@ PartLine::PartLine(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsSce
second_end(QET::None),
second_length(1.5)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -29,7 +29,7 @@ PartPolygon::PartPolygon(QETElementEditor *editor, QGraphicsItem *parent, QGraph
CustomElementGraphicPart(editor),
closed(false)
{
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -24,7 +24,7 @@
@param scene La scene sur laquelle figure ce rectangle
*/
PartRectangle::PartRectangle(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) : QGraphicsRectItem(parent, scene), CustomElementGraphicPart(editor) {
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -30,7 +30,7 @@ PartTerminal::PartTerminal(QETElementEditor *editor, QGraphicsItem *parent, QGra
_orientation(QET::North)
{
updateSecondPoint();
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif

View File

@@ -18,6 +18,7 @@
#include "parttext.h"
#include "texteditor.h"
#include "editorcommands.h"
#include "elementprimitivedecorator.h"
#include "elementscene.h"
#include "qetapp.h"
@@ -29,17 +30,21 @@
*/
PartText::PartText(QETElementEditor *editor, QGraphicsItem *parent, ElementScene *scene) :
QGraphicsTextItem(parent, scene),
CustomElementPart(editor)
CustomElementPart(editor),
previous_text(),
decorator_(0)
{
#if QT_VERSION >= 0x040500
document() -> setDocumentMargin(1.0);
#endif
setDefaultTextColor(Qt::black);
setFont(QETApp::diagramTextsFont());
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
real_font_size_ = font().pointSize();
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
setAcceptHoverEvents(true);
setDefaultTextColor(Qt::black);
setPlainText(QObject::tr("T", "default text when adding a text in the element editor"));
@@ -63,7 +68,7 @@ void PartText::fromXml(const QDomElement &xml_element) {
if (!ok || font_size < 1) font_size = 20;
setBlack(xml_element.attribute("color") != "white");
setFont(QETApp::diagramTextsFont(font_size));
setProperty("size" , font_size);
setPlainText(xml_element.attribute("text"));
qreal default_rotation_angle = 0.0;
@@ -153,31 +158,24 @@ QPointF PartText::margin() const {
}
/**
Permet a l'element texte de redevenir deplacable a la fin de l'edition de texte
@param e Le QFocusEvent decrivant la perte de focus
@reimp QGraphicsItem::focusInEvent(QFocusEvent *)
@param e The QFocusEvent object describing the focus gain.
Start text edition when the item gains focus.
*/
void PartText::focusInEvent(QFocusEvent *e) {
startEdition();
QGraphicsTextItem::focusInEvent(e);
}
/**
@reimp QGraphicsItem::focusOutEvent(QFocusEvent *)
@param e The QFocusEvent object describing the focus loss.
End text edition when the item loses focus.
*/
void PartText::focusOutEvent(QFocusEvent *e) {
QGraphicsTextItem::focusOutEvent(e);
if (previous_text != toPlainText()) {
undoStack().push(
new ChangePartCommand(
TextEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
toPlainText()
)
);
previous_text = toPlainText();
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
endEdition();
}
/**
@@ -185,11 +183,10 @@ void PartText::focusOutEvent(QFocusEvent *e) {
@param e Le QGraphicsSceneMouseEvent qui decrit le double-clic
*/
void PartText::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
previous_text = toPlainText();
QGraphicsTextItem::mouseDoubleClickEvent(e);
setFocus(Qt::MouseFocusReason);
if (e -> button() == Qt::LeftButton) {
setEditable(true);
}
}
/**
@@ -212,6 +209,11 @@ void PartText::setProperty(const QString &property, const QVariant &value) {
} else if (property == "size") {
if (!value.canConvert(QVariant::Int)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toInt();
} else if (property == "real_size") {
if (!value.canConvert(QVariant::Double)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toDouble();
} else if (property == "text") {
setPlainText(value.toString());
} else if (property == "rotation angle") {
@@ -241,6 +243,8 @@ QVariant PartText::property(const QString &property) {
return(pos().y());
} else if (property == "size") {
return(font().pointSize());
} else if (property == "real_size") {
return(real_font_size_);
} else if (property == "text") {
return(toPlainText());
} else if (property == "rotation angle") {
@@ -292,7 +296,7 @@ bool PartText::isUseless() const {
void PartText::startUserTransformation(const QRectF &rect) {
Q_UNUSED(rect)
saved_point_ = pos(); // scene coordinates, no need to mapFromScene()
saved_font_size_ = font().pointSize();
saved_font_size_ = real_font_size_;
}
/**
@@ -303,12 +307,10 @@ void PartText::handleUserTransformation(const QRectF &initial_selection_rect, co
QPointF new_pos = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_point_).first();
setPos(new_pos);
// adjust the font size following the smallest scale factor
qreal sx = new_selection_rect.width() / initial_selection_rect.width();
// adjust the font size following the vertical scale factor
qreal sy = new_selection_rect.height() / initial_selection_rect.height();
qreal smallest_scale_factor = sx > sy ? sy : sx;
qreal new_font_size = saved_font_size_ * smallest_scale_factor;
setProperty("size", qMax(1, qRound(new_font_size)));
qreal new_font_size = saved_font_size_ * sy;
setProperty("real_size", qMax(1, qRound(new_font_size)));
}
/**
@@ -318,7 +320,12 @@ void PartText::handleUserTransformation(const QRectF &initial_selection_rect, co
@param widget Widget sur lequel on dessine (facultatif)
*/
void PartText::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, QWidget *widget) {
QGraphicsTextItem::paint(painter, qsogi, widget);
// According to the source code of QGraphicsTextItem::paint(), this should
// avoid the drawing of the dashed rectangle around the text.
QStyleOptionGraphicsItem our_qsogi(*qsogi);
our_qsogi.state = QStyle::State_None;
QGraphicsTextItem::paint(painter, &our_qsogi, widget);
#ifdef QET_DEBUG_EDITOR_TEXTS
painter -> setPen(Qt::blue);
@@ -332,6 +339,124 @@ void PartText::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, Q
#endif
}
/**
Handle context menu events.
@param event Object describing the context menu event to handle.
*/
void PartText::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
Q_UNUSED(event);
}
/**
Handle events generated when the mouse hovers over the decorator.
@param event Object describing the hover event.
*/
void PartText::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
// force the cursor when the text is being edited
if (hasFocus() && decorator_) {
decorator_ -> setCursor(Qt::IBeamCursor);
}
QGraphicsTextItem::hoverMoveEvent(event);
}
/**
@reimp CustomElementPart::setDecorator(ElementPrimitiveDecorator *)
Install or remove a sceneEventFilter on the decorator and ensure it will
adjust itself while the text is being edited.
*/
void PartText::setDecorator(ElementPrimitiveDecorator *decorator) {
if (decorator) {
decorator -> installSceneEventFilter(this);
// ensure the decorator will adjust itself when the text area expands or shrinks
connect(document(), SIGNAL(contentsChanged()), decorator, SLOT(adjust()));
}
else {
decorator_ -> removeSceneEventFilter(this);
endEdition();
}
decorator_ = decorator;
}
/**
@reimp QGraphicsItem::sceneEventFilter(QGraphicsItem *, QEvent *).
Intercepts events before they reach the watched target, i.e. typically the
primitives decorator.
This method mainly works with key strokes (F2, escape) and double clicks to
begin or end text edition.
*/
bool PartText::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
if (watched != decorator_) return(false);
QPointF event_scene_pos = QET::graphicsSceneEventPos(event);
if (!event_scene_pos.isNull()) {
if (contains(mapFromScene(event_scene_pos))) {
if (hasFocus()) {
return sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
} else {
if (event -> type() == QEvent::GraphicsSceneMouseDoubleClick) {
mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
}
}
}
}
else if (event -> type() == QEvent::KeyRelease || event -> type() == QEvent::KeyPress) {
// Intercept F2 and escape keystrokes to focus in and out
QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
if (key_event -> key() == Qt::Key_F2) {
setEditable(true);
QTextCursor qtc = textCursor();
qtc.setPosition(qMax(0, document()->characterCount() - 1));
setTextCursor(qtc);
} else if (hasFocus() && key_event -> key() == Qt::Key_Escape) {
endEdition();
}
sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
}
return(false);
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartText::singleItemPressEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartText::singleItemMoveEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartText::singleItemReleaseEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartText::singleItemDoubleClickEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
// calling mouseDoubleClickEvent() will set this text item editable and grab keyboard focus
if (event -> button() == Qt::LeftButton) {
mouseDoubleClickEvent(event);
return(true);
}
return(false);
}
/**
Cette methode s'assure que la position du champ de texte est coherente
en repositionnant son origine (c-a-d le milieu du bord gauche du champ de
@@ -349,6 +474,59 @@ void PartText::adjustItemPosition(int new_block_count) {
setTransformOriginPoint(origin_offset);
}
/**
@param editable Whether this text item should be interactively editable.
*/
void PartText::setEditable(bool editable) {
if (editable) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
setFocus(Qt::MouseFocusReason);
}
else {
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
}
/**
Start text edition by storing the former value of the text.
*/
void PartText::startEdition() {
// !previous_text.isNull() means the text is being edited
previous_text = toPlainText();
}
/**
End text edition, potentially generating a ChangePartCommand if the text
has changed.
*/
void PartText::endEdition() {
if (!previous_text.isNull()) {
// the text was being edited
QString new_text = toPlainText();
if (previous_text != new_text) {
// the text was changed
ChangePartCommand *text_change = new ChangePartCommand(
TextEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
new_text
);
previous_text = QString();
undoStack().push(text_change);
}
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setEditable(false);
}
#ifdef QET_DEBUG_EDITOR_TEXTS
/**
Dessine deux petites fleches pour mettre un point en valeur

View File

@@ -20,6 +20,7 @@
#include <QtGui>
#include "customelementpart.h"
class TextEditor;
class ElementPrimitiveDecorator;
/**
This class represents an static text primitive which may be used to compose
the drawing of an electrical element within the element editor.
@@ -59,10 +60,23 @@ class PartText : public QGraphicsTextItem, public CustomElementPart {
virtual void handleUserTransformation(const QRectF &, const QRectF &);
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0 );
virtual void setDecorator(ElementPrimitiveDecorator *);
virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
public slots:
void adjustItemPosition(int = 0);
void setEditable(bool);
void startEdition();
void endEdition();
protected:
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *);
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *);
virtual bool sceneEventFilter(QGraphicsItem *, QEvent *);
virtual void focusInEvent(QFocusEvent *);
virtual void focusOutEvent(QFocusEvent *);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
@@ -74,7 +88,9 @@ class PartText : public QGraphicsTextItem, public CustomElementPart {
void drawPoint(QPainter *, const QPointF &);
#endif
QString previous_text;
qreal real_font_size_;
QPointF saved_point_;
int saved_font_size_;
qreal saved_font_size_;
QGraphicsItem *decorator_;
};
#endif

View File

@@ -18,6 +18,7 @@
#include "parttextfield.h"
#include "textfieldeditor.h"
#include "editorcommands.h"
#include "elementprimitivedecorator.h"
#include "qetapp.h"
/**
@@ -29,14 +30,18 @@
PartTextField::PartTextField(QETElementEditor *editor, QGraphicsItem *parent, QGraphicsScene *scene) :
QGraphicsTextItem(parent, scene),
CustomElementPart(editor),
follow_parent_rotations(true)
follow_parent_rotations(true),
previous_text(),
decorator_(0)
{
setDefaultTextColor(Qt::black);
setFont(QETApp::diagramTextsFont());
setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
real_font_size_ = font().pointSize();
setFlags(QGraphicsItem::ItemIsSelectable);
#if QT_VERSION >= 0x040600
setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
#endif
setAcceptHoverEvents(true);
setPlainText(QObject::tr("_", "default text when adding a textfield in the element editor"));
adjustItemPosition(1);
@@ -58,7 +63,7 @@ void PartTextField::fromXml(const QDomElement &xml_element) {
int font_size = xml_element.attribute("size").toInt(&ok);
if (!ok || font_size < 1) font_size = 20;
setFont(QETApp::diagramTextsFont(font_size));
setProperty("size", font_size);
setPlainText(xml_element.attribute("text"));
qreal default_rotation_angle = 0.0;
@@ -134,32 +139,83 @@ QPointF PartTextField::margin() const {
return(QPointF(0.0, boundingRect().bottom() / 2.0));
}
/**
Handle context menu events.
@param event Object describing the context menu event to handle.
*/
void PartTextField::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
Q_UNUSED(event);
}
/**
Handle events generated when the mouse hovers over the decorator.
@param event Object describing the hover event.
*/
void PartTextField::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
// force the cursor when the text is being edited
if (hasFocus() && decorator_) {
decorator_ -> setCursor(Qt::IBeamCursor);
}
QGraphicsTextItem::hoverMoveEvent(event);
}
/**
@reimp QGraphicsItem::sceneEventFilter(QGraphicsItem *, QEvent *).
Intercepts events before they reach the watched target, i.e. typically the
primitives decorator.
This method mainly works with key strokes (F2, escape) and double clicks to
begin or end text edition.
*/
bool PartTextField::sceneEventFilter(QGraphicsItem *watched, QEvent *event) {
if (watched != decorator_) return(false);
QPointF event_scene_pos = QET::graphicsSceneEventPos(event);
if (!event_scene_pos.isNull()) {
if (contains(mapFromScene(event_scene_pos))) {
if (hasFocus()) {
return sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
} else {
if (event -> type() == QEvent::GraphicsSceneMouseDoubleClick) {
mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
}
}
}
}
else if (event -> type() == QEvent::KeyRelease || event -> type() == QEvent::KeyPress) {
// Intercept F2 and escape keystrokes to focus in and out
QKeyEvent *key_event = static_cast<QKeyEvent *>(event);
if (key_event -> key() == Qt::Key_F2) {
setEditable(true);
QTextCursor qtc = textCursor();
qtc.setPosition(qMax(0, document()->characterCount() - 1));
setTextCursor(qtc);
} else if (hasFocus() && key_event -> key() == Qt::Key_Escape) {
endEdition();
}
sceneEvent(event); // manually deliver the event to this item
return(true); // prevent this event from being delivered to any item
}
return(false);
}
/*
@reimp QGraphicsItem::focusInEvent(QFocusEvent *)
@param e The QFocusEvent object describing the focus gain.
Start text edition when the item gains focus.
*/
void PartTextField::focusInEvent(QFocusEvent *e) {
startEdition();
QGraphicsTextItem::focusInEvent(e);
}
/**
Permet a l'element texte de redevenir deplacable a la fin de l'edition de texte
@param e Le QFocusEvent decrivant la perte de focus
*/
void PartTextField::focusOutEvent(QFocusEvent *e) {
QGraphicsTextItem::focusOutEvent(e);
if (previous_text != toPlainText()) {
undoStack().push(
new ChangePartCommand(
TextFieldEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
toPlainText()
)
);
previous_text = toPlainText();
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
endEdition();
}
/**
@@ -167,11 +223,10 @@ void PartTextField::focusOutEvent(QFocusEvent *e) {
@param e Le QGraphicsSceneMouseEvent qui decrit le double-clic
*/
void PartTextField::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *e) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
previous_text = toPlainText();
QGraphicsTextItem::mouseDoubleClickEvent(e);
setFocus(Qt::MouseFocusReason);
if (e -> button() == Qt::LeftButton) {
setEditable(true);
}
}
/**
@@ -194,6 +249,11 @@ void PartTextField::setProperty(const QString &property, const QVariant &value)
} else if (property == "size") {
if (!value.canConvert(QVariant::Int)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toInt();
} else if (property == "real_size") {
if (!value.canConvert(QVariant::Double)) return;
setFont(QETApp::diagramTextsFont(value.toInt()));
real_font_size_ = value.toDouble();
} else if (property == "text") {
setPlainText(value.toString());
} else if (property == "rotation angle") {
@@ -223,6 +283,8 @@ QVariant PartTextField::property(const QString &property) {
return(pos().y());
} else if (property == "size") {
return(font().pointSize());
} else if (property == "real_size") {
return(real_font_size_);
} else if (property == "text") {
return(toPlainText());
} else if (property == "rotation angle") {
@@ -275,7 +337,7 @@ bool PartTextField::isUseless() const {
void PartTextField::startUserTransformation(const QRectF &initial_selection_rect) {
Q_UNUSED(initial_selection_rect)
saved_point_ = pos(); // scene coordinates, no need to mapFromScene()
saved_font_size_ = font().pointSize();
saved_font_size_ = real_font_size_;
}
/**
@@ -286,12 +348,10 @@ void PartTextField::handleUserTransformation(const QRectF &initial_selection_rec
QPointF new_pos = mapPoints(initial_selection_rect, new_selection_rect, QList<QPointF>() << saved_point_).first();
setPos(new_pos);
// adjust the font size following the smallest scale factor
qreal sx = new_selection_rect.width() / initial_selection_rect.width();
// adjust the font size following the vertical scale factor
qreal sy = new_selection_rect.height() / initial_selection_rect.height();
qreal smallest_scale_factor = sx > sy ? sy : sx;
qreal new_font_size = saved_font_size_ * smallest_scale_factor;
setProperty("size", qMax(1, qRound(new_font_size)));
qreal new_font_size = saved_font_size_ * sy;
setProperty("real_size", qMax(1, qRound(new_font_size)));
}
/**
Dessine le texte statique.
@@ -300,8 +360,12 @@ void PartTextField::handleUserTransformation(const QRectF &initial_selection_rec
@param widget Widget sur lequel on dessine (facultatif)
*/
void PartTextField::paint(QPainter *painter, const QStyleOptionGraphicsItem *qsogi, QWidget *widget) {
QGraphicsTextItem::paint(painter, qsogi, widget);
// According to the source code of QGraphicsTextItem::paint(), this should
// avoid the drawing of the dashed rectangle around the text.
QStyleOptionGraphicsItem our_qsogi(*qsogi);
our_qsogi.state = QStyle::State_None;
QGraphicsTextItem::paint(painter, &our_qsogi, widget);
#ifdef QET_DEBUG_EDITOR_TEXTS
painter -> setPen(Qt::blue);
painter -> drawRect(boundingRect());
@@ -314,6 +378,64 @@ void PartTextField::paint(QPainter *painter, const QStyleOptionGraphicsItem *qso
#endif
}
/**
@reimp CustomElementPart::setDecorator(ElementPrimitiveDecorator *)
Install or remove a sceneEventFilter on the decorator and ensure it will
adjust itself while the text is being edited.
*/
void PartTextField::setDecorator(ElementPrimitiveDecorator *decorator) {
if (decorator) {
decorator -> installSceneEventFilter(this);
// ensure the decorator will adjust itself when the text area expands or shrinks
connect(document(), SIGNAL(contentsChanged()), decorator, SLOT(adjust()));
}
else {
decorator_ -> removeSceneEventFilter(this);
endEdition();
}
decorator_ = decorator;
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemPressEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemMoveEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemReleaseEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
Q_UNUSED(event)
return(hasFocus());
}
/**
Accept the mouse \a event relayed by \a decorator if this text item has focus.
*/
bool PartTextField::singleItemDoubleClickEvent(ElementPrimitiveDecorator *decorator, QGraphicsSceneMouseEvent *event) {
Q_UNUSED(decorator)
// calling mouseDoubleClickEvent() will set this text item editable and grab keyboard focus
if (event -> button() == Qt::LeftButton) {
mouseDoubleClickEvent(event);
return(true);
}
return(false);
}
/**
Cette methode s'assure que la position du champ de texte est coherente
en repositionnant son origine (c-a-d le milieu du bord gauche du champ de
@@ -331,6 +453,59 @@ void PartTextField::adjustItemPosition(int new_block_count) {
setTransformOriginPoint(0.0, origin_offset);
}
/**
@param editable Whether this text item should be interactively editable.
*/
void PartTextField::setEditable(bool editable) {
if (editable) {
setFlag(QGraphicsItem::ItemIsFocusable, true);
setTextInteractionFlags(Qt::TextEditorInteraction);
setFocus(Qt::MouseFocusReason);
}
else {
setTextInteractionFlags(Qt::NoTextInteraction);
setFlag(QGraphicsItem::ItemIsFocusable, false);
}
}
/**
Start text edition by storing the former value of the text.
*/
void PartTextField::startEdition() {
// !previous_text.isNull() means the text is being edited
previous_text = toPlainText();
}
/**
End text edition, potentially generating a ChangePartCommand if the text
has changed.
*/
void PartTextField::endEdition() {
if (!previous_text.isNull()) {
// the text was being edited
QString new_text = toPlainText();
if (previous_text != new_text) {
// the text was changed
ChangePartCommand *text_change = new ChangePartCommand(
TextFieldEditor::tr("contenu") + " " + name(),
this,
"text",
previous_text,
new_text
);
previous_text = QString();
undoStack().push(text_change);
}
}
// deselectionne le texte
QTextCursor qtc = textCursor();
qtc.clearSelection();
setTextCursor(qtc);
setEditable(false);
}
#ifdef QET_DEBUG_EDITOR_TEXTS
/**
Dessine deux petites fleches pour mettre un point en valeur

View File

@@ -21,6 +21,7 @@
#include "customelementpart.h"
class TextFieldEditor;
class QETElementEditor;
class ElementPrimitiveDecorator;
/**
This class represents an editable text field which may be used to compose the
drawing of an electrical element within the element editor. Users may specify
@@ -65,10 +66,23 @@ class PartTextField : public QGraphicsTextItem, public CustomElementPart {
virtual void handleUserTransformation(const QRectF &, const QRectF &);
virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget * = 0 );
virtual void setDecorator(ElementPrimitiveDecorator *);
virtual bool singleItemPressEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemMoveEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemReleaseEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
virtual bool singleItemDoubleClickEvent(ElementPrimitiveDecorator *, QGraphicsSceneMouseEvent *);
public slots:
void adjustItemPosition(int = 0);
void setEditable(bool);
void startEdition();
void endEdition();
protected:
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *);
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *);
virtual bool sceneEventFilter(QGraphicsItem *, QEvent *);
virtual void focusInEvent(QFocusEvent *);
virtual void focusOutEvent(QFocusEvent *);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *);
virtual QVariant itemChange(GraphicsItemChange, const QVariant &);
@@ -80,7 +94,9 @@ class PartTextField : public QGraphicsTextItem, public CustomElementPart {
void drawPoint(QPainter *, const QPointF &);
#endif
QString previous_text;
qreal real_font_size_;
QPointF saved_point_;
int saved_font_size_;
qreal saved_font_size_;
QGraphicsItem *decorator_;
};
#endif

View File

@@ -17,6 +17,7 @@
*/
#include "qet.h"
#include <limits>
#include <QGraphicsSceneContextMenuEvent>
/**
Permet de convertir une chaine de caracteres ("n", "s", "e" ou "w")
@@ -577,3 +578,59 @@ bool QET::writeXmlFile(QDomDocument &xml_doc, const QString &filepath, QString *
return(true);
}
/**
@return the scene position where \a event occurred, provided it is
QGraphicsScene-related event; otherwise, this function returns a null
QPointF.
*/
QPointF QET::graphicsSceneEventPos(QEvent *event) {
QPointF event_scene_pos;
if (event -> type() < QEvent::GraphicsSceneContextMenu) return(event_scene_pos);
if (event -> type() > QEvent::GraphicsSceneWheel) return(event_scene_pos);
switch (event -> type()) {
case QEvent::GraphicsSceneContextMenu: {
QGraphicsSceneContextMenuEvent *qgs_event = static_cast<QGraphicsSceneContextMenuEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
case QEvent::GraphicsSceneDragEnter:
case QEvent::GraphicsSceneDragLeave:
case QEvent::GraphicsSceneDragMove:
case QEvent::GraphicsSceneDrop: {
QGraphicsSceneDragDropEvent *qgs_event = static_cast<QGraphicsSceneDragDropEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
case QEvent::GraphicsSceneHelp: {
QGraphicsSceneHelpEvent *qgs_event = static_cast<QGraphicsSceneHelpEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
case QEvent::GraphicsSceneHoverEnter:
case QEvent::GraphicsSceneHoverLeave:
case QEvent::GraphicsSceneHoverMove: {
QGraphicsSceneHoverEvent *qgs_event = static_cast<QGraphicsSceneHoverEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
case QEvent::GraphicsSceneMouseDoubleClick:
case QEvent::GraphicsSceneMouseMove:
case QEvent::GraphicsSceneMousePress:
case QEvent::GraphicsSceneMouseRelease: {
QGraphicsSceneMouseEvent *qgs_event = static_cast<QGraphicsSceneMouseEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
case QEvent::GraphicsSceneWheel: {
QGraphicsSceneWheelEvent *qgs_event = static_cast<QGraphicsSceneWheelEvent *>(event);
event_scene_pos = qgs_event -> scenePos();
break;
}
default:
break;
}
return(event_scene_pos);
}

View File

@@ -43,6 +43,22 @@ namespace QET {
ToNorthWest
};
/// List areas related to some common operations
enum OperationAreas {
ChangeInnerPoints = -4,
RotateArea = -3,
MoveArea = -2,
NoOperation = -1,
ResizeFromTopLeftCorner = 0,
ResizeFromTopCenterCorner = 1,
ResizeFromTopRightCorner = 2,
ResizeFromMiddleLeftCorner = 3,
ResizeFromMiddleRightCorner = 4,
ResizeFromBottomLeftCorner = 5,
ResizeFromBottomCenterCorner = 6,
ResizeFromBottomRightCorner = 7
};
/// Known kinds of conductor segments
enum ConductorSegmentType {
Horizontal = 1, ///< Horizontal segment
@@ -149,5 +165,6 @@ namespace QET {
bool compareCanonicalFilePaths(const QString &, const QString &);
QString titleBlockColumnLengthToString(const TitleBlockColumnLength &);
bool writeXmlFile(QDomDocument &, const QString &, QString * = 0);
QPointF graphicsSceneEventPos(QEvent *);
}
#endif