1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3     SPDX-FileCopyrightText: 2002-2021 Umbrello UML Modeller Authors <umbrello-devel@kde.org>
4 */
5 
6 // own header
7 #include "umllistviewitem.h"
8 
9 // app includes
10 #include "debug_utils.h"
11 #include "folder.h"
12 #include "classifier.h"
13 #include "entity.h"
14 #include "template.h"
15 #include "attribute.h"
16 #include "operation.h"
17 #include "instanceattribute.h"
18 #include "entityconstraint.h"
19 #include "umldoc.h"
20 #include "umllistview.h"
21 #include "umlobjectlist.h"
22 #include "umlscene.h"
23 #include "umlview.h"
24 #include "model_utils.h"
25 #include "uniqueid.h"
26 #include "uml.h"
27 #include "cmds.h"
28 
29 // kde includes
30 #include <KLocalizedString>
31 #include <KMessageBox>
32 
33 // qt includes
34 #include <QDrag>
35 #include <QFile>
36 #include <QRegExp>
37 #include <QTextStream>
38 #include <QXmlStreamWriter>
39 
40 // system includes
41 #include <cstdlib>
42 
43 #define DBG_LVI QLatin1String("UMLListViewItem")
44 
DEBUG_REGISTER(UMLListViewItem)45 DEBUG_REGISTER(UMLListViewItem)
46 
47 /**
48  * Sets up an instance.
49  *
50  * @param parent   The parent to this instance.
51  * @param name     The name of this instance.
52  * @param t        The type of this instance.
53  * @param o        The object it represents.
54  */
55 UMLListViewItem::UMLListViewItem(UMLListView * parent, const QString &name,
56                                  ListViewType t, UMLObject* o)
57   : QTreeWidgetItem(parent)
58 {
59     init();
60     if (parent == 0) {
61         DEBUG(DBG_LVI) << "UMLListViewItem constructor called with a null listview parent";
62     }
63     m_type = t;
64     m_object = o;
65     if (o) {
66         m_id = o->id();
67     }
68     setIcon(Icon_Utils::it_Home);
69     setText(name);
70 }
71 
72 /**
73  * Sets up an instance for subsequent loadFromXMI1().
74  *
75  * @param parent   The parent to this instance.
76  */
UMLListViewItem(UMLListView * parent)77 UMLListViewItem::UMLListViewItem(UMLListView * parent)
78   : QTreeWidgetItem(parent)
79 {
80     init();
81     if (parent == 0) {
82         DEBUG(DBG_LVI) << "UMLListViewItem constructor called with a NULL listview parent";
83     }
84 }
85 
86 /**
87  * Sets up an instance for subsequent loadFromXMI1().
88  *
89  * @param parent   The parent to this instance.
90  */
UMLListViewItem(UMLListViewItem * parent)91 UMLListViewItem::UMLListViewItem(UMLListViewItem * parent)
92   : QTreeWidgetItem(parent)
93 {
94     init();
95 }
96 
97 /**
98  * Sets up an instance.
99  *
100  * @param parent   The parent to this instance.
101  * @param name     The name of this instance.
102  * @param t        The type of this instance.
103  * @param o        The object it represents.
104  */
UMLListViewItem(UMLListViewItem * parent,const QString & name,ListViewType t,UMLObject * o)105 UMLListViewItem::UMLListViewItem(UMLListViewItem * parent, const QString &name, ListViewType t, UMLObject *o)
106   : QTreeWidgetItem(parent)
107 {
108     init();
109     m_type = t;
110     m_object = o;
111     if (!o) {
112         m_id = Uml::ID::None;
113         updateFolder();
114     } else {
115         parent->addChildItem(o, this);
116         updateObject();
117         m_id = o->id();
118     }
119     setText(name);
120     if (!Model_Utils::typeIsRootView(t)) {
121         setFlags(flags() | Qt::ItemIsEditable);
122     }
123 }
124 
125 /**
126  * Sets up an instance.
127  *
128  * @param parent   The parent to this instance.
129  * @param name     The name of this instance.
130  * @param t        The type of this instance.
131  * @param id       The id of this instance.
132  */
UMLListViewItem(UMLListViewItem * parent,const QString & name,ListViewType t,Uml::ID::Type id)133 UMLListViewItem::UMLListViewItem(UMLListViewItem * parent, const QString &name, ListViewType t, Uml::ID::Type id)
134   : QTreeWidgetItem(parent)
135 {
136     init();
137     m_type = t;
138     m_id = id;
139     switch (m_type) {
140     case lvt_Collaboration_Diagram:
141         setIcon(Icon_Utils::it_Diagram_Collaboration);
142         break;
143     case lvt_Class_Diagram:
144         setIcon(Icon_Utils::it_Diagram_Class);
145         break;
146     case lvt_State_Diagram:
147         setIcon(Icon_Utils::it_Diagram_State);
148         break;
149     case lvt_Activity_Diagram:
150         setIcon(Icon_Utils::it_Diagram_Activity);
151         break;
152     case lvt_Sequence_Diagram:
153         setIcon(Icon_Utils::it_Diagram_Sequence);
154         break;
155     case lvt_Component_Diagram:
156         setIcon(Icon_Utils::it_Diagram_Component);
157         break;
158     case lvt_Deployment_Diagram:
159         setIcon(Icon_Utils::it_Diagram_Deployment);
160         break;
161     case lvt_UseCase_Diagram:
162         setIcon(Icon_Utils::it_Diagram_Usecase);
163         break;
164     case lvt_Object_Diagram:
165         setIcon(Icon_Utils::it_Diagram_Object);
166         break;
167     default:
168         setIcon(Icon_Utils::it_Diagram);
169     }
170     //  Constructor also used by folder so just make sure we don't need to
171     //  to set pixmap to folder.  doesn't hurt diagrams.
172     updateFolder();
173     setText(name);
174     setFlags(flags() | Qt::ItemIsEditable);
175 }
176 
177 /**
178  * Standard destructor.
179  */
~UMLListViewItem()180 UMLListViewItem::~UMLListViewItem()
181 {
182 }
183 
184 /**
185  * Initializes key variables of the class.
186  */
init()187 void UMLListViewItem::init()
188 {
189     m_type = lvt_Unknown;
190     m_object = 0;
191     m_id = Uml::ID::None;
192 }
193 
194 /**
195  * Returns the signature of items that are operations.
196  * @return signature of an operation item, else an empty string
197  */
toolTip() const198 QString UMLListViewItem::toolTip() const
199 {
200     UMLObject *obj = umlObject();
201     if (obj) {
202         switch (obj->baseType()) {
203             case UMLObject::ot_Class:
204                 return obj->doc();
205             case UMLObject::ot_Operation:
206             {
207                 UMLOperation *op = obj->asUMLOperation();
208                 return op->toString(Uml::SignatureType::ShowSig);
209             }
210             case UMLObject::ot_Attribute:
211             {
212                 UMLAttribute *at = obj->asUMLAttribute();
213                 return at->toString(Uml::SignatureType::ShowSig);
214             }
215             default:
216                 return QString();
217         }
218     }
219     else {
220         return QString();
221     }
222 }
223 
224 /**
225  * Returns the type this instance represents.
226  *
227  * @return  The type this instance represents.
228  */
type() const229 UMLListViewItem::ListViewType UMLListViewItem::type() const
230 {
231     return m_type;
232 }
233 
234 /**
235  * Adds the child listview item representing the given UMLObject.
236  */
addChildItem(UMLObject * child,UMLListViewItem * childItem)237 void UMLListViewItem::addChildItem(UMLObject *child, UMLListViewItem *childItem)
238 {
239     if (!child) {
240         uError() << "UMLListViewItem::addChildItem called with null child";
241         return;
242     }
243     m_comap[child] = childItem;
244 }
245 
246 /**
247  * Deletes the child listview item representing the given UMLObject.
248  */
deleteChildItem(UMLObject * child)249 void UMLListViewItem::deleteChildItem(UMLObject *child)
250 {
251     if (!child) {
252         uError() << "UMLListViewItem::deleteChildItem called with null child";
253         return;
254     }
255     UMLListViewItem *childItem = findChildObject(child);
256     if (childItem == 0) {
257         uError() << child->name() << ": child listview item not found";
258         return;
259     }
260     m_comap.remove(child);
261     delete childItem;
262 }
263 
setVisible(bool state)264 void UMLListViewItem::setVisible(bool state)
265 {
266     setHidden(!state);
267 }
268 
269 /**
270  * Returns the id this class represents.
271  *
272  * @return  The id this class represents.
273  */
ID() const274 Uml::ID::Type UMLListViewItem::ID() const
275 {
276     if (m_object) {
277         return m_object->id();
278     }
279     return m_id;
280 }
281 
282 /**
283  * Sets the id this class represents.
284  * This only sets the ID locally, not at the UMLObject that is perhaps
285  * associated to this UMLListViewItem.
286  * @param id   the id this class represents
287  */
setID(Uml::ID::Type id)288 void UMLListViewItem::setID(Uml::ID::Type id)
289 {
290     if (m_object) {
291         Uml::ID::Type oid = m_object->id();
292         if (id != Uml::ID::None && oid != id) {
293             DEBUG(DBG_LVI) << "new id " << Uml::ID::toString(id) << " does not agree with object id "
294                 << Uml::ID::toString(oid);
295         }
296     }
297     m_id = id;
298 }
299 
300 /**
301  * Set the UMLObject associated with this instance.
302  *
303  * @param obj  The object this class represents.
304  */
setUMLObject(UMLObject * obj)305 void UMLListViewItem::setUMLObject(UMLObject * obj)
306 {
307     m_object = obj;
308 }
309 
310 /**
311  * Return the UMLObject associated with this instance.
312  *
313  * @return  The object this class represents.
314  */
umlObject() const315 UMLObject * UMLListViewItem::umlObject() const
316 {
317     return m_object;
318 }
319 
320 /**
321  * Returns true if the UMLListViewItem of the given ID is a parent of
322  * this UMLListViewItem.
323  */
isOwnParent(Uml::ID::Type listViewItemID)324 bool UMLListViewItem::isOwnParent(Uml::ID::Type listViewItemID)
325 {
326     UMLListView* listView = static_cast<UMLListView*>(treeWidget());
327     QTreeWidgetItem *lvi = static_cast<QTreeWidgetItem*>(listView->findItem(listViewItemID));
328     if (lvi == 0) {
329         uError() << "ListView->findItem(" << Uml::ID::toString(listViewItemID) << ") returns NULL";
330         return true;
331     }
332     for (QTreeWidgetItem *self = static_cast<QTreeWidgetItem*>(this); self; self = self->parent()) {
333         if (lvi == self)
334             return true;
335     }
336     return false;
337 }
338 
339 /**
340  * Updates the representation of the object.
341  */
updateObject()342 void UMLListViewItem::updateObject()
343 {
344     if (m_object == 0)
345         return;
346 
347     // check if parent has been changed, remap parent if so
348     UMLListViewItem *oldParent = dynamic_cast<UMLListViewItem*>(parent());
349     if (oldParent && oldParent->m_object != m_object->parent() &&
350                                 dynamic_cast<UMLPackage*>(m_object->parent())) {
351         UMLListViewItem *newParent = UMLApp::app()->listView()->findUMLObject(m_object->umlPackage());
352         if (newParent) {
353             oldParent->removeChild(this);
354             newParent->addChild(this);
355         }
356     }
357 
358     Uml::Visibility::Enum scope = m_object->visibility();
359     UMLObject::ObjectType ot = m_object->baseType();
360     QString modelObjText = m_object->name();
361     if (Model_Utils::isClassifierListitem(ot)) {
362         UMLClassifierListItem *pNarrowed = m_object->asUMLClassifierListItem();
363         modelObjText = pNarrowed->toString(Uml::SignatureType::SigNoVis);
364     } else if (ot == UMLObject::ot_InstanceAttribute) {
365         UMLInstanceAttribute *pNarrowed = m_object->asUMLInstanceAttribute();
366         modelObjText = pNarrowed->toString();
367     }
368     setText(modelObjText);
369 
370     Icon_Utils::IconType icon = Icon_Utils::it_Home;
371     switch (ot) {
372     case UMLObject::ot_Package:
373         if (m_object->stereotype() == QLatin1String("subsystem"))
374             icon = Icon_Utils::it_Subsystem;
375         else
376             icon = Icon_Utils::it_Package;
377         break;
378     case UMLObject::ot_Operation:
379         if (scope == Uml::Visibility::Public)
380             icon = Icon_Utils::it_Public_Method;
381         else if (scope == Uml::Visibility::Private)
382             icon = Icon_Utils::it_Private_Method;
383         else if (scope == Uml::Visibility::Implementation)
384             icon = Icon_Utils::it_Private_Method;
385         else
386             icon = Icon_Utils::it_Protected_Method;
387         break;
388 
389     case UMLObject::ot_Attribute:
390     case UMLObject::ot_EntityAttribute:
391     case UMLObject::ot_InstanceAttribute:
392         if (scope == Uml::Visibility::Public)
393             icon = Icon_Utils::it_Public_Attribute;
394         else if (scope == Uml::Visibility::Private)
395             icon = Icon_Utils::it_Private_Attribute;
396         else if (scope == Uml::Visibility::Implementation)
397             icon = Icon_Utils::it_Private_Attribute;
398         else
399             icon = Icon_Utils::it_Protected_Attribute;
400         break;
401     case UMLObject::ot_UniqueConstraint:
402         m_type = Model_Utils::convert_OT_LVT(umlObject());
403         icon = Model_Utils::convert_LVT_IT(m_type);
404         break;
405 
406     case UMLObject::ot_Class:
407         icon = Model_Utils::convert_LVT_IT(m_type, m_object);
408         break;
409 
410     case UMLObject::ot_Association:
411         icon = Model_Utils::convert_LVT_IT(m_type, m_object);
412         break;
413 
414     default:
415         icon = Model_Utils::convert_LVT_IT(m_type);
416         break;
417     }//end switch
418     if (icon)
419         setIcon(icon);
420 }
421 
422 /**
423  * Updates the icon on a folder.
424  */
updateFolder()425 void UMLListViewItem::updateFolder()
426 {
427     Icon_Utils::IconType icon = Model_Utils::convert_LVT_IT(m_type, m_object);
428 
429     if (icon) {
430         if (Model_Utils::typeIsFolder(m_type)) {
431             icon = (Icon_Utils::IconType)((int)icon + (int)isExpanded());
432         }
433         setIcon(icon);
434     }
435 }
436 
437 /**
438  * Overrides default method.
439  * Will call default method but also makes sure correct icon is shown.
440  */
setOpen(bool expand)441 void UMLListViewItem::setOpen(bool expand)
442 {
443     QTreeWidgetItem::setExpanded(expand);
444     updateFolder();
445 }
446 
447 /**
448  * Changes the current text of column 0.
449  */
setText(const QString & newText)450 void UMLListViewItem::setText(const QString &newText)
451 {
452     setText(0, newText);
453 }
454 
455 /**
456  * Changes the current text.
457  */
setText(int column,const QString & newText)458 void UMLListViewItem::setText(int column, const QString &newText)
459 {
460     m_label = newText;
461     QTreeWidgetItem::setText(column, newText);
462 }
463 
464 /**
465  * Returns the saved text.
466  */
getSavedText() const467 QString UMLListViewItem::getSavedText() const
468 {
469     return m_label;
470 }
471 
472 /**
473  * Set the pixmap corresponding to the given IconType.
474  */
setIcon(Icon_Utils::IconType iconType)475 void UMLListViewItem::setIcon(Icon_Utils::IconType iconType)
476 {
477     QPixmap p = Icon_Utils::SmallIcon(iconType);
478     if (!p.isNull()) {
479         QTreeWidgetItem::setIcon(0, QIcon(p));
480     }
481 }
482 
483 /**
484  * This slot is called to finish item editing
485  */
slotEditFinished(const QString & newText)486 void UMLListViewItem::slotEditFinished(const QString &newText)
487 {
488     m_label = text(0);
489 
490     DEBUG(DBG_LVI) << this << "text=" << newText;
491     UMLListView* listView = static_cast<UMLListView*>(treeWidget());
492     UMLDoc* doc = listView->document();
493     if (newText == m_label) {
494         return;
495     }
496     if (newText.isEmpty()) {
497         cancelRenameWithMsg();
498         return;
499     }
500     switch (m_type) {
501     case lvt_UseCase:
502     case lvt_Actor:
503     case lvt_Class:
504     case lvt_Package:
505     case lvt_UseCase_Folder:
506     case lvt_Logical_Folder:
507     case lvt_Component_Folder:
508     case lvt_Deployment_Folder:
509     case lvt_EntityRelationship_Folder:
510     case lvt_Interface:
511     case lvt_Datatype:
512     case lvt_Enum:
513     case lvt_EnumLiteral:
514     case lvt_Subsystem:
515     case lvt_Component:
516     case lvt_Port:
517     case lvt_Node:
518     case lvt_Category:
519         if (m_object == 0 || !doc->isUnique(newText)) {
520             cancelRenameWithMsg();
521             return;
522         }
523         UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(m_object, newText));
524         doc->setModified(true);
525         m_label = newText;
526         break;
527 
528     case lvt_Operation: {
529         if (m_object == 0) {
530             cancelRenameWithMsg();
531             return;
532         }
533         UMLOperation *op = m_object->asUMLOperation();
534         if (!op) {
535             cancelRenameWithMsg();
536             return;
537         }
538         UMLClassifier *parent = op->umlParent()->asUMLClassifier();
539         Model_Utils::OpDescriptor od;
540         Model_Utils::Parse_Status st = Model_Utils::parseOperation(newText, od, parent);
541         if (st == Model_Utils::PS_OK) {
542             // TODO: Check that no operation with the exact same profile exists.
543             UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(op, od.m_name));
544             op->setType(od.m_pReturnType);
545             UMLAttributeList parmList = op->getParmList();
546             const int newParmListCount = parmList.count();
547             if (newParmListCount > od.m_args.count()) {
548                 // Remove parameters at end of of list that no longer exist.
549                 for (int i = od.m_args.count(); i < newParmListCount; i++) {
550                     UMLAttribute *a = parmList.at(i);
551                     op->removeParm(a, false);
552                 }
553             }
554             Model_Utils::NameAndType_ListIt lit = od.m_args.begin();
555             for (int i = 0; lit != od.m_args.end(); ++lit, ++i) {
556                 const Model_Utils::NameAndType& nm_tp = *lit;
557                 UMLAttribute *a;
558                 if (i < newParmListCount) {
559                     a = parmList.at(i);
560                 } else {
561                     a = new UMLAttribute(op);
562                     a->setID(UniqueID::gen());
563                 }
564                 UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(a, nm_tp.m_name));
565                 a->setType(nm_tp.m_type);
566                 a->setParmKind(nm_tp.m_direction);
567                 a->setInitialValue(nm_tp.m_initialValue);
568                 if (i >= newParmListCount) {
569                     op->addParm(a);
570                 }
571             }
572             m_label = op->toString(Uml::SignatureType::SigNoVis);
573         } else {
574             KMessageBox::error(0,
575                                Model_Utils::psText(st),
576                                i18n("Rename canceled"));
577         }
578         setText(m_label);
579         break;
580     }
581 
582     case lvt_Attribute:
583     case lvt_EntityAttribute:
584     case lvt_InstanceAttribute: {
585         if (m_object == 0) {
586             cancelRenameWithMsg();
587             return;
588         }
589         UMLClassifier *parent = m_object->umlParent()->asUMLClassifier();
590         Model_Utils::NameAndType nt;
591         Uml::Visibility::Enum vis;
592         Model_Utils::Parse_Status st;
593         st = Model_Utils::parseAttribute(newText, nt, parent, &vis);
594         if (st == Model_Utils::PS_OK) {
595             UMLObject *exists = parent ? parent->findChildObject(newText) : 0;
596             if (exists) {
597                 cancelRenameWithMsg();
598                 return;
599             }
600             UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(m_object, nt.m_name));
601             UMLAttribute *pAtt = m_object->asUMLAttribute();
602             pAtt->setType(nt.m_type);
603             pAtt->setVisibility(vis);
604             pAtt->setParmKind(nt.m_direction);
605             pAtt->setInitialValue(nt.m_initialValue);
606             m_label = pAtt->toString(Uml::SignatureType::SigNoVis);
607         } else {
608             KMessageBox::error(0,
609                                Model_Utils::psText(st),
610                                i18n("Rename canceled"));
611         }
612         setText(m_label);
613         break;
614     }
615 
616     case lvt_PrimaryKeyConstraint:
617     case lvt_UniqueConstraint:
618     case lvt_ForeignKeyConstraint:
619     case lvt_CheckConstraint: {
620         if (m_object == 0) {
621             cancelRenameWithMsg();
622             return;
623         }
624         UMLEntity *parent = m_object->umlParent()->asUMLEntity();
625         QString name;
626         Model_Utils::Parse_Status st;
627         st = Model_Utils::parseConstraint(newText, name,  parent);
628         if (st == Model_Utils::PS_OK) {
629             UMLObject *exists = parent->findChildObject(name);
630             if (exists) {
631                 cancelRenameWithMsg();
632                 return;
633             }
634             UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(m_object, name));
635 
636             UMLEntityConstraint* uec = m_object->asUMLEntityConstraint();
637             m_label = uec->toString(Uml::SignatureType::SigNoVis);
638         } else {
639             KMessageBox::error(0,
640                                Model_Utils::psText(st),
641                                i18n("Rename canceled"));
642         }
643         setText(m_label);
644         break;
645     }
646 
647     case lvt_Template: {
648         if (m_object == 0) {
649             cancelRenameWithMsg();
650             return;
651         }
652         UMLClassifier *parent = m_object->umlParent()->asUMLClassifier();
653         Model_Utils::NameAndType nt;
654         Model_Utils::Parse_Status st = Model_Utils::parseTemplate(newText, nt, parent);
655         if (st == Model_Utils::PS_OK) {
656             UMLObject *exists = parent ? parent->findChildObject(newText) : 0;
657             if (exists) {
658                 cancelRenameWithMsg();
659                 return;
660             }
661             UMLApp::app()->executeCommand(new Uml::CmdRenameUMLObject(m_object, nt.m_name));
662             UMLTemplate *tmpl = m_object->asUMLTemplate();
663             tmpl->setType(nt.m_type);
664             m_label = tmpl->toString(Uml::SignatureType::SigNoVis);
665         } else {
666             KMessageBox::error(0,
667                                Model_Utils::psText(st),
668                                i18n("Rename canceled"));
669         }
670         setText(m_label);
671         break;
672     }
673 
674     case lvt_UseCase_Diagram:
675     case lvt_Class_Diagram:
676     case lvt_Sequence_Diagram:
677     case lvt_Collaboration_Diagram:
678     case lvt_State_Diagram:
679     case lvt_Activity_Diagram:
680     case lvt_Component_Diagram:
681     case lvt_Deployment_Diagram:
682     case lvt_Object_Diagram:{
683         UMLView *view = doc->findView(ID());
684         if (view == 0) {
685             cancelRenameWithMsg();
686             return;
687         }
688         UMLView *anotherView = doc->findView(view->umlScene()->type(), newText);
689         if (anotherView && anotherView->umlScene()->ID() == ID()) {
690             anotherView = 0;
691         }
692         if (anotherView) {
693             cancelRenameWithMsg();
694             return;
695         }
696         view->umlScene()->setName(newText);
697         setText(newText);
698         doc->signalDiagramRenamed(view);
699         break;
700     }
701     default:
702         KMessageBox::error(0,
703                            i18n("Renaming an item of listview type %1 is not yet implemented.", m_type),
704                            i18n("Function Not Implemented"));
705         setText(m_label);
706         break;
707     }
708     doc->setModified(true);
709 }
710 
711 /**
712  * Auxiliary method for okRename().
713  */
cancelRenameWithMsg()714 void UMLListViewItem::cancelRenameWithMsg()
715 {
716     DEBUG(DBG_LVI) << this << " - column=" << ":TODO:col" << ", text=" << text(0);
717     KMessageBox::error(0,
718                        i18n("The name you entered was invalid.\nRenaming process has been canceled."),
719                        i18n("Name Not Valid"));
720     setText(m_label);
721 }
722 
723 /**
724  * Overrides the default sorting to sort by item type.
725  * Sort the listview items by type and position within the corresponding list
726  * of UMLObjects. If the item does not have a UMLObject then place it last.
727  */
728 #if 0
729 int UMLListViewItem::compare(QTreeWidgetItem *other, int col, bool ascending) const
730 {
731     UMLListViewItem *ulvi = other->asUMLListViewItem();
732     ListViewType ourType = type();
733     ListViewType otherType = ulvi->type();
734 
735     if (ourType < otherType)
736         return -1;
737     if (ourType > otherType)
738         return 1;
739     // ourType == otherType
740     const bool subItem = Model_Utils::typeIsClassifierList(ourType);
741     const int alphaOrder = key(col, ascending).compare(other->key(col, ascending));
742     int retval = 0;
743     QString dbgPfx = "compare(type=" + QString::number((int)ourType)
744                      + ", self=" + text() + ", other=" + ulvi->text()
745                      + "): return ";
746     UMLObject *otherObj = ulvi->umlObject();
747     if (m_object == 0) {
748         retval = (subItem ? 1 : alphaOrder);
749 #ifdef DEBUG_LVITEM_INSERTION_ORDER
750         DEBUG(DBG_LVI) << dbgPfx << retval << " because (m_object==0)";
751 #endif
752         return retval;
753     }
754     if (otherObj == 0) {
755         retval = (subItem ? -1 : alphaOrder);
756 #ifdef DEBUG_LVITEM_INSERTION_ORDER
757         DEBUG(DBG_LVI) << dbgPfx << retval << " because (otherObj==0)";
758 #endif
759         return retval;
760     }
761     UMLClassifier *ourParent = m_object->umlParent()->asUMLClassifier();
762     UMLClassifier *otherParent = otherObj->umlParent()->asUMLClassifier();
763     if (ourParent == 0) {
764         retval = (subItem ? 1 : alphaOrder);
765 #ifdef DEBUG_LVITEM_INSERTION_ORDER
766         DEBUG(DBG_LVI) << dbgPfx << retval << " because (ourParent==0)";
767 #endif
768         return retval;
769     }
770     if (otherParent == 0) {
771         retval = (subItem ? -1 : alphaOrder);
772 #ifdef DEBUG_LVITEM_INSERTION_ORDER
773         DEBUG(DBG_LVI) << dbgPfx << retval << " because (otherParent==0)";
774 #endif
775         return retval;
776     }
777     if (ourParent != otherParent) {
778         retval = (subItem ? 0 : alphaOrder);
779 #ifdef DEBUG_LVITEM_INSERTION_ORDER
780         DEBUG(DBG_LVI) << dbgPfx << retval << " because (ourParent != otherParent)";
781 #endif
782         return retval;
783     }
784     UMLClassifierListItem *thisUmlItem = m_object->asUMLClassifierListItem();
785     UMLClassifierListItem *otherUmlItem = otherObj->asUMLClassifierListItem();
786     if (thisUmlItem == 0) {
787         retval = (subItem ? 1 : alphaOrder);
788 #ifdef DEBUG_LVITEM_INSERTION_ORDER
789         DEBUG(DBG_LVI) << dbgPfx << retval << " because (thisUmlItem==0)";
790 #endif
791         return retval;
792     }
793     if (otherUmlItem == 0) {
794         retval = (subItem ? -1 : alphaOrder);
795 #ifdef DEBUG_LVITEM_INSERTION_ORDER
796         DEBUG(DBG_LVI) << dbgPfx << retval << " because (otherUmlItem==0)";
797 #endif
798         return retval;
799     }
800     UMLClassifierListItemList items = ourParent->getFilteredList(thisUmlItem->baseType());
801     int myIndex = items.indexOf(thisUmlItem);
802     int otherIndex = items.indexOf(otherUmlItem);
803     if (myIndex < 0) {
804         retval = (subItem ? -1 : alphaOrder);
805         uError() << dbgPfx << retval << " because (myIndex < 0)";
806         return retval;
807     }
808     if (otherIndex < 0) {
809         retval = (subItem ? 1 : alphaOrder);
810         uError() << dbgPfx << retval << " because (otherIndex < 0)";
811         return retval;
812     }
813     return (myIndex < otherIndex ? -1 : myIndex > otherIndex ? 1 : 0);
814 }
815 #endif
816 
817 /**
818  * Create a deep copy of this UMLListViewItem, but using the
819  * given parent instead of the parent of this UMLListViewItem.
820  * Return the new UMLListViewItem created.
821  */
deepCopy(UMLListViewItem * newParent)822 UMLListViewItem* UMLListViewItem::deepCopy(UMLListViewItem *newParent)
823 {
824     QString nm = text(0);
825     ListViewType t = type();
826     UMLObject *o = umlObject();
827     UMLListViewItem* newItem;
828     if (o)
829         newItem = new UMLListViewItem(newParent, nm, t, o);
830     else
831         newItem = new UMLListViewItem(newParent, nm, t, m_id);
832     for (int i=0; i < childCount(); i++) {
833         UMLListViewItem *childItem = static_cast<UMLListViewItem*>(child(i));
834         childItem->deepCopy(newItem);
835     }
836     return newItem;
837 }
838 
839 /**
840  * Find the UMLListViewItem that is related to the given UMLObject
841  * in the tree rooted at the current UMLListViewItem.
842  * Return a pointer to the item or NULL if not found.
843  */
findUMLObject(const UMLObject * o)844 UMLListViewItem* UMLListViewItem::findUMLObject(const UMLObject *o)
845 {
846     if (m_object == o)
847         return this;
848     for (int i = 0; i < childCount(); i++) {
849         UMLListViewItem *item = static_cast<UMLListViewItem*>(child(i));
850         UMLListViewItem *testItem = item->findUMLObject(o);
851         if (testItem)
852             return testItem;
853     }
854     return 0;
855 }
856 
857 /**
858  * Find the UMLListViewItem that represents the given UMLObject in the
859  * children of the current UMLListViewItem.
860  * Return a pointer to the item or NULL if not found.
861  */
findChildObject(UMLObject * child)862 UMLListViewItem* UMLListViewItem::findChildObject(UMLObject *child)
863 {
864     ChildObjectMap::iterator it = m_comap.find(child);
865     if (it != m_comap.end()) {
866         return *it;
867     }
868     return 0;
869 }
870 
871 /**
872  * Find the UMLListViewItem of the given ID in the tree rooted at
873  * the current UMLListViewItem.
874  * Return a pointer to the item or NULL if not found.
875  *
876  * @param id   The ID to search for.
877  * @return The item with the given ID or NULL if not found.
878  */
findItem(Uml::ID::Type id)879 UMLListViewItem * UMLListViewItem::findItem(Uml::ID::Type id)
880 {
881     if (ID() == id) {
882         return this;
883     }
884     for (int i = 0; i < childCount(); ++i) {
885         UMLListViewItem *childItem = static_cast<UMLListViewItem*>(child(i));
886         UMLListViewItem *inner = childItem->findItem(id);
887         if (inner) {
888             return inner;
889         }
890     }
891     return 0;
892 }
893 
894 /**
895  * Saves the listview item to a "listitem" tag.
896  */
saveToXMI1(QXmlStreamWriter & writer)897 void UMLListViewItem::saveToXMI1(QXmlStreamWriter& writer)
898 {
899     writer.writeStartElement(QLatin1String("listitem"));
900     Uml::ID::Type id = ID();
901     QString idStr = Uml::ID::toString(id);
902     //DEBUG(DBG_LVI) << "id = " << idStr << ", type = " << m_type;
903     if (id != Uml::ID::None)
904         writer.writeAttribute(QLatin1String("id"), idStr);
905     writer.writeAttribute(QLatin1String("type"), QString::number(m_type));
906     if (m_object == 0) {
907         if (! Model_Utils::typeIsDiagram(m_type) && m_type != lvt_View)
908             uError() << text(0) << ": m_object is NULL";
909         if (m_type != lvt_View)
910             writer.writeAttribute(QLatin1String("label"), text(0));
911     } else if (m_object->id() == Uml::ID::None) {
912         if (text(0).isEmpty()) {
913             DEBUG(DBG_LVI) << "Skipping empty item";
914             return;
915         }
916         DEBUG(DBG_LVI) << "saving local label " << text(0) << " because umlobject ID is not set";
917         if (m_type != lvt_View)
918             writer.writeAttribute(QLatin1String("label"), text(0));
919     } else if (m_object->baseType() == UMLObject::ot_Folder) {
920         UMLFolder *extFolder = m_object->asUMLFolder();
921         if (!extFolder->folderFile().isEmpty()) {
922             writer.writeAttribute(QLatin1String("open"), QLatin1String("0"));
923             writer.writeEndElement();
924             return;
925         }
926     }
927     writer.writeAttribute(QLatin1String("open"), QString::number(isExpanded()));
928     for (int i = 0; i < childCount(); i++) {
929         UMLListViewItem *childItem = static_cast<UMLListViewItem*>(child(i));
930         childItem->saveToXMI1(writer);
931     }
932     writer.writeEndElement();
933 }
934 
935 /**
936  * Loads a "listitem" tag, this is only used by the clipboard currently.
937  */
loadFromXMI1(QDomElement & qElement)938 bool UMLListViewItem::loadFromXMI1(QDomElement& qElement)
939 {
940     QString id = qElement.attribute(QLatin1String("id"), QLatin1String("-1"));
941     QString type = qElement.attribute(QLatin1String("type"), QLatin1String("-1"));
942     QString label = qElement.attribute(QLatin1String("label"));
943     QString open = qElement.attribute(QLatin1String("open"), QLatin1String("1"));
944     if (!label.isEmpty())
945         setText(label);
946     else if (id == QLatin1String("-1")) {
947         uError() << "Item of type " << type << " has neither ID nor label";
948         return false;
949     }
950 
951     m_id = Uml::ID::fromString(id);
952     if (m_id != Uml::ID::None) {
953         UMLListView* listView = static_cast<UMLListView*>(treeWidget());
954         m_object = listView->document()->findObjectById(m_id);
955     }
956     m_type = (ListViewType)(type.toInt());
957     if (m_object)
958         updateObject();
959     setOpen((bool)open.toInt());
960     return true;
961 }
962 
childItem(int i)963 UMLListViewItem* UMLListViewItem::childItem(int i)
964 {
965     return static_cast<UMLListViewItem*>(child(i));
966 }
967 
toString(ListViewType type)968 QString UMLListViewItem::toString(ListViewType type)
969 {
970     switch (type) {
971         case lvt_View:
972             return QLatin1String("lvt_View");
973         case lvt_Logical_View:
974             return QLatin1String("lvt_Logical_View");
975         case lvt_UseCase_View:
976             return QLatin1String("lvt_UseCase_View");
977         case lvt_Logical_Folder:
978             return QLatin1String("lvt_Logical_Folder");
979         case lvt_UseCase_Folder:
980             return QLatin1String("lvt_UseCase_Folder");
981         case lvt_UseCase_Diagram:
982             return QLatin1String("lvt_UseCase_Diagram");
983         case lvt_Collaboration_Diagram:
984             return QLatin1String("lvt_Collaboration_Diagram");
985         case lvt_Class_Diagram:
986             return QLatin1String("lvt_Class_Diagram");
987         case lvt_State_Diagram:
988             return QLatin1String("lvt_State_Diagram");
989         case lvt_Activity_Diagram:
990             return QLatin1String("lvt_Activity_Diagram");
991         case lvt_Sequence_Diagram:
992             return QLatin1String("lvt_Sequence_Diagram");
993         case lvt_Object_Diagram:
994             return QLatin1String("lvt_Object_Diagram");
995         case lvt_Actor:
996             return QLatin1String("lvt_Actor");
997         case lvt_Association:
998             return QLatin1String("lvt_Association");
999         case lvt_UseCase:
1000             return QLatin1String("lvt_UseCase");
1001         case lvt_Class:
1002             return QLatin1String("lvt_Class");
1003         case lvt_Attribute:
1004             return QLatin1String("lvt_Attribute");
1005         case lvt_Operation:
1006             return QLatin1String("lvt_Operation");
1007         case lvt_Template:
1008             return QLatin1String("lvt_Template");
1009         case lvt_Interface:
1010             return QLatin1String("lvt_Interface");
1011         case lvt_Package:
1012             return QLatin1String("lvt_Package");
1013         case lvt_Component_Diagram:
1014             return QLatin1String("lvt_Component_Diagram");
1015         case lvt_Component_Folder:
1016             return QLatin1String("lvt_Component_Folder");
1017         case lvt_Component_View:
1018             return QLatin1String("lvt_Component_View");
1019         case lvt_Component:
1020             return QLatin1String("lvt_Component");
1021         case lvt_Diagrams:
1022             return QLatin1String("lvt_Diagrams");
1023         case lvt_Artifact:
1024             return QLatin1String("lvt_Artifact");
1025         case lvt_Deployment_Diagram:
1026             return QLatin1String("lvt_Deployment_Diagram");
1027         case lvt_Deployment_Folder:
1028             return QLatin1String("lvt_Deployment_Folder");
1029         case lvt_Deployment_View:
1030             return QLatin1String("lvt_Deployment_View");
1031         case lvt_Port:
1032             return QLatin1String("lvt_Port");
1033         case lvt_Node:
1034             return QLatin1String("lvt_Node");
1035         case lvt_Datatype:
1036             return QLatin1String("lvt_Datatype");
1037         case lvt_Datatype_Folder:
1038             return QLatin1String("lvt_Datatype_Folder");
1039         case lvt_Enum:
1040             return QLatin1String("lvt_Enum");
1041         case lvt_Entity:
1042             return QLatin1String("lvt_Entity");
1043         case lvt_EntityAttribute:
1044             return QLatin1String("lvt_EntityAttribute");
1045         case lvt_EntityRelationship_Diagram:
1046             return QLatin1String("lvt_EntityRelationship_Diagram");
1047         case lvt_EntityRelationship_Folder:
1048             return QLatin1String("lvt_EntityRelationship_Folder");
1049         case lvt_EntityRelationship_Model:
1050             return QLatin1String("lvt_EntityRelationship_Model");
1051         case lvt_Subsystem:
1052             return QLatin1String("lvt_Subsystem");
1053         case lvt_Model:
1054             return QLatin1String("lvt_Model");
1055         case lvt_EnumLiteral:
1056             return QLatin1String("lvt_EnumLiteral");
1057         case lvt_UniqueConstraint:
1058             return QLatin1String("lvt_UniqueConstraint");
1059         case lvt_PrimaryKeyConstraint:
1060             return QLatin1String("lvt_PrimaryKeyConstraint");
1061         case lvt_ForeignKeyConstraint:
1062             return QLatin1String("lvt_ForeignKeyConstraint");
1063         case lvt_CheckConstraint:
1064             return QLatin1String("lvt_CheckConstraint");
1065         case lvt_Category:
1066             return QLatin1String("lvt_Category");
1067         case lvt_Properties:
1068             return QLatin1String("lvt_Properties");
1069         case lvt_Unknown:
1070             return QLatin1String("lvt_Unknown");
1071         case lvt_Instance:
1072             return QLatin1String("lvt_Instance");
1073         case lvt_InstanceAttribute:
1074             return QLatin1String("lvt_InstanceAttribute");
1075         default:
1076             return QLatin1String("? ListViewType ?");
1077     }
1078 }
1079 
1080 /**
1081  * Overloading operator for debugging output.
1082  */
operator <<(QDebug dbg,const UMLListViewItem & item)1083 QDebug operator<<(QDebug dbg, const UMLListViewItem& item)
1084 {
1085     dbg.nospace() << "UMLListViewItem: " << item.text(0)
1086         << ", type=" << UMLListViewItem::toString(item.type())
1087         << ", id=" << Uml::ID::toString(item.ID())
1088         << ", children=" << item.childCount();
1089     return dbg.space();
1090 }
1091