umbrello API Documentation

umlclipboard.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *   copyright (C) 2002-2007                                               *
00009  *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "umlclipboard.h"
00014 
00015 // qt/kde includes
00016 #include <kdebug.h>
00017 #include <kmessagebox.h>
00018 #include <klocale.h>
00019 
00020 // local includes
00021 #include "umldrag.h"
00022 #include "idchangelog.h"
00023 #include "../associationwidget.h"
00024 #include "../attribute.h"
00025 #include "../classifier.h"
00026 #include "../floatingtextwidget.h"
00027 #include "../operation.h"
00028 #include "../umldoc.h"
00029 #include "../umllistview.h"
00030 #include "../umllistviewitem.h"
00031 #include "../umlobjectlist.h"
00032 #include "../umlview.h"
00033 #include "../umlwidget.h"
00034 #include "../uml.h"
00035 #include "../model_utils.h"
00036 
00037 UMLClipboard::UMLClipboard() {
00038     m_type = clip1;
00039 }
00040 
00041 UMLClipboard::~UMLClipboard() {
00042 }
00043 
00044 QMimeSource* UMLClipboard::copy(bool fromView/*=false*/) {
00045     //Clear previous copied data
00046     m_AssociationList.clear();
00047     m_ItemList.clear();
00048     m_ObjectList.clear();
00049     m_ViewList.clear();
00050 
00051     UMLDrag *data = 0;
00052     QPixmap* png = 0;
00053 
00054     UMLListView * listView = UMLApp::app()->getListView();
00055     UMLListViewItemList selectedItems;
00056     selectedItems.setAutoDelete(false);
00057 
00058     if(fromView) {
00059         m_type = clip4;
00060         UMLView *view = UMLApp::app()->getCurrentView();
00061         view->checkSelections();
00062         if(!view->getSelectedWidgets(m_WidgetList)) {
00063             return 0;
00064         }
00065         //if there is no selected widget then there is no copy action
00066         if(!m_WidgetList.count()) {
00067             return 0;
00068         }
00069         m_AssociationList = view->getSelectedAssocs();
00070         view->copyAsImage(png);
00071 
00072     } else { //if the copy action is being performed from the ListView
00073         if(!listView->getSelectedItems(selectedItems)) {
00074             return 0;
00075         }
00076         //Set What type of copy operation are we performing and
00077         //also fill m_ViewList with all the selected Diagrams
00078         setCopyType(selectedItems);
00079 
00080         //if we are copying a diagram or part of a diagram, select the items
00081         //on the ListView that correspond to a UseCase, Actor or Concept
00082         //in the Diagram
00083         if(m_type == clip2) {
00084             //Fill the member lists with all the object and stuff to be copied
00085             //to the clipboard
00086             selectedItems.clear();
00087             //For each selected view select all the Actors, USe Cases and Concepts
00088             //widgets in the ListView
00089             for (UMLViewListIt vit(m_ViewList); vit.current(); ++vit) {
00090                 UMLObjectList objects = vit.current()->getUMLObjects();
00091                 for (UMLObjectListIt oit(objects); oit.current(); ++oit) {
00092                     UMLObject *o = oit.current();
00093                     UMLListViewItem *item = listView->findUMLObject(o);
00094                     if(item) {
00095                         listView->setSelected(item, true);
00096                     }
00097                 }
00098             }
00099             if(!listView->getSelectedItems(selectedItems)) {
00100                 return 0;
00101             }
00102         }
00103         if(!fillSelectionLists(selectedItems)) {
00104             return 0;
00105         }
00106     }
00107     int i =0;
00108     switch(m_type) {
00109     case clip1:
00110         data = new UMLDrag(m_ObjectList);
00111         break;
00112     case clip2:
00113         data = new UMLDrag(m_ObjectList, m_ItemList, m_ViewList);
00114         break;
00115     case clip3:
00116         data = new UMLDrag(m_ItemList);
00117         break;
00118     case clip4:
00119         if(png) {
00120             UMLView *view = UMLApp::app()->getCurrentView();
00121             data = new UMLDrag(m_ObjectList, m_WidgetList,
00122                                m_AssociationList, *png, view->getType());
00123         } else {
00124             return 0;
00125         }
00126         break;
00127     case clip5:
00128         data = new UMLDrag(m_ObjectList, i);
00129         // The int i is used to differentiate
00130         // which UMLDrag constructor gets called.
00131         break;
00132     }
00133 
00134     return (QMimeSource*)data;
00135 }
00136 
00137 bool UMLClipboard::paste(QMimeSource* data) {
00138     UMLDoc *doc = UMLApp::app()->getDocument();
00139     bool result = false;
00140     doc->beginPaste();
00141     switch(UMLDrag::getCodingType(data)) {
00142     case 1:
00143         result = pasteClip1(data);
00144         break;
00145     case 2:
00146         result = pasteClip2(data);
00147         break;
00148     case 3:
00149         result = pasteClip3(data);
00150         break;
00151     case 4:
00152         result = pasteClip4(data);
00153         break;
00154     case 5:
00155         result = pasteClip5(data);
00156         break;
00157     default:
00158         break;
00159     }
00160     doc->endPaste();
00161     return result;
00162 }
00163 
00164 bool UMLClipboard::fillSelectionLists(UMLListViewItemList& SelectedItems) {
00165     UMLListViewItemListIt it(SelectedItems);
00166     UMLListViewItem* item = 0;
00167     Uml::ListView_Type type;
00168     switch(m_type) {
00169     case clip4:
00170         break;
00171     case clip3:
00172         for ( ; it.current(); ++it ) {
00173             item = (UMLListViewItem*)it.current();
00174             type = item->getType();
00175             if ( !Model_Utils::typeIsClassifierList(type) ) {
00176                 m_ItemList.append(item);
00177                 insertItemChildren(item, SelectedItems);
00178                 //Because it is being called when m_type is 3
00179                 //it will insert only child empty folders of other folders.
00180                 //If a child folder
00181                 //is not empty that means m_type wouldn't be 3 because if a folder is
00182                 //selected then its complete contents are treated as if
00183                 //they were selected
00184             }
00185         }
00186         break;
00187     case clip2:
00188     case clip1:
00189         for ( ; it.current(); ++it ) {
00190             item = (UMLListViewItem*)it.current();
00191             type = item->getType();
00192             if ( !Model_Utils::typeIsClassifierList(type) ) {
00193 
00194                 m_ItemList.append(item);
00195 
00196                 if ( Model_Utils::typeIsCanvasWidget(type) ) {
00197                     m_ObjectList.append(item->getUMLObject());
00198                 }
00199                 insertItemChildren(it.current(), SelectedItems);
00200             }
00201         }
00202         break;
00203     case clip5:
00204         for ( ; it.current(); ++it ) {
00205             item = (UMLListViewItem*)it.current();
00206             type = item->getType();
00207             if( Model_Utils::typeIsClassifierList(type) ) {
00208                 m_ItemList.append(item);
00209                 m_ObjectList.append(item->getUMLObject());
00210 
00211             } else {
00212                 return false;
00213             }
00214         }
00215         break;
00216     }
00217 
00218     return true;
00219 }
00220 
00221 void UMLClipboard::setCopyType(UMLListViewItemList& SelectedItems) {
00222     bool withDiagrams = false; //If the selection includes diagrams
00223     bool withObjects = false; //If the selection includes objects
00224     bool onlyAttsOps = false; //If the selection only includes Attributes and/or Operations
00225     UMLListViewItemListIt it(SelectedItems);
00226     for ( ; it.current(); ++it ) {
00227         checkItemForCopyType(it.current(), withDiagrams, withObjects, onlyAttsOps);
00228     }
00229     if(onlyAttsOps) {
00230         m_type = clip5;
00231     } else if(withDiagrams) {
00232         m_type = clip2;
00233     } else if(withObjects) {
00234         m_type = clip1;
00235     } else {
00236         m_type = clip3;
00237     }
00238 }
00239 
00240 void UMLClipboard::checkItemForCopyType(UMLListViewItem* Item, bool & WithDiagrams, bool &WithObjects,
00241                                         bool &OnlyAttsOps) {
00242     if(!Item) {
00243         return;
00244     }
00245     UMLDoc *doc = UMLApp::app()->getDocument();
00246     OnlyAttsOps = true;
00247     UMLView * view = 0;
00248     UMLListViewItem * child = 0;
00249     Uml::ListView_Type type = Item->getType();
00250     if ( Model_Utils::typeIsCanvasWidget(type) ) {
00251         WithObjects = true;
00252         OnlyAttsOps = false;
00253     } else if ( Model_Utils::typeIsDiagram(type) ) {
00254         WithDiagrams = true;
00255         OnlyAttsOps = false;
00256         view = doc->findView( Item->getID() );
00257         m_ViewList.append( view );
00258     } else if ( Model_Utils::typeIsFolder(type) ) {
00259         OnlyAttsOps = false;
00260         if(Item->childCount()) {
00261             child = (UMLListViewItem*)Item->firstChild();
00262             while(child) {
00263                 checkItemForCopyType(child, WithDiagrams, WithObjects, OnlyAttsOps);
00264                 child = (UMLListViewItem*)child->nextSibling();
00265             }
00266         }
00267     }
00268 }
00269 
00271 bool UMLClipboard::insertItemChildren(UMLListViewItem * Item, UMLListViewItemList& SelectedItems) {
00272     if(Item->childCount()) {
00273         UMLListViewItem * child = (UMLListViewItem*)Item->firstChild();
00274         int type;
00275         while(child) {
00276             m_ItemList.append(child);
00277             type = child->getType();
00278             if(type == Uml::lvt_Actor || type == Uml::lvt_UseCase || type == Uml::lvt_Class) {
00279                 m_ObjectList.append(child->getUMLObject());
00280             }
00281             // If the child is selected, remove it from the list of selected items
00282             // otherwise it will be inserted twice in m_ObjectList.
00283             if(child->isSelected()) {
00284                 SelectedItems.remove(SelectedItems.find(child) );
00285             }
00286             insertItemChildren(child, SelectedItems);
00287             child = (UMLListViewItem*)child->nextSibling();
00288         }
00289     }
00290     return true;
00291 }
00292 
00293 bool UMLClipboard::pasteChildren(UMLListViewItem *parent, IDChangeLog *chgLog) {
00294     if (!parent) {
00295         kWarning() << "Paste Children Error, parent missing" << endl;
00296         return false;
00297     }
00298     UMLDoc *doc = UMLApp::app()->getDocument();
00299     UMLListView *listView = UMLApp::app()->getListView();
00300     UMLListViewItem *childItem = static_cast<UMLListViewItem*>(parent->firstChild());
00301     while (childItem) {
00302         Uml::IDType oldID = childItem->getID();
00303         Uml::IDType newID = chgLog->findNewID(oldID);
00304         UMLListViewItem *shouldNotExist = listView->findItem(newID);
00305         if (shouldNotExist) {
00306             kError() << "UMLClipboard::pasteChildren: new list view item " << ID2STR(newID)
00307             << " already exists (internal error)" << endl;
00308             childItem = static_cast<UMLListViewItem*>(childItem->nextSibling());
00309             continue;
00310         }
00311         UMLObject *newObj = doc->findObjectById(newID);
00312         if (newObj) {
00313             kDebug() << "UMLClipboard::pasteChildren: adjusting lvitem(" << ID2STR(oldID)
00314             << ") to new UMLObject(" << ID2STR(newID) << ")" << endl;
00315             childItem->setUMLObject(newObj);
00316             childItem->setText(newObj->getName());
00317         } else {
00318             kDebug() << "UMLClipboard::pasteChildren: no UMLObject found for lvitem "
00319             << ID2STR(newID) << endl;
00320         }
00321         childItem = static_cast<UMLListViewItem*>(childItem->nextSibling());
00322     }
00323     return true;
00324 }
00325 
00328 void UMLClipboard::CleanAssociations(AssociationWidgetList& associations) {
00329     AssociationWidgetListIt it(associations);
00330     AssociationWidget* assoc = it.current();
00331 
00332     while (assoc) {
00333         ++it;
00334         assoc = it.current();
00335     }
00336 }
00337 
00340 bool UMLClipboard::pasteClip1(QMimeSource* data) {
00341     UMLObjectList objects;
00342     if (! UMLDrag::decodeClip1(data, objects)) {
00343         return false;
00344     }
00345     UMLListView *lv = UMLApp::app()->getListView();
00346     if ( !lv->startedCopy() )
00347         return true;
00348     lv->setStartedCopy(false);
00349     /* If we get here we are pasting after a Copy and need to
00350     // paste possible children.
00351     UMLListViewItem* itemdata = 0;
00352     UMLListViewItemListIt it(itemdatalist);
00353     while ( (itemdata=it.current()) != 0 ) {
00354         if(itemdata -> childCount()) {
00355                 if(!pasteChildren(itemdata, idchanges)) {
00356                         return false;
00357                 }
00358         }
00359         ++it;
00360     }
00361      */
00362     return true;
00363 }
00364 
00367 bool UMLClipboard::pasteClip2(QMimeSource* data) {
00368     UMLDoc *doc = UMLApp::app()->getDocument();
00369     UMLListViewItemList itemdatalist;
00370     UMLObjectList objects;
00371     objects.setAutoDelete(false);
00372     UMLViewList         views;
00373     IDChangeLog* idchanges = 0;
00374 
00375     bool result = UMLDrag::decodeClip2(data, objects, itemdatalist, views);
00376     if(!result) {
00377         return false;
00378     }
00379     UMLObject *obj = 0;
00380     UMLObjectListIt object_it(objects);
00381     idchanges = doc->getChangeLog();
00382     if(!idchanges) {
00383         return false;
00384     }
00385     while ( (obj=object_it.current()) != 0 ) {
00386         ++object_it;
00387         if(!doc->assignNewIDs(obj)) {
00388             kDebug()<<"UMLClipboard: error adding umlobject"<<endl;
00389             return false;
00390         }
00391     }
00392 
00393     UMLView * pView = 0;
00394     UMLViewListIt view_it( views );
00395     while ( ( pView =view_it.current()) != 0 ) {
00396         ++view_it;
00397         if( !doc->addUMLView( pView ) ) {
00398             return false;
00399         }
00400     }
00401 
00402     UMLListView *listView = UMLApp::app()->getListView();
00403     UMLListViewItem* item = 0;
00404     UMLListViewItem* itemdata = 0;
00405     UMLListViewItemListIt it(itemdatalist);
00406     while ( (itemdata=it.current()) != 0 ) {
00407         item = listView->createItem(*itemdata, *idchanges);
00408         if(!item) {
00409             return false;
00410         }
00411         if(itemdata -> childCount()) {
00412             if(!pasteChildren(item, idchanges)) {
00413                 return false;
00414             }
00415         }
00416         ++it;
00417     }
00418 
00419     return result;
00420 }
00421 
00424 bool UMLClipboard::pasteClip3(QMimeSource* data) {
00425     UMLDoc *doc = UMLApp::app()->getDocument();
00426     UMLListViewItemList itemdatalist;
00427     UMLListViewItem* item = 0;
00428     UMLListViewItem* itemdata = 0;
00429     IDChangeLog* idchanges = doc->getChangeLog();
00430 
00431     if(!idchanges) {
00432         return false;
00433     }
00434 
00435     UMLListView *listView = UMLApp::app()->getListView();
00436     bool result = UMLDrag::decodeClip3(data, itemdatalist, listView);
00437     if(!result) {
00438         return false;
00439     }
00440     UMLListViewItemListIt it(itemdatalist);
00441     while ( (itemdata=it.current()) != 0 ) {
00442         item = listView->createItem(*itemdata, *idchanges);
00443         if(itemdata -> childCount()) {
00444             if(!pasteChildren(item, idchanges)) {
00445                 return false;
00446             }
00447         }
00448         ++it;
00449     }
00450 
00451     return result;
00452 }
00453 
00456 bool UMLClipboard::pasteClip4(QMimeSource* data) {
00457     UMLDoc *doc = UMLApp::app()->getDocument();
00458 
00459     UMLObjectList objects;
00460     objects.setAutoDelete(false);
00461 
00462 
00463     UMLWidgetList               widgets;
00464     widgets.setAutoDelete(false);
00465 
00466     AssociationWidgetList       assocs;
00467     assocs.setAutoDelete(false);
00468 
00469     IDChangeLog* idchanges = 0;
00470 
00471     Uml::Diagram_Type diagramType;
00472 
00473     if( !UMLDrag::decodeClip4(data, objects, widgets, assocs, diagramType) ) {
00474         return false;
00475     }
00476 
00477     if( diagramType != UMLApp::app()->getCurrentView()->getType() ) {
00478         if( !checkPasteWidgets(widgets) ) {
00479             assocs.setAutoDelete(true);
00480             assocs.clear();
00481             return false;
00482         }
00483     }
00484     UMLObjectListIt object_it(objects);
00485     idchanges = doc->getChangeLog();
00486     if(!idchanges) {
00487         return false;
00488     }
00489      //make sure the file we are pasting into has the objects
00490      //we need if there are widgets to be pasted
00491      UMLObject* obj = 0;
00492      while ( (obj=object_it.current()) != 0 ) {
00493          ++object_it;
00494 
00495         if(!doc->assignNewIDs(obj)) {
00496             return false;
00497         }
00498 
00499      }
00500 
00501     //now add any widget we are want to paste
00502     bool objectAlreadyExists = false;
00503     UMLView *currentView = UMLApp::app()->getCurrentView();
00504     currentView->beginPartialWidgetPaste();
00505     UMLWidget* widget =0;
00506     UMLWidgetListIt widget_it(widgets);
00507     while ( (widget=widget_it.current()) != 0 ) {
00508         ++widget_it;
00509 
00510         Uml::IDType oldId = widget->getID();
00511         Uml::IDType newId = idchanges->findNewID(oldId);
00512         if (currentView->findWidget(newId)) {
00513             kError() << "UMLClipboard::pasteClip4: widget (oldID=" << ID2STR(oldId)
00514                 << ", newID=" << ID2STR(newId) << ") already exists in target view."
00515                 << endl;
00516             widgets.remove(widget);
00517             delete widget;
00518             objectAlreadyExists = true;
00519         } else if (! currentView->addWidget(widget, true)) {
00520             currentView->endPartialWidgetPaste();
00521             return false;
00522         }
00523     }
00524 
00525     //now paste the associations
00526     AssociationWidget* assoc;
00527     AssociationWidgetListIt assoc_it(assocs);
00528     while ( (assoc=assoc_it.current()) != 0 ) {
00529         ++assoc_it;
00530         if (!currentView->addAssociation(assoc, true)) {
00531             currentView->endPartialWidgetPaste();
00532             return false;
00533         }
00534     }
00535 
00536     //Activate all the pasted associations and widgets
00537     currentView->activate();
00538     currentView->endPartialWidgetPaste();
00539 
00540     /*
00541     UMLListView *listView = UMLApp::app()->getListView();
00542     UMLListViewItem* item = 0;
00543     UMLListViewItem* itemdata = 0;
00544     UMLListViewItemListIt it(itemdatalist);
00545     while ( (itemdata=it.current()) != 0 ) {
00546         item = listView->createItem(*itemdata, *idchanges);
00547         if(!item) {
00548             return false;
00549         }
00550         if(itemdata -> childCount()) {
00551             if(!pasteChildren(item, idchanges)) {
00552                 return false;
00553             }
00554         }
00555         ++it;
00556         }*/
00557 
00558     if (objectAlreadyExists) {
00559         pasteItemAlreadyExists();
00560     }
00561     return true;
00562 }
00563 
00566 bool UMLClipboard::pasteClip5(QMimeSource* data) {
00567     UMLDoc *doc = UMLApp::app()->getDocument();
00568     UMLListView *listView = UMLApp::app()->getListView();
00569     UMLListViewItem* lvitem = dynamic_cast<UMLListViewItem *>( listView->currentItem() );
00570     if (!lvitem ||
00571             (lvitem->getType() != Uml::lvt_Class && lvitem->getType() != Uml::lvt_Interface)) {
00572         return false;
00573     }
00574     UMLClassifier *parent = dynamic_cast<UMLClassifier *>(lvitem->getUMLObject());
00575     if (parent == NULL) {
00576         kError() << "UMLClipboard::pasteClip5: parent is not a UMLClassifier"
00577         << endl;
00578         return false;
00579     }
00580 
00581     UMLObjectList objects;
00582     objects.setAutoDelete(false);
00583     IDChangeLog* idchanges = 0;
00584     bool result = UMLDrag::decodeClip5(data, objects, parent);
00585 
00586     if(!result) {
00587         return false;
00588     }
00589 
00590     UMLObject   *obj = 0;
00591     doc->setModified(true);
00592     idchanges = doc->getChangeLog();
00593     // Assume success if at least one child object could be pasted
00594     if (objects.count())
00595         result = false;
00596 
00597     for (UMLObjectListIt it(objects); (obj = it.current()) != NULL; ++it) {
00598         obj->setID(doc->assignNewID(obj->getID()));
00599         switch(obj->getBaseType()) {
00600         case Uml::ot_Attribute :
00601             {
00602                 UMLObject *exist = parent->findChildObject(obj->getName(), Uml::ot_Attribute);
00603                 if (exist) {
00604                     QString newName = parent->uniqChildName(Uml::ot_Attribute, obj->getName());
00605                     obj->setName(newName);
00606                 }
00607                 UMLAttribute *att = static_cast<UMLAttribute*>(obj);
00608                 if (parent->addAttribute(att, idchanges)) {
00609                     result = true;
00610                 } else {
00611                     kError() << "UMLClipboard::pasteClip5: " << parent->getName()
00612                         << "->addAttribute(" << att->getName() << ") failed" << endl;
00613                 }
00614                 break;
00615             }
00616         case Uml::ot_Operation :
00617             {
00618                 UMLOperation *op = static_cast<UMLOperation*>(obj);
00619                 UMLOperation *exist = parent->checkOperationSignature(op->getName(), op->getParmList());
00620                 if (exist) {
00621                     QString newName = parent->uniqChildName(Uml::ot_Operation, obj->getName());
00622                     op->setName(newName);
00623                 }
00624                 if (parent->addOperation(op, idchanges)) {
00625                     result = true;
00626                 } else {
00627                     kError() << "UMLClipboard::pasteClip5: " << parent->getName()
00628                         << "->addOperation(" << op->getName() << ") failed" << endl;
00629                 }
00630                 break;
00631             }
00632         default :
00633             kWarning() << "pasting unknown children type in clip type 5" << endl;
00634             return false;
00635         }
00636     }
00637 
00638     return result;
00639 }
00640 
00641 bool UMLClipboard::insertItemChildren( UMLListViewItem * item ) {
00642     if( item -> childCount() ) {
00643         UMLListViewItem * child =dynamic_cast<UMLListViewItem *>( item -> firstChild() );
00644         while( child ) {
00645             m_ItemList.append( child );
00646             insertItemChildren( child );
00647             child = dynamic_cast<UMLListViewItem *>( child->nextSibling() );
00648         }
00649     }
00650     return true;
00651 }
00652 
00653 bool UMLClipboard::checkPasteWidgets( UMLWidgetList & widgetList ) {
00654     bool retval = true;
00655     UMLWidget * p = 0;
00656     UMLWidgetListIt it( widgetList );
00657     while ( ( p = it.current()) != 0 ) {
00658         ++it;
00659         switch( p -> getBaseType() ) {
00660         case Uml::wt_Note:
00661             break;
00662 
00663         case Uml::wt_Text:
00664             {
00665                 FloatingTextWidget *ft = static_cast<FloatingTextWidget*>(p);
00666                 if (ft->getRole() != Uml::tr_Floating) {
00667                     widgetList.remove(p);
00668                     delete ft;
00669                     retval = false;
00670                 }
00671             }
00672             break;
00673 
00674         default:
00675             widgetList.remove(p);
00676             delete p;
00677             retval = false;
00678             break;
00679         }
00680     }
00681     return retval;
00682 }
00683 
00684 void UMLClipboard::pasteItemAlreadyExists() {
00685     UMLView *currentView = UMLApp::app()->getCurrentView();
00686     KMessageBox::sorry( currentView,
00687                         i18n("At least one of the items in the clipboard "
00688                              "could not be pasted because an item of the "
00689                              "same name already exists.  Any other items "
00690                              "have been pasted."),
00691                         i18n("Paste Error") );
00692 }
00693 
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:08:00 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003