Rapatriement de la branche 0.2 dans le trunk

git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/trunk@558 bfdf4180-ca20-0410-9c96-a3a8aa849046
This commit is contained in:
xavier
2009-04-03 19:30:25 +00:00
parent f84b86e4a4
commit 4da7e54d75
366 changed files with 31970 additions and 3696 deletions

View File

@@ -1,5 +1,5 @@
/*
Copyright 2006-2007 Xavier Guerrin
Copyright 2006-2009 Xavier Guerrin
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "qetelementeditor.h"
#include <cmath>
#include "partline.h"
#include "partrectangle.h"
#include "partellipse.h"
#include "partcircle.h"
#include "partpolygon.h"
@@ -28,9 +29,7 @@
#include "partarc.h"
#include "hotspoteditor.h"
#include "editorcommands.h"
const int ElementScene::xGrid = 10;
const int ElementScene::yGrid = 10;
#include "elementcontent.h"
/**
Constructeur
@@ -47,6 +46,8 @@ ElementScene::ElementScene(QETElementEditor *editor, QObject *parent) :
element_editor(editor)
{
current_polygon = NULL;
setGrid(1, 1);
initPasteArea();
undo_stack.setClean();
}
@@ -68,6 +69,13 @@ void ElementScene::slot_addLine() {
behavior = Line;
}
/**
Passe la scene en mode "ajout de rectangle"
*/
void ElementScene::slot_addRectangle() {
behavior = Rectangle;
}
/**
Passe la scene en mode "ajout de cercle"
*/
@@ -104,7 +112,6 @@ void ElementScene::slot_addTerminal() {
behavior = Terminal;
}
/**
Passe la scene en mode "ajout d'arc de cercle"
*/
@@ -124,7 +131,17 @@ void ElementScene::slot_addTextField() {
@param e objet decrivant l'evenement
*/
void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
QPointF event_pos = e -> scenePos();
if (mustSnapToGrid(e)) snapToGrid(event_pos);
if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
if (behavior == PasteArea) {
QRectF current_rect(paste_area_ -> rect());
current_rect.moveCenter(event_pos);
paste_area_ -> setRect(current_rect);
return;
}
QRectF temp_rect;
qreal radius;
QPointF temp_point;
@@ -132,21 +149,26 @@ void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if (e -> buttons() & Qt::LeftButton) {
switch(behavior) {
case Line:
current_line -> setLine(QLineF(current_line -> line().p1(), e -> scenePos()));
current_line -> setLine(QLineF(current_line -> line().p1(), event_pos));
break;
case Rectangle:
temp_rect = current_rectangle -> rect();
temp_rect.setBottomRight(event_pos);
current_rectangle -> setRect(temp_rect);
break;
case Ellipse:
temp_rect = current_ellipse -> rect();
temp_rect.setBottomRight(e -> scenePos());
temp_rect.setBottomRight(event_pos);
current_ellipse -> setRect(temp_rect);
break;
case Arc:
temp_rect = current_arc -> rect();
temp_rect.setBottomRight(e -> scenePos());
temp_rect.setBottomRight(event_pos);
current_arc -> setRect(temp_rect);
break;
case Circle:
temp_rect = current_circle -> rect();
temp_point = e -> scenePos() - current_circle -> mapToScene(temp_rect.center());
temp_point = event_pos - current_circle -> mapToScene(temp_rect.center());
radius = sqrt(pow(temp_point.x(), 2) + pow(temp_point.y(), 2));
temp_rect = QRectF(
temp_rect.center() - QPointF(radius, radius),
@@ -158,17 +180,35 @@ void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
if (current_polygon == NULL) break;
temp_polygon = current_polygon -> polygon();
temp_polygon.pop_back();
temp_polygon << e -> scenePos();
temp_polygon << event_pos;
current_polygon -> setPolygon(temp_polygon);
break;
case Normal:
default:
QGraphicsScene::mouseMoveEvent(e);
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());
}
}
}
} else if (behavior == Polygon && current_polygon != NULL) {
temp_polygon = current_polygon -> polygon();
temp_polygon.pop_back();
temp_polygon << e -> scenePos();
temp_polygon << event_pos;
current_polygon -> setPolygon(temp_polygon);
} else QGraphicsScene::mouseMoveEvent(e);
}
@@ -178,27 +218,34 @@ void ElementScene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
@param e objet decrivant l'evenement
*/
void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
QPointF event_pos = e -> scenePos();
if (mustSnapToGrid(e)) snapToGrid(event_pos);
if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
QPolygonF temp_polygon;
if (e -> button() & Qt::LeftButton) {
switch(behavior) {
case Line:
current_line = new PartLine(element_editor, 0, this);
current_line -> setLine(QLineF(e -> scenePos(), e -> scenePos()));
current_line -> setLine(QLineF(event_pos, event_pos));
break;
case Rectangle:
current_rectangle = new PartRectangle(element_editor, 0, this);
current_rectangle -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
break;
case Ellipse:
current_ellipse = new PartEllipse(element_editor, 0, this);
current_ellipse -> setRect(QRectF(e -> scenePos(), QSizeF(0.0, 0.0)));
current_ellipse -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
current_ellipse -> setProperty("antialias", true);
break;
case Arc:
current_arc = new PartArc(element_editor, 0, this);
current_arc -> setRect(QRectF(e -> scenePos(), QSizeF(0.0, 0.0)));
current_arc -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
current_arc -> setProperty("antialias", true);
break;
case Circle:
current_circle = new PartCircle(element_editor, 0, this);
current_circle -> setRect(QRectF(e -> scenePos(), QSizeF(0.0, 0.0)));
current_circle -> setRect(QRectF(event_pos, QSizeF(0.0, 0.0)));
current_circle -> setProperty("antialias", true);
break;
case Polygon:
@@ -207,14 +254,23 @@ void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
temp_polygon = QPolygonF(0);
} else temp_polygon = current_polygon -> polygon();
// au debut, on insere deux points
if (!temp_polygon.count()) temp_polygon << e -> scenePos();
temp_polygon << e -> scenePos();
if (!temp_polygon.count()) temp_polygon << event_pos;
temp_polygon << event_pos;
current_polygon -> setPolygon(temp_polygon);
break;
case Normal:
default:
QGraphicsScene::mousePressEvent(e);
if (!selectedItems().isEmpty()) fsi_pos = selectedItems().first() -> scenePos();
// 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);
}
@@ -224,71 +280,97 @@ void ElementScene::mousePressEvent(QGraphicsSceneMouseEvent *e) {
@param e objet decrivant l'evenement
*/
void ElementScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
QPointF event_pos = e -> scenePos();
if (mustSnapToGrid(e)) snapToGrid(event_pos);
PartTerminal *terminal;
PartText *text;
PartTextField *textfield;
if (behavior != Polygon && current_polygon != NULL) current_polygon = NULL;
if (behavior == PasteArea) {
defined_paste_area_ = paste_area_ -> rect();
removeItem(paste_area_);
emit(pasteAreaDefined(defined_paste_area_));
behavior = Normal;
return;
}
if (e -> button() & Qt::LeftButton) {
switch(behavior) {
case Line:
if (qgiManager().manages(current_line)) break;
undo_stack.push(new AddPartCommand(tr("ligne"), this, current_line));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Rectangle:
if (qgiManager().manages(current_rectangle)) break;
current_rectangle -> setRect(current_rectangle -> rect().normalized());
undo_stack.push(new AddPartCommand(tr("rectangle"), this, current_rectangle));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Ellipse:
if (qgiManager().manages(current_ellipse)) break;
current_ellipse -> setRect(current_ellipse -> rect().normalized());
undo_stack.push(new AddPartCommand(tr("ellipse"), this, current_ellipse));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Arc:
if (qgiManager().manages(current_arc)) break;
current_arc-> setRect(current_arc -> rect().normalized());
undo_stack.push(new AddPartCommand(tr("arc"), this, current_arc));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Circle:
if (qgiManager().manages(current_circle)) break;
current_circle -> setRect(current_circle -> rect().normalized());
undo_stack.push(new AddPartCommand(tr("cercle"), this, current_circle));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Terminal:
terminal = new PartTerminal(element_editor, 0, this);
terminal -> setPos(e -> scenePos());
terminal -> setPos(event_pos);
undo_stack.push(new AddPartCommand(tr("borne"), this, terminal));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Text:
text = new PartText(element_editor, 0, this);
text -> setPos(e -> scenePos());
text -> setPos(event_pos);
undo_stack.push(new AddPartCommand(tr("texte"), this, text));
emit(partsAdded());
endCurrentBehavior(e);
break;
case TextField:
textfield = new PartTextField(element_editor, 0, this);
textfield -> setPos(e -> scenePos());
textfield -> setPos(event_pos);
undo_stack.push(new AddPartCommand(tr("champ de texte"), this, textfield));
emit(partsAdded());
endCurrentBehavior(e);
break;
case Normal:
default:
QGraphicsScene::mouseReleaseEvent(e);
// detecte les deplacements de parties
if (!selectedItems().isEmpty()) {
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;
}
} else if (e -> button() & Qt::RightButton) {
if (behavior == Polygon && current_polygon != NULL) {
behavior = Normal;
undo_stack.push(new AddPartCommand(tr("polygone"), this, current_polygon));
current_polygon = NULL;
emit(partsAdded());
emit(needNormalMode());
endCurrentBehavior(e);
} else QGraphicsScene::mouseReleaseEvent(e);
} else QGraphicsScene::mouseReleaseEvent(e);
}
@@ -317,6 +399,10 @@ void ElementScene::drawBackground(QPainter *p, const QRectF &r) {
p -> setBrush(Qt::NoBrush);
p -> drawRect(drawable_area);
// on dessine un point de la grille sur 10
int drawn_x_grid = x_grid * 10;
int drawn_y_grid = y_grid * 10;
if (r.width() < 2500 && r.height() < 2500) {
// dessine les points de la grille
p -> setPen(Qt::black);
@@ -325,12 +411,12 @@ void ElementScene::drawBackground(QPainter *p, const QRectF &r) {
qreal limite_y = r.y() + r.height();
int g_x = (int)ceil(r.x());
while (g_x % xGrid) ++ g_x;
while (g_x % drawn_x_grid) ++ g_x;
int g_y = (int)ceil(r.y());
while (g_y % yGrid) ++ g_y;
while (g_y % drawn_y_grid) ++ g_y;
for (int gx = g_x ; gx < limite_x ; gx += xGrid) {
for (int gy = g_y ; gy < limite_y ; gy += yGrid) {
for (int gx = g_x ; gx < limite_x ; gx += drawn_x_grid) {
for (int gy = g_y ; gy < limite_y ; gy += drawn_y_grid) {
p -> drawPoint(gx, gy);
}
}
@@ -358,11 +444,50 @@ void ElementScene::drawForeground(QPainter *p, const QRectF &) {
p -> restore();
}
/**
A partir d'un evenement souris, cette methode regarde si la touche shift est
enfoncee ou non. Si oui, elle laisse le comportement en cours (cercle,
texte, polygone, ...). Si non, elle repasse en mode normal / selection.
@param e objet decrivant l'evenement souris
*/
void ElementScene::endCurrentBehavior(const QGraphicsSceneMouseEvent *event) {
if (!(event -> modifiers() & Qt::ShiftModifier)) {
// la touche Shift n'est pas enfoncee : on demande le mode normal
behavior = Normal;
emit(needNormalMode());
}
}
/**
@return la taille horizontale de la grille
*/
int ElementScene::xGrid() const {
return(x_grid);
}
/**
@return la taille verticale de la grille
*/
int ElementScene::yGrid() const {
return(y_grid);
}
/**
@param x_grid Taille horizontale de la grille
@param y_grid Taille verticale de la grille
*/
void ElementScene::setGrid(int x_g, int y_g) {
x_grid = x_g ? x_g : 1;
y_grid = y_g ? y_g : 1;
}
/**
Exporte l'element en XML
@param diagram Booleen (a vrai par defaut) indiquant si le XML genere doit
representer tout l'element ou seulement les elements selectionnes
@return un document XML decrivant l'element
*/
const QDomDocument ElementScene::toXml() const {
const QDomDocument ElementScene::toXml(bool all_parts) const {
// document XML
QDomDocument xml_document;
@@ -383,6 +508,8 @@ const QDomDocument ElementScene::toXml() const {
QDomElement description = xml_document.createElement("description");
// description de l'element
foreach(QGraphicsItem *qgi, zItems(true)) {
// si l'export ne concerne que la selection, on ignore les parties non selectionnees
if (!all_parts && !qgi -> isSelected()) continue;
if (CustomElementPart *ce = dynamic_cast<CustomElementPart *>(qgi)) {
if (ce -> isUseless()) continue;
description.appendChild(ce -> toXml(xml_document));
@@ -395,91 +522,90 @@ const QDomDocument ElementScene::toXml() const {
}
/**
Lit un element depuis un document XML
@param xml_document un document XML decrivant l'element
@param xml_document un document XML decrivant un element
@return le boundingRect du contenu de l'element
*/
void ElementScene::fromXml(const QDomDocument &xml_document) {
QRectF ElementScene::boundingRectFromXml(const QDomDocument &xml_document) {
// charge les parties depuis le document XML
ElementContent loaded_content = loadContent(xml_document);
if (loaded_content.isEmpty()) return(QRectF());
// calcule le boundingRect
QRectF bounding_rect = elementContentBoundingRect(loaded_content);
// detruit les parties chargees
qDeleteAll(loaded_content);
return(bounding_rect);
}
/**
Importe l'element decrit dans un document XML. Si une position est
precisee, les elements importes sont positionnes de maniere a ce que le
coin superieur gauche du plus petit rectangle pouvant les entourant tous
(le bounding rect) soit a cette position.
@param xml_document un document XML decrivant l'element
@param position La position des parties importees
@param consider_informations Si vrai, les informations complementaires
(dimensions, hotspot, etc.) seront prises en compte
@param content_ptr si ce pointeur vers un ElementContent est different de 0,
il sera rempli avec le contenu ajoute a l'element par le fromXml
@return true si l'import a reussi, false sinon
*/
void ElementScene::fromXml(
const QDomDocument &xml_document,
const QPointF &position,
bool consider_informations,
ElementContent *content_ptr
) {
QString error_message;
bool state = true;
// la racine est supposee etre une definition d'element
QDomElement root = xml_document.documentElement();
if (root.tagName() != "definition" || root.attribute("type") != "element") {
state = false;
error_message = tr("Ce document XML n'est pas une definition d'\351l\351ment.");
}
// dimensions et hotspot
if (state) {
// ces attributs doivent etre presents et valides
int w, h, hot_x, hot_y;
if (
!QET::attributeIsAnInteger(root, QString("width"), &w) ||\
!QET::attributeIsAnInteger(root, QString("height"), &h) ||\
!QET::attributeIsAnInteger(root, QString("hotspot_x"), &hot_x) ||\
!QET::attributeIsAnInteger(root, QString("hotspot_y"), &hot_y)
) {
state = false;
error_message = tr("Les dimensions ou le point de saisie ne sont pas valides.");
} else {
setWidth(w);
setHeight(h);
setHotspot(QPoint(hot_x, hot_y));
}
}
// orientations et connexions internes
if (state) {
internal_connections = (root.attribute("ic") == "true");
if (!ori.fromString(root.attribute("orientation"))) {
state = false;
error_message = tr("Les orientations ne sont pas valides.");
}
}
// extrait les noms de la definition XML
if (state) {
_names.fromXml(root);
// prend en compte les informations de l'element
if (consider_informations) {
state = applyInformations(xml_document, &error_message);
}
// parcours des enfants de la definition : parties de l'element
if (state) {
for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
QDomElement elmts = node.toElement();
if (elmts.isNull()) continue;
if (elmts.tagName() == "description") {
// gestion de la description graphique de l'element
// = parcours des differentes parties du dessin
int z = 1;
for (QDomNode n = node.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
QDomElement qde = n.toElement();
if (qde.isNull()) continue;
CustomElementPart *cep;
if (qde.tagName() == "line") cep = new PartLine (element_editor, 0, this);
else if (qde.tagName() == "ellipse") cep = new PartEllipse (element_editor, 0, this);
else if (qde.tagName() == "circle") cep = new PartCircle (element_editor, 0, this);
else if (qde.tagName() == "polygon") cep = new PartPolygon (element_editor, 0, this);
else if (qde.tagName() == "terminal") cep = new PartTerminal (element_editor, 0, this);
else if (qde.tagName() == "text") cep = new PartText (element_editor, 0, this);
else if (qde.tagName() == "input") cep = new PartTextField(element_editor, 0, this);
else if (qde.tagName() == "arc") cep = new PartArc (element_editor, 0, this);
else continue;
if (QGraphicsItem *qgi = dynamic_cast<QGraphicsItem *>(cep)) qgi -> setZValue(z++);
cep -> fromXml(qde);
}
}
ElementContent loaded_content = loadContent(xml_document, &error_message);
if (position != QPointF()) {
addContentAtPos(loaded_content, position, &error_message);
} else {
addContent(loaded_content, &error_message);
}
// renvoie le contenu ajoute a l'element
if (content_ptr) {
*content_ptr = loaded_content;
}
}
}
/**
@return le rectangle representant les limites de l'element.
Ce rectangle a pour dimensions la taille de l'element et pour coin
superieur gauche les coordonnees opposees du hotspot.
*/
QRectF ElementScene::borderRect() const {
return(QRectF(-_hotspot, QSizeF(width(), height())));
}
/**
@return un rectangle englobant toutes les parties ainsi que le
"bounding rect" de l'element
*/
QRectF ElementScene::sceneContent() const {
return(itemsBoundingRect().unite(QRectF(-_hotspot, QSizeF(width(), height()))));
return(itemsBoundingRect().unite(borderRect()));
}
/**
@return true si toutes les parties graphiques composant l'element sont
integralement contenues dans le rectangle representant les limites de
l'element.
*/
bool ElementScene::borderContainsEveryParts() const {
return(borderRect().contains(itemsBoundingRect()));
}
/**
@@ -496,6 +622,64 @@ QGIManager &ElementScene::qgiManager() {
return(qgi_manager);
}
/**
@return true si le presse-papier semble contenir un element
*/
bool ElementScene::clipboardMayContainElement() {
QString clipboard_text = QApplication::clipboard() -> text().trimmed();
bool may_be_element = clipboard_text.startsWith("<definition") && clipboard_text.endsWith("</definition>");
return(may_be_element);
}
/**
@param clipboard_content chaine de caractere, provenant vraisemblablement du
presse-papier.
@return true si clipboard_content a ete copie depuis cet element.
*/
bool ElementScene::wasCopiedFromThisElement(const QString &clipboard_content) {
return(clipboard_content == last_copied_);
}
/**
Gere le fait de couper la selection = l'exporter en XML dans le
presse-papier puis la supprimer.
*/
void ElementScene::cut() {
copy();
QList<QGraphicsItem *> cut_content = selectedItems();
clearSelection();
undoStack().push(new CutPartsCommand(this, cut_content));
}
/**
Gere le fait de copier la selection = l'exporter en XML dans le
presse-papier.
*/
void ElementScene::copy() {
// accede au presse-papier
QClipboard *clipboard = QApplication::clipboard();
// genere la description XML de la selection
QString clipboard_content = toXml(false).toString(4);
// met la description XML dans le presse-papier
if (clipboard -> supportsSelection()) {
clipboard -> setText(clipboard_content, QClipboard::Selection);
}
clipboard -> setText(clipboard_content);
// retient le dernier contenu copie
last_copied_ = clipboard_content;
}
/**
Gere le fait de coller le contenu du presse-papier = l'importer dans le
presse-papier a une position donnee.
*/
void ElementScene::paste() {
}
/**
Selectionne tout
*/
@@ -538,7 +722,7 @@ void ElementScene::slot_editSizeHotSpot() {
// cree un dialogue
QDialog dialog_sh(element_editor);
dialog_sh.setModal(true);
dialog_sh.setWindowTitle(tr("\311diter la taille et le point de saisie"));
dialog_sh.setWindowTitle(tr("\311diter la taille et le point de saisie", "window title"));
QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_sh);
// ajoute un HotspotEditor au dialogue
@@ -578,7 +762,7 @@ void ElementScene::slot_editOrientations() {
QDialog dialog_ori(element_editor);
dialog_ori.setModal(true);
dialog_ori.setMinimumSize(400, 260);
dialog_ori.setWindowTitle(tr("\311diter les orientations"));
dialog_ori.setWindowTitle(tr("\311diter les orientations", "window title"));
QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog_ori);
// ajoute un champ explicatif au dialogue
@@ -624,7 +808,7 @@ void ElementScene::slot_editNames() {
QDialog dialog(element_editor);
dialog.setModal(true);
dialog.setMinimumSize(400, 330);
dialog.setWindowTitle(tr("\311diter les noms"));
dialog.setWindowTitle(tr("\311diter les noms", "window title"));
QVBoxLayout *dialog_layout = new QVBoxLayout(&dialog);
// ajoute un champ explicatif au dialogue
@@ -716,6 +900,30 @@ QList<QGraphicsItem *> ElementScene::zItems(bool include_terminals) const {
return(all_items_list);
}
/**
@return les parties graphiques selectionnees
*/
ElementContent ElementScene::selectedContent() const {
ElementContent content;
foreach(QGraphicsItem *qgi, zItems(true)) {
if (qgi -> isSelected()) content << qgi;
}
return(content);
}
/**
@param to_paste Rectangle englobant les parties a coller
@return le rectangle ou il faudra coller ces parties
*/
void ElementScene::getPasteArea(const QRectF &to_paste) {
// on le dessine sur la scene
paste_area_ -> setRect(to_paste);
addItem(paste_area_);
// on passe la scene en mode "recherche de zone pour copier/coller"
behavior = PasteArea;
}
/**
Supprime les parties de l'element et les objets d'annulations.
Les autres caracteristiques sont conservees.
@@ -729,3 +937,200 @@ void ElementScene::reset() {
qgiManager().release(qgi);
}
}
/**
@param content Contenu ( = parties) d'un element
@return le boundingRect de ces parties, exprime dans les coordonnes de la
scene
*/
QRectF ElementScene::elementContentBoundingRect(const ElementContent &content) {
QRectF bounding_rect;
foreach(QGraphicsItem *qgi, content) {
bounding_rect |= qgi -> sceneBoundingRect();
}
return(bounding_rect);
}
/**
Applique les informations (dimensions, hostpot, orientations, connexions
internes et noms) contenu dans un document XML.
@param xml_document Document XML a analyser
@param error_message pointeur vers une QString ; si error_message est
different de 0, un message d'erreur sera stocke dedans si necessaire
@return true si la lecture et l'application des informations s'est bien
passee, false sinon.
*/
bool ElementScene::applyInformations(const QDomDocument &xml_document, QString *error_message) {
// la racine est supposee etre une definition d'element
QDomElement root = xml_document.documentElement();
if (root.tagName() != "definition" || root.attribute("type") != "element") {
if (error_message) {
*error_message = tr("Ce document XML n'est pas une d\351finition d'\351l\351ment.", "error message");
}
return(false);
}
// dimensions et hotspot : ces attributs doivent etre presents et valides
int w, h, hot_x, hot_y;
if (
!QET::attributeIsAnInteger(root, QString("width"), &w) ||\
!QET::attributeIsAnInteger(root, QString("height"), &h) ||\
!QET::attributeIsAnInteger(root, QString("hotspot_x"), &hot_x) ||\
!QET::attributeIsAnInteger(root, QString("hotspot_y"), &hot_y)
) {
if (error_message) {
*error_message = tr("Les dimensions ou le point de saisie ne sont pas valides.", "error message");
}
return(false);
}
//
setWidth(w);
setHeight(h);
setHotspot(QPoint(hot_x, hot_y));
// orientations
internal_connections = (root.attribute("ic") == "true");
// connexions internes
if (!ori.fromString(root.attribute("orientation"))) {
if (error_message) {
*error_message = tr("Les orientations ne sont pas valides.", "error message");
}
return(false);
}
// extrait les noms de la definition XML
_names.fromXml(root);
return(true);
}
/**
Par le document XML xml_document et retourne le contenu ( = liste de
parties) correspondant.
@param xml_document Document XML a analyser
@param error_message pointeur vers une QString ; si error_message est
different de 0, un message d'erreur sera stocke dedans si necessaire
*/
ElementContent ElementScene::loadContent(const QDomDocument &xml_document, QString *error_message) {
ElementContent loaded_parts;
// la racine est supposee etre une definition d'element
QDomElement root = xml_document.documentElement();
if (root.tagName() != "definition" || root.attribute("type") != "element") {
if (error_message) {
*error_message = tr("Ce document XML n'est pas une d\351finition d'\351l\351ment.", "error message");
}
return(loaded_parts);
}
// chargement de la description graphique de l'element
for (QDomNode node = root.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
QDomElement elmts = node.toElement();
if (elmts.isNull()) continue;
if (elmts.tagName() == "description") {
// = parcours des differentes parties du dessin
int z = 1;
for (QDomNode n = node.firstChild() ; !n.isNull() ; n = n.nextSibling()) {
QDomElement qde = n.toElement();
if (qde.isNull()) continue;
CustomElementPart *cep;
if (qde.tagName() == "line") cep = new PartLine (element_editor, 0, 0);
else if (qde.tagName() == "rect") cep = new PartRectangle(element_editor, 0, 0);
else if (qde.tagName() == "ellipse") cep = new PartEllipse (element_editor, 0, 0);
else if (qde.tagName() == "circle") cep = new PartCircle (element_editor, 0, 0);
else if (qde.tagName() == "polygon") cep = new PartPolygon (element_editor, 0, 0);
else if (qde.tagName() == "terminal") cep = new PartTerminal (element_editor, 0, 0);
else if (qde.tagName() == "text") cep = new PartText (element_editor, 0, 0);
else if (qde.tagName() == "input") cep = new PartTextField(element_editor, 0, 0);
else if (qde.tagName() == "arc") cep = new PartArc (element_editor, 0, 0);
else continue;
if (QGraphicsItem *qgi = dynamic_cast<QGraphicsItem *>(cep)) {
qgi -> setZValue(z++);
loaded_parts << qgi;
}
cep -> fromXml(qde);
}
}
}
return(loaded_parts);
}
/**
Ajoute le contenu content a cet element
@param content contenu ( = liste de parties) a charger
@param error_message pointeur vers une QString ; si error_message est
different de 0, un message d'erreur sera stocke dedans si necessaire
@return Le contenu ajoute
*/
ElementContent ElementScene::addContent(const ElementContent &content, QString */*error_message*/) {
foreach(QGraphicsItem *part, content) {
addItem(part);
}
return(content);
}
/**
Ajoute le contenu content a cet element
@param content contenu ( = liste de parties) a charger
@param pos Position du coin superieur gauche du contenu apres avoir ete ajoute
@param error_message pointeur vers une QString ; si error_message est
different de 0, un message d'erreur sera stocke dedans si necessaire
@return Le contenu ajoute
*/
ElementContent ElementScene::addContentAtPos(const ElementContent &content, const QPointF &pos, QString */*error_message*/) {
// calcule le boundingRect du contenu a ajouter
QRectF bounding_rect = elementContentBoundingRect(content);
// en deduit le decalage a appliquer aux parties pour les poser au point demander
QPointF offset = pos - bounding_rect.topLeft();
// ajoute les parties avec le decalage adequat
foreach(QGraphicsItem *part, content) {
part -> setPos(part -> pos() + offset);
addItem(part);
}
return(content);
}
/**
Initialise la zone de collage
*/
void ElementScene::initPasteArea() {
paste_area_ = new QGraphicsRectItem();
paste_area_ -> setZValue(1000000);
QPen paste_area_pen;
paste_area_pen.setStyle(Qt::DashDotLine);
paste_area_pen.setColor(QColor(30, 56, 86, 255));
QBrush paste_area_brush;
paste_area_brush.setStyle(Qt::SolidPattern);
paste_area_brush.setColor(QColor(90, 167, 255, 64));
paste_area_ -> setPen(paste_area_pen);
paste_area_ -> setBrush(paste_area_brush);
}
/**
Arrondit les coordonnees du point passees en parametre de facon a ce que ce
point soit aligne sur la grille.
@param point une reference vers un QPointF. Cet objet sera modifie.
*/
void ElementScene::snapToGrid(QPointF &point) {
point.rx() = qRound(point.x() / x_grid) * x_grid;
point.ry() = qRound(point.y() / y_grid) * y_grid;
}
/**
@param e Evenement souris
@return true s'il faut utiliser le snap-to-grid
Typiquement, cette methode retourne true si l'evenement souris se produit
sans la touche Ctrl enfoncee.
*/
bool ElementScene::mustSnapToGrid(QGraphicsSceneMouseEvent *e) {
return(!(e -> modifiers() & Qt::ControlModifier));
}