Logo Search packages:      
Sourcecode: kdesdk version File versions

floatingtext.cpp

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/
#include "floatingtext.h"

// system includes
#include <qregexp.h>
#include <qpainter.h>
#include <klocale.h>
#include <kdebug.h>

// local includes
#include "associationwidget.h"
#include "association.h"
#include "umlview.h"
#include "umldoc.h"
#include "uml.h"
#include "classifier.h"
#include "messagewidget.h"
#include "listpopupmenu.h"
#include "operation.h"
#include "inputdialog.h"
#include "dialogs/assocpropdlg.h"
#include "dialogs/selectopdlg.h"

00031 FloatingText::FloatingText(UMLView * view, Text_Role role, QString text, int id)
  : UMLWidget(view, id)
{
      init();
      m_Text = text;
      m_Role = role;
}

00039 void FloatingText::init() {
      // initialize loaded/saved (i.e. persistent) data
      m_PreText = "";
      m_Text = "";
      m_PostText = "";
      //m_SeqNum = "";
      //m_Operation = "";
      m_Role = Uml::tr_Floating;
      m_Type = Uml::wt_Text;
      // initialize non-saved (i.e. volatile) data
      m_pLink = NULL;
      if ( ! UMLApp::app()->getDocument()->loading() ) {
            calculateSize();
            setZ( 10 );//make sure always on top.
            update();
      }
}

00057 FloatingText::~FloatingText() {
}

00060 void FloatingText::draw(QPainter & p, int offsetX, int offsetY) {
      int w = width();
      int h = height();
      p.setFont( UMLWidget::getFont() );
      QColor textColor(50, 50, 50);
      p.setPen(textColor);
      p.drawText( offsetX , offsetY,w,h, AlignCenter, getDisplayText() );
      if(m_bSelected)
            drawSelected(&p, offsetX, offsetY);
}

00071 void FloatingText::resizeEvent(QResizeEvent * /*re*/) {}

00073 void FloatingText::setLinePos(int x, int y) {
      bool xIsValid = (x >= restrictPositionMin && x <= restrictPositionMax);
      bool yIsValid = (y >= restrictPositionMin && y <= restrictPositionMax);
      if (xIsValid && yIsValid) { // fine
            setX(x);
            setY(y);
      } else { // something is broken
            kdDebug() << "FloatingText::setLinePos( " << x << " , " << y << " ) "
                  << "- was blocked because at least one value is out of bounds: ["
                  << restrictPositionMin << "..." << restrictPositionMax << "]  "
                  << "origX: " << getX() << ", origY: " << getY()
                  << endl;
            // Let's just leave them at their original values.
      }
}

00089 void FloatingText::setLinePositionRelatively(int newX, int newY, int oldX, int oldY) {
      int myNewX = getX() + (newX-oldX);
      int myNewY = getY() + (newY-oldY);
      bool xIsValid = (myNewX >= restrictPositionMin && myNewX <= restrictPositionMax);
      bool yIsValid = (myNewY >= restrictPositionMin && myNewY <= restrictPositionMax);
      if (xIsValid && yIsValid) { // fine
            bool oldIgnoreSnapToGrid = m_bIgnoreSnapToGrid;
            m_bIgnoreSnapToGrid = true;
            setX(myNewX);
            setY(myNewY);
            m_bIgnoreSnapToGrid = oldIgnoreSnapToGrid;
      } else { // something is broken
            kdDebug() << "FloatingText::setLinePositionRelatively( " << myNewX
                  << " , " << myNewY << " ) - was Blocked because at least one value is out of bounds: ["
                  << restrictPositionMin << "..." << restrictPositionMax << "]\n"
                  << "ToX: " << newX << ", ToY: " << newY
                  << "FromX: " << oldX << ", FromY: " << oldY
                  << "CurrentPointX: " << getX() << ", CurrentPointY: " << getY()
                  << endl;
            // Let's just leave them at their original values.
      }
}

00112 void FloatingText::calculateSize() {
      QFontMetrics &fm = getFontMetrics(FT_NORMAL);
      int h = fm.lineSpacing();
      int w = fm.width( getDisplayText() );
      setSize( w + 8, h + 4 );//give a small margin
}

00119 void FloatingText::slotMenuSelection(int sel) {
      switch(sel) {
      case ListPopupMenu::mt_Properties:
            mouseDoubleClickEvent((QMouseEvent *)0);
            break;

      case ListPopupMenu::mt_Delete_Association:
            // UNEXPECTED - should not be here
            // Remove this branch when confident that it does not happen
            kdError() << "UNEXPECTED: "
                    << "FloatingText::slotMenuSelection(mt_Delete_Association)"
                    << " is called." << endl;
            break;

      case ListPopupMenu::mt_Delete:
            m_pView -> removeWidget(this);
            break;

      case ListPopupMenu::mt_Delete_Message:
            // UNEXPECTED - should not be here
            // Remove this branch when confident that it does not happen
            kdDebug() << "FloatingText::slotMenuSelection(mt_Delete_Message)"
                    << " is called" << endl;
            if (m_pLink == NULL)
                  return;
            if (m_Role == tr_Seq_Message || m_Role == tr_Seq_Message_Self) {
                  //here to delete this from a seq. diagram.
                  kdDebug() << "  --- for Seq_Message." << endl;
                  MessageWidget *m = static_cast<MessageWidget*>(m_pLink);
                  m_pView->removeWidget( m );
            } else {
                  if (m_pView->getType() != dt_Collaboration)
                        return;
                  kdDebug() << "  --- for association." << endl;
                  //here to delete assoc. on collab diagram.
                  AssociationWidget *a = static_cast<AssociationWidget*>(m_pLink);
                  m_pView->removeAssoc( a );
            }
            break;

      case ListPopupMenu::mt_Operation:
            {
                  kdDebug() << "FloatingText::slotMenuSelection(mt_Operation) is called."
                          << endl;
                  if (m_pLink == NULL) {
                        kdDebug() << "FloatingText::slotMenuSelection(mt_Operation): "
                                << "m_pLink is NULL" << endl;
                        return;
                  }
                  UMLClassifier* c = m_pLink->getOperationOwner(this);
                  if (c == NULL) {
                        bool ok = false;
                        QString opText = KInputDialog::getText(i18n("Name"),
                                                       i18n("Enter operation name:"),
                                                       getText(), &ok, m_pView);
                        if (ok)
                              m_pLink->setOperationText(this, opText);
                        return;
                  }
                  UMLObject* umlObj = UMLApp::app()->getDocument()->createChildObject(c, Uml::ot_Operation);
                  if (umlObj) {
                        UMLOperation* newOperation = static_cast<UMLOperation*>( umlObj );
                        // It should perhaps be configurable whether the
                        // operation is displayed with or without its signature.
                        // For the time being, we don't display the signature.
                        QString opText = newOperation->toString(st_NoSigNoScope);
                        m_pLink->setOperationText(this, opText);
                  }
            }
            break;

      case ListPopupMenu::mt_Select_Operation:
            showOpDlg();
            break;

      case ListPopupMenu::mt_Rename:
            handleRename();
            break;

      case ListPopupMenu::mt_Change_Font:
            {
                  QFont font = getFont();
                  if( KFontDialog::getFont( font, false, m_pView ) ) {
                        if( m_Role == tr_Floating || m_Role == tr_Seq_Message ) {
                              setFont( font );
                        } else if (m_pLink) {
                              m_pLink->setFont(font);
                        }
                  }
            }
            break;

      case ListPopupMenu::mt_Reset_Label_Positions:
            if (m_pLink)
                  m_pLink->resetTextPositions();
            break;

      default:
            UMLWidget::slotMenuSelection(sel);
            break;
      }//end switch
}

00222 void FloatingText::handleRename() {
      QString t;
      if( m_Role == tr_RoleAName || m_Role == tr_RoleBName ) {
            t = i18n("Enter role name:");
      } else if (m_Role == tr_MultiA || m_Role == tr_MultiB) {
            t = i18n("Enter multiplicity:");
            /*
            // NO! shouldnt be allowed
            } else if( m_Role == tr_ChangeA || m_Role == tr_ChangeB ) {
            t = i18n("Enter changeability");
            */
      } else if (m_Role == tr_Name) {
            t = i18n("Enter association name:");
      } else if (m_Role == tr_Floating) {
            t = i18n("Enter new text:");
      } else {
            t = i18n("ERROR");
      }
      bool ok = false;
      QString newText = KInputDialog::getText(i18n("Rename"), t, getText(), &ok, m_pView);
      if (!ok)
            return;
      bool valid = isTextValid(newText);
      if (!valid || newText == getText()) {
            if (!valid && m_Role == tr_Floating)
                  m_pView -> removeWidget(this);
            calculateSize();
            update();
            return;
      }
      if (m_pLink && m_Role != tr_Seq_Message && m_Role != tr_Seq_Message_Self) {
            m_pLink->setText(this, newText);
      } else {
            setText( newText );
            UMLApp::app()->getDocument()->setModified(true);
      }
      setVisible( true );
      calculateSize();
      update();
}

00263 void FloatingText::setText(QString t) {
      if (m_Role == tr_Seq_Message || m_Role == tr_Seq_Message_Self) {
            QString seqNum, op;
            m_pLink->getSeqNumAndOp(this, seqNum, op);
            if (seqNum.length() > 0 || op.length() > 0)
                  m_Text = seqNum.append(": ").append( op );
            else
                  m_Text = t;
      } else
            m_Text = t;
      calculateSize();
      update();
}

00277 void FloatingText::setPreText (QString t)
{
      m_PreText = t;
      calculateSize();
      update();
}

00284 void FloatingText::setPostText(QString t) {
      m_PostText = t;
      calculateSize();
      update();
}

00290 void FloatingText::changeTextDlg() {
      bool ok = false;
      QString newText = KInputDialog::getText(i18n("Change Text"), i18n("Enter new text:"), getText(), &ok, m_pView);

      if(ok && newText != getText() && isTextValid(newText)) {
            setText( newText );
            setVisible( ( getText().length() > 0 ) );
            calculateSize();
            update();
      }
      if(!isTextValid(newText))
            hide();
}

00304 void FloatingText::mouseDoubleClickEvent(QMouseEvent * /* me*/) {
      if(m_pView -> getCurrentCursor() != WorkToolBar::tbb_Arrow)
            return;
      if (m_Role == tr_Coll_Message || m_Role == tr_Coll_Message_Self ||
          m_Role == tr_Seq_Message || m_Role == tr_Seq_Message_Self) {
            showOpDlg();
      } else if (m_Role == tr_Floating) {
            // double clicking on a text line opens the dialog to change the text
            handleRename();
      } else if (m_pLink) {
            m_pLink->showDialog();
      }
}

00318 void FloatingText::showOpDlg() {
      if (m_pLink == NULL) {
            kdError() << "FloatingText::showOpDlg: m_pLink is NULL" << endl;
            return;
      }
      QString seqNum, op;
      UMLClassifier* c = m_pLink->getSeqNumAndOp(this, seqNum, op);
      if (c == NULL) {
            kdError() << "FloatingText::showOpDlg: "
                    << "m_pLink->getSeqNumAndOp() returns a NULL classifier"
                    << endl;
            return;
      }

      SelectOpDlg selectDlg((QWidget*)m_pView, c);
      selectDlg.setSeqNumber( seqNum );
      selectDlg.setCustomOp( op );
      int result = selectDlg.exec();
      if(!result) {
            return;
      }
      seqNum = selectDlg.getSeqNumber();
      // It should perhaps be configurable whether the operation
      // is displayed with or without its signature.
      // Here, we just discard the signature.
      op = selectDlg.getOpText();
      op.replace( QRegExp("\\(.*\\)"), "()" );
      m_pLink->setSeqNumAndOp(seqNum, op);
      setMessageText();
}

00349 void FloatingText::mouseMoveEvent(QMouseEvent* me) {
      if (!m_bMouseDown && me->button() != LeftButton)
            return;
      if (m_Role == tr_Seq_Message_Self)
            return;
      QPoint newPosition = doMouseMove(me);
      int newX = newPosition.x();
      int newY = newPosition.y();

      //implement specific rules for a sequence diagram
      if (m_Role == tr_Seq_Message) {
            MessageWidget *pMessage = static_cast<MessageWidget*>(m_pLink);
            const int minHeight = pMessage->getMinHeight();
            newY = newY < minHeight ? minHeight : newY;
            const int maxHeight = pMessage->getMaxHeight() - height() - 5;
            newY = newY < maxHeight ? newY : maxHeight;
            pMessage->setY( newY + height() );
      }
      m_nOldX = newX;
      m_nOldY = newY;
      setX( newX );
      setY( newY );
      if (m_pLink)
            m_pLink->calculateNameTextSegment();
      m_pView->resizeCanvasToItems();
}

00376 QString FloatingText::getPreText() const {
      return m_PreText;
}

00380 QString FloatingText::getPostText() const {
      return m_PostText;
}

00384 QString FloatingText::getText() const {
      //test to make sure not just the ":" between the seq number
      //and the actual message widget
      // hmm. this section looks like it could have been avoided by using pre-, post- text
      // instead of storing in the main body of the text -b.t.
      if(m_Role == tr_Seq_Message || m_Role == tr_Seq_Message_Self ||
         m_Role == tr_Coll_Message || m_Role == tr_Coll_Message_Self) {
            if( m_Text.length() <= 1 || m_Text == ": " )
                  return "";
      }
      return m_Text;
}

00397 QString FloatingText::getDisplayText() const
{
      QString displayText = m_Text;
      displayText.prepend(m_PreText);
      displayText.append(m_PostText);
      return displayText;
}

00405 bool FloatingText::activate( IDChangeLog* ChangeLog /*= 0 */) {
      bool status = UMLWidget::activate( ChangeLog );
      calculateSize();
      update();
      return status;
}

// God this is bad. Why not use the assocList in the
// parent umlwidget class? At the very least, we should
// keep them synced.
// Also, there seems to be some issues with setting id to the association
// XMI id. There CAN be more than one floating text widget with this id
// but a findWidget will come up with only the first one.
00418 void FloatingText::setLink(LinkWidget * l) {
      if (m_pLink)  // remove pre-existing link
            m_pLink->cleanupBeforeFTsetLink(this);
      m_pLink = l;
      if (m_pLink)
            m_pLink->setupAfterFTsetLink(this);
}

00426 LinkWidget * FloatingText::getLink() {
      return m_pLink;
}

00430 void FloatingText::setRole(Text_Role role) {
      m_Role = role;
}

00434 Uml::Text_Role FloatingText::getRole() const {
      return m_Role;
}

00438 bool FloatingText::isTextValid(QString text) {
      int length = text.length();
      if(length < 1)
            return false;
      for(int i=0;i<length;i++)
            if(!text.at(i).isSpace())
                  return true;
      return false;
}

/*
void FloatingText::setSeqNum(QString sn)
{
      m_SeqNum = sn;
      update();
}

QString FloatingText::getSeqNum() const {
      return m_SeqNum;
}

void FloatingText::setOperation(QString op) {
      m_Operation = op;
}

QString FloatingText::getOperation() const {
      return m_Operation;
}
*/

00468 void FloatingText::setSelected(bool _select) {
      if( m_nOldID == -10 )//used to stop a recursive call
      {
            m_nOldID = 0;
            return;
      }
      UMLWidget::setSelected( _select );
}

00477 void FloatingText::saveToXMI( QDomDocument & qDoc, QDomElement & qElement ) {
      QDomElement textElement = qDoc.createElement( "floatingtext" );
      UMLWidget::saveToXMI( qDoc, textElement );
      textElement.setAttribute( "text", m_Text );
      textElement.setAttribute( "pretext", m_PreText );
      textElement.setAttribute( "posttext", m_PostText );

      /* No need to save these - the messagewidget already did it.
      m_Operation  = qElement.attribute( "operation", "" );
      m_SeqNum = qElement.attribute( "seqnum", "" );
       */

      textElement.setAttribute( "role", m_Role );
      qElement.appendChild( textElement );
}

00493 bool FloatingText::loadFromXMI( QDomElement & qElement ) {
      if( !UMLWidget::loadFromXMI( qElement ) )
            return false;

      QString role = qElement.attribute( "role", "" );
      if( !role.isEmpty() )
            m_Role = (Uml::Text_Role)role.toInt();

      m_PreText = qElement.attribute( "pretext", "" );
      m_PostText = qElement.attribute( "posttext", "" );
      m_Text = qElement.attribute( "text", "" );
      // If all texts are empty then this is a useless widget.
      // In that case we return false.
      // CAVEAT: The caller should not interpret the false return value
      //  as an indication of failure since previous umbrello versions
      //  saved lots of these empty FloatingTexts.
      bool isDummy = (m_Text == "" && m_PreText == "" && m_PostText == "");
      return !isDummy;
}

00513 void FloatingText::setMessageText() {
      if (m_pLink)
            m_pLink->setMessageText(this);
      setVisible(getText().length() > 0);
      calculateSize();
}


Generated by  Doxygen 1.6.0   Back to index