|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
* copyright (C) 2002-2007 *
|
|
|
|
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
// own header
|
|
|
|
#include "floatingtextwidget.h"
|
|
|
|
|
|
|
|
// system includes
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqevent.h>
|
|
|
|
#include <tqvalidator.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kinputdialog.h>
|
|
|
|
|
|
|
|
// local includes
|
|
|
|
#include "floatingtextwidgetcontroller.h"
|
|
|
|
#include "association.h"
|
|
|
|
#include "umlview.h"
|
|
|
|
#include "umldoc.h"
|
|
|
|
#include "uml.h"
|
|
|
|
#include "classifier.h"
|
|
|
|
#include "listpopupmenu.h"
|
|
|
|
#include "operation.h"
|
|
|
|
#include "model_utils.h"
|
|
|
|
#include "object_factory.h"
|
|
|
|
#include "messagewidget.h"
|
|
|
|
#include "dialogs/assocpropdlg.h"
|
|
|
|
#include "dialogs/selectopdlg.h"
|
|
|
|
|
|
|
|
|
|
|
|
FloatingTextWidget::FloatingTextWidget(UMLView * view, Uml::Text_Role role,
|
|
|
|
const TQString& text, Uml::IDType id)
|
|
|
|
: UMLWidget(view, id, new FloatingTextWidgetController(this))
|
|
|
|
{
|
|
|
|
init();
|
|
|
|
m_Text = text;
|
|
|
|
m_Role = role;
|
|
|
|
if ( ! UMLApp::app()->getDocument()->loading() ) {
|
|
|
|
updateComponentSize();
|
|
|
|
setZ( 10 );//make sure always on top.
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::init() {
|
|
|
|
// initialize loaded/saved (i.e. persistent) data
|
|
|
|
m_PreText = "";
|
|
|
|
m_Text = "";
|
|
|
|
m_PostText = "";
|
|
|
|
m_Role = Uml::tr_Floating;
|
|
|
|
m_Type = Uml::wt_Text;
|
|
|
|
// initialize non-saved (i.e. volatile) data
|
|
|
|
m_pLink = NULL;
|
|
|
|
UMLWidget::m_bResizable = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FloatingTextWidget::~FloatingTextWidget() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::draw(TQPainter & p, int offsetX, int offsetY) {
|
|
|
|
int w = width();
|
|
|
|
int h = height();
|
|
|
|
p.setFont( UMLWidget::getFont() );
|
|
|
|
TQColor textColor(50, 50, 50);
|
|
|
|
p.setPen(textColor);
|
|
|
|
p.drawText( offsetX , offsetY,w,h, TQt::AlignCenter, getDisplayText() );
|
|
|
|
if(m_bSelected)
|
|
|
|
drawSelected(&p, offsetX, offsetY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::resizeEvent(TQResizeEvent * /*re*/) {}
|
|
|
|
|
|
|
|
TQSize FloatingTextWidget::calculateSize() {
|
|
|
|
const TQFontMetrics &fm = getFontMetrics(FT_NORMAL);
|
|
|
|
int h = fm.lineSpacing();
|
|
|
|
int w = fm.width( getDisplayText() );
|
|
|
|
return TQSize(w + 8, h + 4); // give a small margin
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::slotMenuSelection(int sel) {
|
|
|
|
switch(sel) {
|
|
|
|
case ListPopupMenu::mt_Properties:
|
|
|
|
showProperties();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Delete:
|
|
|
|
m_pView -> removeWidget(this);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Operation:
|
|
|
|
{
|
|
|
|
kDebug() << "FloatingTextWidget::slotMenuSelection(mt_Operation) is called."
|
|
|
|
<< endl;
|
|
|
|
if (m_pLink == NULL) {
|
|
|
|
kDebug() << "FloatingTextWidget::slotMenuSelection(mt_Operation): "
|
|
|
|
<< "m_pLink is NULL" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
UMLClassifier* c = m_pLink->getOperationOwner();
|
|
|
|
if (c == NULL) {
|
|
|
|
bool ok = false;
|
|
|
|
TQString opText = KInputDialog::getText(i18n("Name"),
|
|
|
|
i18n("Enter operation name:"),
|
|
|
|
getText(), &ok, m_pView);
|
|
|
|
if (ok)
|
|
|
|
m_pLink->setCustomOpText(opText);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
UMLClassifierListItem* umlObj = Object_Factory::createChildObject(c, Uml::ot_Operation);
|
|
|
|
if (umlObj) {
|
|
|
|
UMLOperation* newOperation = static_cast<UMLOperation*>( umlObj );
|
|
|
|
m_pLink->setOperation(newOperation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Select_Operation:
|
|
|
|
showOpDlg();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Rename:
|
|
|
|
handleRename();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Change_Font:
|
|
|
|
{
|
|
|
|
TQFont font = getFont();
|
|
|
|
if( KFontDialog::getFont( font, false, m_pView ) ) {
|
|
|
|
if( m_Role == Uml::tr_Floating || m_Role == Uml::tr_Seq_Message ) {
|
|
|
|
setFont( font );
|
|
|
|
} else if (m_pLink) {
|
|
|
|
m_pLink->lwSetFont(font);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ListPopupMenu::mt_Reset_Label_Positions:
|
|
|
|
if (m_pLink)
|
|
|
|
m_pLink->resetTextPositions();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
UMLWidget::slotMenuSelection(sel);
|
|
|
|
break;
|
|
|
|
}//end switch
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::handleRename() {
|
|
|
|
TQRegExpValidator v(TQRegExp(".*"), 0);
|
|
|
|
TQString t;
|
|
|
|
if( m_Role == Uml::tr_RoleAName || m_Role == Uml::tr_RoleBName ) {
|
|
|
|
t = i18n("Enter role name:");
|
|
|
|
} else if (m_Role == Uml::tr_MultiA || m_Role == Uml::tr_MultiB) {
|
|
|
|
t = i18n("Enter multiplicity:");
|
|
|
|
/*
|
|
|
|
// NO! shouldn't be allowed
|
|
|
|
} else if( m_Role == Uml::tr_ChangeA || m_Role == Uml::tr_ChangeB ) {
|
|
|
|
t = i18n("Enter changeability");
|
|
|
|
*/
|
|
|
|
} else if (m_Role == Uml::tr_Name) {
|
|
|
|
t = i18n("Enter association name:");
|
|
|
|
} else if (m_Role == Uml::tr_Floating) {
|
|
|
|
t = i18n("Enter new text:");
|
|
|
|
} else {
|
|
|
|
t = i18n("ERROR");
|
|
|
|
}
|
|
|
|
bool ok = false;
|
|
|
|
TQString newText = KInputDialog::getText(i18n("Rename"), t, getText(), &ok,
|
|
|
|
m_pView, NULL, &v);
|
|
|
|
if (!ok || newText == getText())
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::changeName(const TQString& newText)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (m_pLink && !isTextValid(newText)) {
|
|
|
|
AssociationWidget *assoc = dynamic_cast<AssociationWidget*>(m_pLink);
|
|
|
|
if (assoc) {
|
|
|
|
switch (m_Role) {
|
|
|
|
case Uml::tr_MultiA:
|
|
|
|
assoc->setMulti(TQString(), Uml::A);
|
|
|
|
break;
|
|
|
|
case Uml::tr_MultiB:
|
|
|
|
assoc->setMulti(TQString(), Uml::B);
|
|
|
|
break;
|
|
|
|
case Uml::tr_RoleAName:
|
|
|
|
assoc->setRoleName(TQString(), Uml::A);
|
|
|
|
break;
|
|
|
|
case Uml::tr_RoleBName:
|
|
|
|
assoc->setRoleName(TQString(), Uml::B);
|
|
|
|
break;
|
|
|
|
case Uml::tr_ChangeA:
|
|
|
|
assoc->setChangeability(Uml::chg_Changeable, Uml::A);
|
|
|
|
break;
|
|
|
|
case Uml::tr_ChangeB:
|
|
|
|
assoc->setChangeability(Uml::chg_Changeable, Uml::B);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assoc->setName(TQString());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MessageWidget *msg = dynamic_cast<MessageWidget*>(m_pLink);
|
|
|
|
if (msg) {
|
|
|
|
msg->setName(TQString());
|
|
|
|
m_pView->removeWidget(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_pLink && m_Role != Uml::tr_Seq_Message && m_Role != Uml::tr_Seq_Message_Self) {
|
|
|
|
m_pLink->setText(this, newText);
|
|
|
|
} else {
|
|
|
|
setText( newText );
|
|
|
|
UMLApp::app()->getDocument()->setModified(true);
|
|
|
|
}
|
|
|
|
tqsetVisible( true );
|
|
|
|
updateComponentSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setText(const TQString &t) {
|
|
|
|
if (m_Role == Uml::tr_Seq_Message || m_Role == Uml::tr_Seq_Message_Self) {
|
|
|
|
TQString seqNum, op;
|
|
|
|
m_pLink->getSeqNumAndOp(seqNum, op);
|
|
|
|
if (seqNum.length() > 0 || op.length() > 0) {
|
|
|
|
if (! m_pView->getShowOpSig())
|
|
|
|
op.replace( TQRegExp("\\(.*\\)"), "()" );
|
|
|
|
m_Text = seqNum.append(": ").append( op );
|
|
|
|
} else
|
|
|
|
m_Text = t;
|
|
|
|
} else
|
|
|
|
m_Text = t;
|
|
|
|
updateComponentSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setPreText (const TQString &t)
|
|
|
|
{
|
|
|
|
m_PreText = t;
|
|
|
|
updateComponentSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setPostText(const TQString &t) {
|
|
|
|
m_PostText = t;
|
|
|
|
updateComponentSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::changeTextDlg() {
|
|
|
|
bool ok = false;
|
|
|
|
TQString newText = KInputDialog::getText(i18n("Change Text"), i18n("Enter new text:"), getText(), &ok, m_pView);
|
|
|
|
|
|
|
|
if(ok && newText != getText() && isTextValid(newText)) {
|
|
|
|
setText( newText );
|
|
|
|
tqsetVisible( ( getText().length() > 0 ) );
|
|
|
|
updateComponentSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
if(!isTextValid(newText))
|
|
|
|
hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::showOpDlg() {
|
|
|
|
if (m_pLink == NULL) {
|
|
|
|
kError() << "FloatingTextWidget::showOpDlg: m_pLink is NULL" << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
TQString seqNum, opText;
|
|
|
|
UMLClassifier* c = m_pLink->getSeqNumAndOp(seqNum, opText);
|
|
|
|
if (c == NULL) {
|
|
|
|
kError() << "FloatingTextWidget::showOpDlg: "
|
|
|
|
<< "m_pLink->getSeqNumAndOp() returns a NULL classifier"
|
|
|
|
<< endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectOpDlg selectDlg(m_pView, c);
|
|
|
|
selectDlg.setSeqNumber( seqNum );
|
|
|
|
if (m_pLink->getOperation() == NULL) {
|
|
|
|
selectDlg.setCustomOp( opText );
|
|
|
|
} else {
|
|
|
|
selectDlg.setClassOp( opText );
|
|
|
|
}
|
|
|
|
int result = selectDlg.exec();
|
|
|
|
if(!result) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
seqNum = selectDlg.getSeqNumber();
|
|
|
|
opText = selectDlg.getOpText();
|
|
|
|
if (selectDlg.isClassOp()) {
|
|
|
|
Model_Utils::OpDescriptor od;
|
|
|
|
Model_Utils::Parse_tqStatus st = Model_Utils::parseOperation(opText, od, c);
|
|
|
|
if (st == Model_Utils::PS_OK) {
|
|
|
|
UMLClassifierList selfAndAncestors = c->findSuperClassConcepts();
|
|
|
|
selfAndAncestors.prepend(c);
|
|
|
|
UMLOperation *op = NULL;
|
|
|
|
for (UMLClassifier *cl = selfAndAncestors.first(); cl; cl = selfAndAncestors.next()) {
|
|
|
|
op = cl->findOperation(od.m_name, od.m_args);
|
|
|
|
if (op != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (op == NULL) {
|
|
|
|
// The op does not yet exist. Create a new one.
|
|
|
|
UMLObject *o = c->createOperation(od.m_name, NULL, &od.m_args);
|
|
|
|
op = static_cast<UMLOperation*>(o);
|
|
|
|
}
|
|
|
|
if (od.m_pReturnType)
|
|
|
|
op->setType(od.m_pReturnType);
|
|
|
|
m_pLink->setOperation(op);
|
|
|
|
opText = TQString();
|
|
|
|
} else {
|
|
|
|
m_pLink->setOperation(NULL);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_pLink->setOperation(NULL);
|
|
|
|
}
|
|
|
|
m_pLink->setSeqNumAndOp(seqNum, opText);
|
|
|
|
setMessageText();
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString FloatingTextWidget::getPreText() const {
|
|
|
|
return m_PreText;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString FloatingTextWidget::getPostText() const {
|
|
|
|
return m_PostText;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString FloatingTextWidget::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 == Uml::tr_Seq_Message || m_Role == Uml::tr_Seq_Message_Self ||
|
|
|
|
m_Role == Uml::tr_Coll_Message || m_Role == Uml::tr_Coll_Message_Self) {
|
|
|
|
if( m_Text.length() <= 1 || m_Text == ": " )
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return m_Text;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString FloatingTextWidget::getDisplayText() const
|
|
|
|
{
|
|
|
|
TQString displayText = m_Text;
|
|
|
|
displayText.prepend(m_PreText);
|
|
|
|
displayText.append(m_PostText);
|
|
|
|
return displayText;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FloatingTextWidget::activate( IDChangeLog* ChangeLog /*= 0 */) {
|
|
|
|
if (! UMLWidget::activate(ChangeLog))
|
|
|
|
return false;
|
|
|
|
update();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setLink(LinkWidget * l) {
|
|
|
|
m_pLink = l;
|
|
|
|
}
|
|
|
|
|
|
|
|
LinkWidget * FloatingTextWidget::getLink() {
|
|
|
|
return m_pLink;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setRole(Uml::Text_Role role) {
|
|
|
|
m_Role = role;
|
|
|
|
}
|
|
|
|
|
|
|
|
Uml::Text_Role FloatingTextWidget::getRole() const {
|
|
|
|
return m_Role;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FloatingTextWidget::isTextValid( const TQString &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 FloatingTextWidget::showProperties() {
|
|
|
|
if (m_Role == Uml::tr_Coll_Message || m_Role == Uml::tr_Coll_Message_Self ||
|
|
|
|
m_Role == Uml::tr_Seq_Message || m_Role == Uml::tr_Seq_Message_Self) {
|
|
|
|
showOpDlg();
|
|
|
|
} else if (m_Role == Uml::tr_Floating) {
|
|
|
|
// double clicking on a text line opens the dialog to change the text
|
|
|
|
handleRename();
|
|
|
|
} else if (m_pLink) {
|
|
|
|
m_pLink->showDialog();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::saveToXMI( TQDomDocument & qDoc, TQDomElement & qElement ) {
|
|
|
|
TQDomElement 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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FloatingTextWidget::loadFromXMI( TQDomElement & qElement ) {
|
|
|
|
if( !UMLWidget::loadFromXMI( qElement ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
TQString 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.isEmpty() && m_PreText.isEmpty() && m_PostText.isEmpty());
|
|
|
|
return !isDummy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FloatingTextWidget::setMessageText() {
|
|
|
|
if (m_pLink)
|
|
|
|
m_pLink->setMessageText(this);
|
|
|
|
tqsetVisible(getText().length() > 0);
|
|
|
|
updateComponentSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "floatingtextwidget.moc"
|
|
|
|
|