Logo Search packages:      
Sourcecode: qelectrotech version File versions  Download package

elementview.cpp

/*
      Copyright 2006-2009 Xavier Guerrin
      This file is part of QElectroTech.
      
      QElectroTech is free software: you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation, either version 2 of the License, or
      (at your option) any later version.
      
      QElectroTech is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.
      
      You should have received a copy of the GNU General Public License
      along with QElectroTech.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "elementview.h"
#include "qetelementeditor.h"
#include "editorcommands.h"
/**
      Constructeur
      @param scene ElementScene visualisee par cette ElementView
      @param parent QWidget parent de cette ElementView
*/
00026 ElementView::ElementView(ElementScene *scene, QWidget *parent) :
      QGraphicsView(scene, parent),
      scene_(scene),
      offset_paste_count_(0)
{
      setInteractive(true);
      setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
      setResizeAnchor(QGraphicsView::AnchorUnderMouse);
      setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
      zoomReset();
      connect(scene_, SIGNAL(pasteAreaDefined(const QRectF &)), this, SLOT(pasteAreaDefined(const QRectF &)));
      connect(scene_, SIGNAL(partsAdded()), this, SLOT(adjustSceneRect()));
}

/// Destructeur
00041 ElementView::~ElementView() {
}

/// @return l'ElementScene visualisee par cette ElementView
00045 ElementScene *ElementView::scene() const {
      return(scene_);
}

/**
      @return le rectangle de l'element visualise par cet ElementView
*/
00052 QRectF ElementView::viewedSceneRect() const {
      // recupere la taille du widget viewport
      QSize viewport_size = viewport() -> size();
      
      // recupere la transformation viewport -> scene
      QTransform view_to_scene   = viewportTransform().inverted();
      
      // mappe le coin superieur gauche et le coin inferieur droit de la viewport sur la scene
      QPointF scene_left_top     = view_to_scene.map(QPointF(0.0, 0.0));
      QPointF scene_right_bottom = view_to_scene.map(QPointF(viewport_size.width(), viewport_size.height()));
      
      // en deduit le rectangle visualise par la scene
      return(QRectF(scene_left_top, scene_right_bottom));
}

/**
      Definit l'ElementScene visualisee par cette ElementView
      @param s l'ElementScene visualisee par cette ElementView
*/
00071 void ElementView::setScene(ElementScene *s) {
      QGraphicsView::setScene(s);
      scene_ = s;
}

/**
      Gere les evenements envoyes a la vue.
      Methode reimplentee pour gerer le conflit de raccourcis avec Suppr
      (supprimer une partie ou supprimer le caractere suivant)
      @param e evenement a gerer
*/
00082 bool ElementView::event(QEvent *e) {
      if (e -> type() == QEvent::ShortcutOverride && scene_ -> focusItem()) {
            e -> accept();
            return(true);
      }
      return(QGraphicsView::event(e));
}


/**
      Agrandit le schema (+33% = inverse des -25 % de zoomMoins())
*/
00094 void ElementView::zoomIn() {
      adjustSceneRect();
      scale(4.0/3.0, 4.0/3.0);
}

/**
      Retrecit le schema (-25% = inverse des +33 % de zoomPlus())
*/
00102 void ElementView::zoomOut() {
      adjustSceneRect();
      scale(0.75, 0.75);
}

/**
      Agrandit ou rectrecit le schema de facon a ce que tous les elements du
      schema soient visibles a l'ecran. S'il n'y a aucun element sur le schema,
      le zoom est reinitialise
*/
00112 void ElementView::zoomFit() {
      adjustSceneRect();
      fitInView(sceneRect(), Qt::KeepAspectRatio);
}

/**
      Reinitialise le zoom
*/
00120 void ElementView::zoomReset() {
      adjustSceneRect();
      resetMatrix();
      scale(4.0, 4.0);
}

/**
      Ajuste le sceneRect (zone du schema visualisee par l'ElementView) afin que
      celui inclut a la fois les parties dans et en dehors du cadre et le cadre
      lui-meme.
*/
00131 void ElementView::adjustSceneRect() {
      QRectF old_scene_rect = scene_ -> sceneRect();
      QRectF new_scene_rect = scene_ -> sceneContent();
      setSceneRect(new_scene_rect);
      
      // met a jour la scene
      scene_ -> update(old_scene_rect.united(new_scene_rect));
}

/**
      Gere le fait de couper la selection = l'exporter en XML dans le
      presse-papier puis la supprimer.
*/
00144 void ElementView::cut() {
      // delegue cette action a la scene
      scene_ -> cut();
}

/**
      Gere le fait de copier la selection = l'exporter en XML dans le
      presse-papier.
*/
00153 void ElementView::copy() {
      // delegue cette action a la scene
      scene_ -> copy();
      offset_paste_count_ = 0;
}

/**
      Gere le fait de coller le contenu du presse-papier = l'importer dans
      l'element. Cette methode examine le contenu du presse-papier. Si celui-ci
      semble avoir ete copie depuis cet element, il est colle a cote de sa zone
      d'origine ; s'il est recolle, il sera colle un cran a cote de la zone deja
      recollee, etc.
      Sinon, cette methode demande a l'utilisateur de definir la zone ou le
      collage devra s'effectuer.
      @see pasteAreaDefined(const QRectF &)
*/
00169 void ElementView::paste() {
      QString clipboard_text = QApplication::clipboard() -> text();
      if (clipboard_text.isEmpty()) return;
      
      QDomDocument document_xml;
      if (!document_xml.setContent(clipboard_text)) return;
      
      if (scene_ -> wasCopiedFromThisElement(clipboard_text)) {
            // copier/coller avec decalage
            pasteWithOffset(document_xml);
      } else {
            // copier/coller par choix de la zone de collage
            QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(document_xml);
            if (pasted_content_bounding_rect.isEmpty()) return;
            
            to_paste_in_area_ = clipboard_text;
            getPasteArea(pasted_content_bounding_rect);
      }
}

/**
      Colle le contenu du presse-papier en demandant systematiquement a
      l'utilisateur de choisir une zone de collage
*/
00193 void ElementView::pasteInArea() {
      QString clipboard_text = QApplication::clipboard() -> text();
      if (clipboard_text.isEmpty()) return;
      
      QDomDocument document_xml;
      if (!document_xml.setContent(clipboard_text)) return;
      
      QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(document_xml);
      if (pasted_content_bounding_rect.isEmpty()) return;
      
      // copier/coller par choix de la zone de collage
      to_paste_in_area_ = clipboard_text;
      getPasteArea(pasted_content_bounding_rect);
}

/**
      Gere le fait de coller le contenu du presse-papier = l'importer dans
      l'element. Cette methode examine le contenu du presse-papier. Si celui-ci
      est exploitable, elle le colle a la position passee en parametre.
      @see pasteAreaDefined(const QRectF &)
      @param position Point de collage
*/
00215 ElementContent ElementView::paste(const QPointF &position) {
      QString clipboard_text = QApplication::clipboard() -> text();
      if (clipboard_text.isEmpty()) return(ElementContent());
      
      QDomDocument document_xml;
      if (!document_xml.setContent(clipboard_text)) return(ElementContent());
      
      // objet pour recuperer le contenu ajoute au schema par le coller
      return(paste(document_xml, position));
}

/**
      @param to_paste Rectangle englobant les parties a coller
*/
00229 void ElementView::getPasteArea(const QRectF &to_paste) {
      // on copie le rectangle fourni - on s'interesse a ses dimensions, pas a sa position
      QRectF used_rect(to_paste);
      
      // on lui attribue pour centre l'origine du repere
      if (underMouse()) {
            used_rect.moveCenter(mapToScene(mapFromGlobal(QCursor::pos())));
      } else {
            used_rect.moveCenter(QPointF(0.0, 0.0));
      }
      scene_ -> getPasteArea(used_rect);
}

/**
      Slot appele lorsque la scene annonce avoir defini une zone de collage
      @param target_rect Rectangle cible pour le collage
*/
00246 ElementContent ElementView::pasteAreaDefined(const QRectF &target_rect) {
      if (to_paste_in_area_.isEmpty()) return(ElementContent());
      
      QDomDocument xml_document;
      if (!xml_document.setContent(to_paste_in_area_)) {
            to_paste_in_area_.clear();
            return(ElementContent());
      } else {
            return(paste(xml_document, target_rect.topLeft()));
      }
}

/**
      Colle le document XML xml_document a la position pos
      @param xml_document Document XML a coller
      @param pos Coin superieur gauche du rectangle cible
*/
00263 ElementContent ElementView::paste(const QDomDocument &xml_document, const QPointF &pos) {
      // objet pour recuperer le contenu ajoute au schema par le coller
      ElementContent content_pasted;
      scene_ -> fromXml(xml_document, pos, false, &content_pasted);
      
      // si quelque chose a effectivement ete ajoute au schema, on cree un objet d'annulation
      if (content_pasted.count()) {
            scene_ -> clearSelection();
            PastePartsCommand *undo_object = new PastePartsCommand(this, content_pasted);
            scene_ -> undoStack().push(undo_object);
      }
      return(content_pasted);
}

/**
      Colle le document XML xml_document a la position pos
      @param xml_document Document XML a coller
*/
00281 ElementContent ElementView::pasteWithOffset(const QDomDocument &xml_document) {
      // objet pour recuperer le contenu ajoute au schema par le coller
      ElementContent content_pasted;
      
      // rectangle source
      QRectF pasted_content_bounding_rect = scene_ -> boundingRectFromXml(xml_document);
      if (pasted_content_bounding_rect.isEmpty()) return(content_pasted);
      
      // copier/coller avec decalage
      ++ offset_paste_count_;
      if (offset_paste_count_ == 1) {
            start_top_left_corner_ = pasted_content_bounding_rect.topLeft();
      } else {
            pasted_content_bounding_rect.moveTopLeft(start_top_left_corner_);
      }
      
      // on applique le decalage qui convient
      QRectF final_pasted_content_bounding_rect = applyMovement(
            pasted_content_bounding_rect,
            QETElementEditor::pasteMovement(),
            QETElementEditor::pasteOffset()
      );
      
      QPointF old_start_top_left_corner_ = start_top_left_corner_;
      start_top_left_corner_ = final_pasted_content_bounding_rect.topLeft();
      scene_ -> fromXml(xml_document, start_top_left_corner_, false, &content_pasted);
      
      // si quelque chose a effectivement ete ajoute au schema, on cree un objet d'annulation
      if (content_pasted.count()) {
            scene_ -> clearSelection();
            PastePartsCommand *undo_object = new PastePartsCommand(this, content_pasted);
            undo_object -> setOffset(offset_paste_count_ - 1, old_start_top_left_corner_, offset_paste_count_, start_top_left_corner_);
            scene_ -> undoStack().push(undo_object);
      }
      return(content_pasted);
}

/**
      Gere les clics sur la vue - permet de coller lorsaue l'on enfonce le bouton
      du milieu de la souris.
      @param e QMouseEvent decrivant l'evenement souris
*/
00323 void ElementView::mousePressEvent(QMouseEvent *e) {
      if (e -> buttons() & Qt::MidButton) {
            paste(mapToScene(e -> pos()));
      }
      QGraphicsView::mousePressEvent(e);
}

/**
      Gere les actions liees a la rollette de la souris
      @param e QWheelEvent decrivant l'evenement rollette
*/
00334 void ElementView::wheelEvent(QWheelEvent *e) {
      // si la touche Ctrl est enfoncee, on zoome / dezoome
      if (e -> modifiers() & Qt::ControlModifier) {
            if (e -> delta() > 0) {
                  zoomIn();
            } else {
                  zoomOut();
            }
      } else {
            QAbstractScrollArea::wheelEvent(e);
      }
}

/**
      Dessine l'arriere-plan de l'editeur, cad la grille.
      @param p Le QPainter a utiliser pour dessiner
      @param r Le rectangle de la zone a dessiner
*/
00352 void ElementView::drawBackground(QPainter *p, const QRectF &r) {
      p -> save();
      
      // desactive tout antialiasing, sauf pour le texte
      p -> setRenderHint(QPainter::Antialiasing, false);
      p -> setRenderHint(QPainter::TextAntialiasing, true);
      p -> setRenderHint(QPainter::SmoothPixmapTransform, false);
      
      // dessine un fond blanc
      p -> setPen(Qt::NoPen);
      p -> setBrush(Qt::white);
      p -> drawRect(r);
      
      // encadre la zone dessinable de l'element
      QRectF drawable_area(-scene_ -> hotspot().x(), -scene_ -> hotspot().y(), scene_ -> width(), scene_ -> height());
      p -> setPen(Qt::black);
      p -> setBrush(Qt::NoBrush);
      p -> drawRect(drawable_area);
      
      // determine le zoom en cours
      qreal zoom_factor = matrix().m11();
      
      // choisit la granularite de la grille en fonction du zoom en cours
      int drawn_x_grid = scene_ -> xGrid();
      int drawn_y_grid = scene_ -> yGrid();
      bool draw_grid = true;
      bool draw_cross = false;
      if (zoom_factor < (4.0/3.0)) {
            // pas de grille du tout
            draw_grid = false;
      } else if (zoom_factor < 4.0) {
            // grille a 10 px
            drawn_x_grid *= 10;
            drawn_y_grid *= 10;
      } else if (zoom_factor < 6.0) {
            // grille a 2 px (avec croix)
            drawn_x_grid *= 2;
            drawn_y_grid *= 2;
            draw_cross = true;
      } else {
            // grille a 1 px (avec croix)
            draw_cross = true;
      }
      
      if (draw_grid) {
            // dessine les points de la grille
            p -> setPen(Qt::black);
            p -> setBrush(Qt::NoBrush);
            qreal limite_x = r.x() + r.width();
            qreal limite_y = r.y() + r.height();
            
            int g_x = (int)ceil(r.x());
            while (g_x % drawn_x_grid) ++ g_x;
            int g_y = (int)ceil(r.y());
            while (g_y % drawn_y_grid) ++ g_y;
            
            for (int gx = g_x ; gx < limite_x ; gx += drawn_x_grid) {
                  for (int gy = g_y ; gy < limite_y ; gy += drawn_y_grid) {
                        if (draw_cross) {
                              if (!(gx % 10) && !(gy % 10)) {
                                    p -> drawLine(QLineF(gx - 0.25, gy, gx + 0.25, gy));
                                    p -> drawLine(QLineF(gx, gy - 0.25, gx, gy + 0.25));
                              } else {
                                    p -> drawPoint(gx, gy);
                              }
                        } else {
                              p -> drawPoint(gx, gy);
                        }
                  }
            }
      }
      p -> restore();
}

/**
      Applique le decalage offset dans le sens movement au rectangle start
      @param start rectangle a decaler
      @param movement Orientation du decalage a appliquer
      @param offset Decalage a appliquer
*/
00432 QRectF ElementView::applyMovement(const QRectF &start, const QET::OrientedMovement &movement, const QPointF &offset) {
      // calcule le decalage a appliquer a partir de l'offset indique et du mouvement
      QPointF final_offset;
      if (movement == QET::ToNorthEast || movement == QET::ToEast || movement == QET::ToSouthEast) {
            final_offset.rx() =  start.width() + offset.x();
      } else if (movement == QET::ToNorthWest || movement == QET::ToWest || movement == QET::ToSouthWest) {
            final_offset.rx() = -start.width() - offset.x();
      }
      
      if (movement == QET::ToNorthWest || movement == QET::ToNorth || movement == QET::ToNorthEast) {
            final_offset.ry() = -start.height() - offset.y();
      } else if (movement == QET::ToSouthWest || movement == QET::ToSouth || movement == QET::ToSouthEast) {
            final_offset.ry() =  start.height() + offset.y();
      }
      
      // applique le decalage ainsi calcule
      return(start.translated(final_offset));
}

Generated by  Doxygen 1.6.0   Back to index