Commit efd686cd authored by Joris Steyn's avatar Joris Steyn

Refactor decoding of clip data

Decoding of UMLObjects now uses the same code path on all clips. Fixes that
recently went to clip1 are now also active for clip2 (diagrams). List view
items are never decoded, but created in response of a signal of new UMLObjects.

When pasting clip2 in the same file, only the diagram gets duplicated and not
all contained UMLObjects, like described in feature request 85807.

BUG: 327910
BUG: 327670
CCBUG: 85807
parent 8a176163
......@@ -65,9 +65,8 @@ UMLClipboard::~UMLClipboard()
*/
QMimeData* UMLClipboard::copy(bool fromView/*=false*/)
{
//Clear previous copied data
// Clear previous copied data
m_AssociationList.clear();
m_ItemList.clear();
m_ObjectList.clear();
m_ViewList.clear();
......@@ -103,49 +102,56 @@ QMimeData* UMLClipboard::copy(bool fromView/*=false*/)
m_ObjectList.append(widget->umlObject());
}
}
} else { //if the copy action is being performed from the ListView
} else {
// The copy action is being performed from the ListView
UMLListViewItemList itemsSelected = listView->selectedItems();
if (itemsSelected.count() <= 0) {
return 0;
}
//Set What type of copy operation are we performing and
//also fill m_ViewList with all the selected Diagrams
// Set What type of copy operation are we performing and
// also fill m_ViewList with all the selected Diagrams
setCopyType(itemsSelected);
//if we are copying a diagram or part of a diagram, select the items
//on the ListView that correspond to a UseCase, Actor or Concept
//in the Diagram
// If we are copying a diagram or part of a diagram, select the items
// on the ListView that correspond to a UseCase, Actor or Concept
// in the Diagram
if (m_type == clip2) {
//Fill the member lists with all the object and stuff to be copied
//to the clipboard
itemsSelected.clear();
//For each selected view select all the Actors, USe Cases and Concepts
//widgets in the ListView
foreach (UMLView* view, m_ViewList) {
UMLObjectList objects = view->umlScene()->umlObjects();
foreach (UMLObject* o, objects) {
UMLListViewItem *item = listView->findUMLObject(o);
if (item) {
item->setSelected(true);
UMLWidgetList widgets = view->umlScene()->widgetList();
foreach (UMLWidget* widget, widgets) {
if (widget->umlObject() != 0) {
m_ObjectList.append(widget->umlObject());
}
}
AssociationWidgetList associations = view->umlScene()->associationList();
foreach (AssociationWidget* association, associations) {
if (association->umlObject() != 0) {
m_ObjectList.append(association->umlObject());
}
}
}
itemsSelected = listView->selectedItems();
} else {
// Clip1, 4 and 5: fill the clip with only the specific objects
// selected in the list view
if (!fillSelectionLists(itemsSelected)) {
return 0;
}
if (itemsSelected.count() <= 0) {
return 0;
}
}
if (!fillSelectionLists(itemsSelected)) {
return 0;
}
}
int i = 0;
switch(m_type) {
case clip1:
data = new UMLDragData(m_ObjectList);
break;
case clip2:
data = new UMLDragData(m_ObjectList, m_ItemList, m_ViewList);
data = new UMLDragData(m_ObjectList, m_ViewList);
break;
case clip3:
data = new UMLDragData(m_ItemList);
......@@ -186,6 +192,7 @@ bool UMLClipboard::paste(const QMimeData* data)
bool result = false;
doc->beginPaste();
switch (codingType) {
case 1:
result = pasteClip1(data);
......@@ -226,12 +233,6 @@ bool UMLClipboard::fillSelectionLists(UMLListViewItemList& selectedItems)
if (!Model_Utils::typeIsClassifierList(type)) {
m_ItemList.append(item);
insertItemChildren(item, selectedItems);
//Because it is being called when m_type is 3
//it will insert only child empty folders of other folders.
//If a child folder
//is not empty that means m_type wouldn't be 3 because if a folder is
//selected then its complete contents are treated as if
//they were selected
}
}
break;
......@@ -240,9 +241,6 @@ bool UMLClipboard::fillSelectionLists(UMLListViewItemList& selectedItems)
foreach (UMLListViewItem* item, selectedItems) {
type = item->type();
if (!Model_Utils::typeIsClassifierList(type)) {
m_ItemList.append(item);
if (Model_Utils::typeIsCanvasWidget(type)) {
m_ObjectList.append(item->umlObject());
}
......@@ -254,9 +252,7 @@ bool UMLClipboard::fillSelectionLists(UMLListViewItemList& selectedItems)
foreach (UMLListViewItem* item, selectedItems) {
type = item->type();
if(Model_Utils::typeIsClassifierList(type)) {
m_ItemList.append(item);
m_ObjectList.append(item->umlObject());
} else {
return false;
}
......@@ -330,7 +326,8 @@ void UMLClipboard::checkItemForCopyType(UMLListViewItem* item, bool & withDiagra
}
/**
* Adds the children of a UMLListViewItem to m_ItemList.
* Traverse children of a UMLListViewItem and add its UMLObjects to the list
*
* @param item parent of the children to insert
* @param selectedItems list of selected items
* @return success flag
......@@ -357,58 +354,6 @@ bool UMLClipboard::insertItemChildren(UMLListViewItem * item, UMLListViewItemLis
return true;
}
/**
* Pastes the children of a UMLListViewItem (The Parent)
* @param parent parent of the children
* @param chgLog ID change log
* @return success flag
*/
bool UMLClipboard::pasteChildren(UMLListViewItem *parent, IDChangeLog *chgLog)
{
if (!parent) {
uWarning() << "Paste Children Error, parent missing";
return false;
}
UMLDoc *doc = UMLApp::app()->document();
UMLListView *listView = UMLApp::app()->listView();
for (int i = 0; i < parent->childCount(); i++) {
UMLListViewItem *childItem = static_cast<UMLListViewItem*>(parent->child(i));
Uml::ID::Type oldID = childItem->ID();
Uml::ID::Type newID = chgLog->findNewID(oldID);
UMLListViewItem *shouldNotExist = listView->findItem(newID);
if (shouldNotExist) {
uError() << "new list view item " << Uml::ID::toString(newID)
<< " already exists (internal error)";
continue;
}
UMLObject *newObj = doc->findObjectById(newID);
if (newObj) {
uDebug() << "adjusting lvitem(" << Uml::ID::toString(oldID)
<< ") to new UMLObject(" << Uml::ID::toString(newID) << ")";
childItem->setUMLObject(newObj);
childItem->setText(newObj->name());
} else {
uDebug() << "no UMLObject found for lvitem " << Uml::ID::toString(newID);
}
}
return true;
}
/**
* Cleans the list of associations taking out the ones that point to an object
* not in m_ObjectList.
* @param associations list of associations
*/
void UMLClipboard::CleanAssociations(AssociationWidgetList& associations)
{
AssociationWidgetListIt it(associations);
while (it.hasNext()) {
AssociationWidget* assoc = it.next();
Q_UNUSED(assoc);
}
}
/**
* If clipboard has mime type application/x-uml-clip1,
* Pastes the data from the clipboard into the current Doc.
......@@ -420,23 +365,13 @@ bool UMLClipboard::pasteClip1(const QMimeData* data)
if (! UMLDragData::decodeClip1(data, objects)) {
return false;
}
UMLListView *lv = UMLApp::app()->listView();
if (!lv->startedCopy())
return true;
lv->setStartedCopy(false);
/* If we get here we are pasting after a Copy and need to
// paste possible children.
UMLListViewItem* itemdata = 0;
UMLListViewItemListIt it(itemdatalist);
while ((itemdata=it.current()) != 0) {
if(itemdata->childCount()) {
if(!pasteChildren(itemdata, idchanges)) {
return false;
}
}
++it;
UMLDoc* doc = UMLApp::app()->document();
foreach (UMLObject* object, objects) {
doc->assignNewIDs(object);
object->resolveRef();
}
*/
return true;
}
......@@ -449,27 +384,14 @@ bool UMLClipboard::pasteClip1(const QMimeData* data)
bool UMLClipboard::pasteClip2(const QMimeData* data)
{
UMLDoc* doc = UMLApp::app()->document();
UMLListViewItemList itemdatalist;
UMLObjectList objects;
UMLViewList views;
IDChangeLog* idchanges = 0;
bool result = UMLDragData::decodeClip2(data, objects, itemdatalist, views);
bool result = UMLDragData::decodeClip2(data, objects, views);
if(!result) {
return false;
}
idchanges = doc->changeLog();
if(!idchanges) {
return false;
}
foreach (UMLObject* obj, objects) {
if(!doc->assignNewIDs(obj)) {
uDebug() << "UMLClipboard: error adding umlobject";
return false;
}
}
foreach (UMLView* pView, views) {
if(!doc->addUMLView(pView)) {
return false;
......@@ -482,6 +404,14 @@ bool UMLClipboard::pasteClip2(const QMimeData* data)
/**
* If clipboard has mime type application/x-uml-clip3,
* Pastes the data from the clipboard into the current Doc.
*
* Note: clip3 is only used to determine if the selected items can be dragged
* onto the view. Pasting only listview items makes no sense. Clip3 is implemented
* as a fallback-clip when clip 1, 2, 4 or 5 are not applicable. But that should
* never happen.
*
* Todo: remove clip3 alltogether.
*
* @param data mime type
* @return success flag
*/
......@@ -744,24 +674,6 @@ bool UMLClipboard::pasteClip5(const QMimeData* data)
return result;
}
/**
* Inserts the data of the children of the given item
* into the item data list. Used for clip type 4. Used
* to make * sure classes have all the attributes and
* operations saved.
* @param item parent item, its children are inserted
* @return success flag
*/
bool UMLClipboard::insertItemChildren(UMLListViewItem * item)
{
for (int i = 0; i < item->childCount(); i++) {
UMLListViewItem * child =dynamic_cast<UMLListViewItem *>(item->child(i));
m_ItemList.append(child);
insertItemChildren(child);
}
return true;
}
/**
* When pasting widgets see if they can be pasted on
* different diagram types. Will return true if all the
......
......@@ -55,8 +55,6 @@ public:
private:
void CleanAssociations(AssociationWidgetList& associations);
bool pasteClip1(const QMimeData* data);
bool pasteClip2(const QMimeData* data);
bool pasteClip3(const QMimeData* data);
......@@ -86,10 +84,6 @@ private:
bool insertItemChildren(UMLListViewItem* item,
UMLListViewItemList& selectedItems);
bool insertItemChildren(UMLListViewItem* item);
bool pasteChildren(UMLListViewItem* parent, IDChangeLog *chgLog);
void pasteItemAlreadyExists();
};
......
......@@ -47,11 +47,10 @@ UMLDragData::UMLDragData(UMLObjectList& objects, QWidget* dragSource /* = 0 */)
* from the ListView to be copied, Mime type =
* "application/x-uml-clip2
*/
UMLDragData::UMLDragData(UMLObjectList& objects, UMLListViewItemList& umlListViewItems,
UMLViewList& diagrams, QWidget* dragSource /* = 0 */)
UMLDragData::UMLDragData(UMLObjectList& objects, UMLViewList& diagrams, QWidget* dragSource /* = 0 */)
{
Q_UNUSED(dragSource);
setUMLDataClip2(objects, umlListViewItems, diagrams);
setUMLDataClip2(objects, diagrams);
}
/**
......@@ -125,9 +124,6 @@ void UMLDragData::setUMLDataClip1(UMLObjectList& objects)
obj->saveToXMI(domDoc, objectsTag);
}
QDomElement itemsTag = domDoc.createElement("umllistviewitems");
xmiclip.appendChild(itemsTag);
setData("application/x-uml-clip1", domDoc.toString().toUtf8());
}
......@@ -135,8 +131,7 @@ void UMLDragData::setUMLDataClip1(UMLObjectList& objects)
* For use when the user selects UML Object and Diagrams
* from the ListView to be copied
*/
void UMLDragData::setUMLDataClip2(UMLObjectList& objects, UMLListViewItemList& umlListViewItems,
UMLViewList& diagrams)
void UMLDragData::setUMLDataClip2(UMLObjectList& objects, UMLViewList& diagrams)
{
QDomDocument domDoc;
QDomElement xmiclip = domDoc.createElement("xmiclip");
......@@ -158,13 +153,6 @@ void UMLDragData::setUMLDataClip2(UMLObjectList& objects, UMLListViewItemList& u
view->umlScene()->saveToXMI(domDoc, viewsTag);
}
QDomElement itemsTag = domDoc.createElement("umllistviewitems");
xmiclip.appendChild(itemsTag);
foreach(UMLListViewItem* item, umlListViewItems) {
item->saveToXMI(domDoc, itemsTag);
}
setData("application/x-uml-clip2", domDoc.toString().toUtf8());
}
......@@ -222,9 +210,6 @@ void UMLDragData::setUMLDataClip4(UMLObjectList& objects, UMLWidgetList& widgets
association->saveToXMI(domDoc, associationWidgetsTag);
}
QDomElement itemsTag = domDoc.createElement("umllistviewitems");
xmiclip.appendChild(itemsTag);
setData("application/x-uml-clip4", domDoc.toString().toUtf8());
QImage img = pngImage.toImage();
......@@ -253,9 +238,6 @@ void UMLDragData::setUMLDataClip5(UMLObjectList& objects)
obj->saveToXMI(domDoc, objectsTag);
}
QDomElement itemsTag = domDoc.createElement("umllistviewitems");
xmiclip.appendChild(itemsTag);
setData("application/x-uml-clip5", domDoc.toString().toUtf8());
}
......@@ -267,7 +249,6 @@ void UMLDragData::setUMLDataClip5(UMLObjectList& objects)
*/
bool UMLDragData::decodeClip1(const QMimeData* mimeData, UMLObjectList& objects)
{
UMLDoc* doc = UMLApp::app()->document();
if (!mimeData->hasFormat("application/x-uml-clip1")) {
return false;
}
......@@ -294,95 +275,9 @@ bool UMLDragData::decodeClip1(const QMimeData* mimeData, UMLObjectList& objects)
return false;
}
//UMLObjects
QDomNode objectsNode = xmiClipNode.firstChild();
QDomNode objectElement = objectsNode.firstChild();
QDomElement element = objectElement.toElement();
if (element.isNull()) {
return false;//return ok as it means there is no umlobjects
}
UMLObject* pObject = 0;
while (!element.isNull()) {
pObject = 0;
QString type = element.tagName();
QString stereotype = element.attribute("stereotype");
if (type == "UML:Association") {
objectElement = objectElement.nextSibling();
element = objectElement.toElement();
continue;
}
// Remove ownedElements from containers: the clip already contains all children
// as a flat list (UMLClipboard::insertItemChildren)
if (type == "UML:Package" ||
type == "UML:Class" ||
type == "UML:Interface" ||
type == "UML:Component") {
QDomNodeList list = element.childNodes();
for (int i=(list.length() - 1); i>=0; i--) {
QDomNode child = list.at(i);
QString tagName = child.toElement().tagName();
if (tagName == "UML:Namespace.ownedElement" ||
tagName == "UML:Namespace.contents") {
element.removeChild(child);
}
}
}
pObject = Object_Factory::makeObjectFromXMI(type, stereotype);
if(!pObject) {
uWarning() << "Given wrong type of umlobject to create: " << type;
return false;
}
pObject->setInPaste(true);
// Note: element should not be used after calling loadFromXMI() because
// it can point to an arbitrary child node
QString oldParentId = element.attribute("namespace", "-1");
if(!pObject->loadFromXMI(element)) {
uWarning() << "Failed to load object of type " << type << " from XMI";
delete pObject;
return false;
}
pObject->setInPaste(false);
// Todo: skip "assignNewIDs" when not needed (when doing a move or
// cut-copy). This could also happen in UMLObject::clone().
doc->assignNewIDs(pObject);
// Determine the parent package of the pasted object
UMLPackage* newParent = 0;
if (oldParentId != "-1") {
Uml::ID::Type newParentId = doc->changeLog()->findNewID(
Uml::ID::fromString(oldParentId)
);
if (newParentId != "-1") {
// Find the newly pasted package
newParent = static_cast<UMLPackage*>(doc->findObjectById(newParentId));
} else {
// Package is not in this clip, determine the parent based
// on the selected tree view item
newParent = Model_Utils::treeViewGetPackageFromCurrent();
}
}
if (newParent != 0) {
UMLPackage* oldParent = pObject->umlPackage();
if (oldParent != 0) {
oldParent->removeObject(pObject);
}
pObject->setUMLPackage(newParent);
newParent->addObject(pObject);
}
pObject->resolveRef();
objects.append(pObject);
objectElement = objectElement.nextSibling();
element = objectElement.toElement();
if (!UMLDragData::decodeObjects(objectsNode, objects, false)) {
return false;
}
return true;
......@@ -393,8 +288,7 @@ bool UMLDragData::decodeClip1(const QMimeData* mimeData, UMLObjectList& objects)
* from the ListView to be copied, decodes Mime type =
* "application/x-uml-clip2
*/
bool UMLDragData::decodeClip2(const QMimeData* mimeData, UMLObjectList& objects,
UMLListViewItemList& umlListViewItems, UMLViewList& diagrams)
bool UMLDragData::decodeClip2(const QMimeData* mimeData, UMLObjectList& objects, UMLViewList& diagrams)
{
if (!mimeData->hasFormat("application/x-uml-clip2")) {
return false;
......@@ -422,85 +316,18 @@ bool UMLDragData::decodeClip2(const QMimeData* mimeData, UMLObjectList& objects,
return false;
}
//UMLObjects
// Load UMLObjects
QDomNode objectsNode = xmiClipNode.firstChild();
QDomNode objectElement = objectsNode.firstChild();
QDomElement element = objectElement.toElement();
if (element.isNull()) {
return false;//return ok as it means there is no umlobjects
}
UMLObject* pObject = 0;
while (!element.isNull()) {
pObject = 0;
QString type = element.tagName();
QString stereotype = element.attribute("stereotype");
if (type != "UML:Association") {
pObject = Object_Factory::makeObjectFromXMI(type, stereotype);
if(!pObject) {
uWarning() << "Given wrong type of umlobject to create:" << type;
return false;
}
if(!pObject->loadFromXMI(element)) {
uWarning() << "Failed to load object from XMI.";
return false;
}
objects.append(pObject);
}
objectElement = objectElement.nextSibling();
element = objectElement.toElement();
if (!UMLDragData::decodeObjects(objectsNode, objects, true)) {
return false;
}
//UMLViews (diagrams)
// Load UMLViews (diagrams)
QDomNode umlviewsNode = objectsNode.nextSibling();
QDomNode diagramNode = umlviewsNode.firstChild();
QDomElement diagramElement = diagramNode.toElement();
if (diagramElement.isNull()) {
uWarning() << "No diagrams in XMI clip.";
if (!UMLDragData::decodeViews(umlviewsNode, diagrams)) {
return false;
}
UMLListView *listView = UMLApp::app()->listView();
while (!diagramElement.isNull()) {
QString type = diagramElement.attribute("type", "0");
Uml::DiagramType::Enum dt = Uml::DiagramType::fromInt(type.toInt());
UMLListViewItem *parent = listView->findFolderForDiagram(dt);
if (parent == NULL)
return false;
UMLObject *po = parent->umlObject();
if (po == NULL || po->baseType() != UMLObject::ot_Folder) {
uError() << "Bad parent for view.";
return false;
}
UMLFolder *f = static_cast<UMLFolder*>(po);
UMLView* view = new UMLView(f);
view->umlScene()->loadFromXMI(diagramElement);
diagrams.append(view);
diagramNode = diagramNode.nextSibling();
diagramElement = diagramNode.toElement();
}
//listviewitems
QDomNode listItemNode = umlviewsNode.nextSibling();
QDomNode listItems = listItemNode.firstChild();
QDomElement listItemElement = listItems.toElement();
if (listItemElement.isNull()) {
uWarning() << "No listitems in XMI clip.";
return false;
}
UMLListViewItem *currentItem = (UMLListViewItem*)listView->currentItem();
while (!listItemElement.isNull()) {
UMLListViewItem* itemData;
if (currentItem)
itemData = new UMLListViewItem(currentItem);
else
itemData = new UMLListViewItem(listView);
if (itemData->loadFromXMI(listItemElement))
umlListViewItems.append(itemData);
else
delete itemData;
listItems = listItems.nextSibling();
listItemElement = listItems.toElement();
}
return true;
}
......@@ -568,12 +395,11 @@ bool UMLDragData::getClip3TypeAndID(const QMimeData* mimeData,
}
return true;
}
/**
* For use when the user selects UMLObjects from
* the ListView to be copied, decodes Mime * type =
* "application/x-uml-clip3
*/
/**
- * For use when the user selects UMLObjects from
- * the ListView to be copied, decodes Mime * type =
- * "application/x-uml-clip3
- */
bool UMLDragData::decodeClip3(const QMimeData* mimeData, UMLListViewItemList& umlListViewItems,
const UMLListView* parentListView)
{
......@@ -674,41 +500,16 @@ bool UMLDragData::decodeClip4(const QMimeData* mimeData, UMLObjectList& objects,
}
dType = Uml::DiagramType::fromInt(root.attribute("diagramtype", "0").toInt());
//UMLObjects
QDomNode objectsNode = xmiClipNode.firstChild();
QDomNode objectElement = objectsNode.firstChild();
QDomElement element = objectElement.toElement();
while (!element.isNull()) {
UMLObject* pObject = 0;
QString type = element.tagName();
UMLDoc* doc = UMLApp::app()->document();
Uml::ID::Type elmId = Uml::ID::fromString(element.attribute("xmi.id"));
QString stereotype = element.attribute("stereotype");
pObject = doc->findObjectById(elmId);
if (!pObject) {
pObject = Object_Factory::makeObjectFromXMI(type, stereotype);
if (!pObject) {
uWarning() << "Given wrong type of umlobject to create: " << type;
return false;
}
if (!pObject->loadFromXMI(element)) {
uWarning() << "Failed to load object from XMI.";
return false;
}
// Load UMLObjects and do not fail if there are none in the clip
bool hasObjects = !objectsNode.firstChild().toElement().isNull();
doc->signalUMLObjectCreated(pObject);
}
objects.append(pObject);
objectElement = objectElement.nextSibling();
element = objectElement.toElement();