--- /dev/null
+/*!
+
+\page howtos How-To's and Best Practices
+
+These documents provide guidelines and best practices explaining different aspects
+and usage examples of SALOME platform.
+
+- \subpage use_case_builder
+- \subpage drag_and_drop
+- \subpage using_pluginsmanager
+
+\page use_case_builder Customize data tree representation in the Object browser by means of use case builder
+
+\tableofcontents
+
+In SALOME, the representation of the data tree in the Object browser for the \a full
+(CORBA-based) module is done basing on the study contents as it is supplied by SALOME
+data server (SALOMEDS). In contrast to the \a light module which data tree is completely
+defined and can be easily attuned by the module specific implementation, \a full module
+must publish its data in the CORBA study by means of the corresponding API of SALOME
+data server, namely \c SALOMEDS::StudyBuilder. As soon as data entities are published
+in the study, they are shown in the Object browser, in the same order as they appear
+in the study tree. Re-arrangement of the data entities with such approach is not a
+trivial task: for example, when copying/moving any data entity at the new position
+within the tree, it is necessary to copy all its attributes also and clear (in case
+of move operation) data entity at the original position. Also, it is not possible to
+have some data items in the tree "invisible" for the user (though it might be useful).
+
+Use case builder provides alternative and more flexible way for customizing of the
+data tree representation. It implements another approach to the data tree hierarchy,
+based on the tree node attributes. With use case builder it is possible to arrange
+and easily re-arrange the data items in the data tree in any appropriate way.
+
+For example, with use case builder it is easy to implement such operations like
+\ref drag_and_drop "Drag and Drop" and Copy/Cut/Paste. With use case builder approach
+it's not important how data entities are arranged in the study tree, they even may be
+lying on the same level - use case builder allows providing custom data tree
+representation, completely indepedent on the study data tree itself. It is even possible
+to hide some data entities in the tree representation while still keeping them in the
+study (to store specific module data).
+
+Object browser automatically checks it the module root data object contains tree node
+attribute and switches to the browsing of the data tree for such module using use case
+builder. Otherwise, it browses data using ordinary study tree iterator. Thus, it is
+possible to have in the same study some modules based on use case builder approach and
+other ones not using it.
+
+\section use_case_builder_usage Use case builder usage
+
+To obtain a reference to the use case builder, the function \c GetUseCaseBuilder() of the
+\c SALOMEDS::Study interface can be used:
+
+\code
+interface Study
+{
+ // Get reference to the use case builder
+ UseCaseBuilder GetUseCaseBuilder();
+};
+\endcode
+
+\c SALOMEDS::UseCaseBuilder interface of the \c SALOMEDS CORBA module provides several
+methods that can be used to build custom data tree. Its API is similar to that one of
+\c SALOMEDS::StudyBuilder interface - it operates with terms \a "father object" and
+\a "child object". In addition, use case builder uses term \a "current object" that is
+used as a parent of the children objects being added if parent is not explicitly
+specified.
+
+\code
+interface UseCaseBuilder
+{
+ // Set top-level root object of the use case tree as current
+ // This method is usually used to add SComponent items to the top level of the tree
+ boolean SetRootCurrent();
+
+ // Set the object theObject as current object of the use case builder
+ boolean SetCurrentObject(in SObject theObject);
+
+ // Append object SObject to the end of children list of the current object
+ boolean Append(in SObject theObject);
+
+ // Append object SObject to the end of children list of parent object theFather
+ boolean AppendTo(in SObject theFather, in SObject theObject);
+
+ // Insert object theFirst before the object theNext (under the same parent object)
+ boolean InsertBefore(in SObject theFirst, in SObject theNext);
+
+ // Remove object from the use case tree (without removing it from the study)
+ boolean Remove(in SObject theObject);
+
+ // Check if the object theObject has children (in the use case tree)
+ boolean HasChildren(in SObject theObject);
+
+ // Get father object of the given object theObject in the use cases tree
+ SObject GetFather(in SObject theObject);
+
+ // Check if given object theObject is added to the use case tree
+ boolean IsUseCaseNode(in SObject theObject);
+
+ // Get the current object of the use case builder
+ SObject GetCurrentObject();
+};
+\endcode
+
+\section browse_use_case_tree Browsing use case data tree
+
+Browsing the use case tree can be done by means of the use case iterator, that is
+provided by the \c SALOMEDS::UseCaseIterator interface of the \c SALOMEDS CORBA
+module. Access to the use case iterator can be done via \c SALOMEDS::UseCaseBuilder
+interface:
+
+\code
+interface UseCaseBuilder
+{
+ // Get reference to the use case iterator and initialize it
+ // by the given object theObject
+ UseCaseIterator GetUseCaseIterator(in SObject theObject);
+};
+\endcode
+
+The API of the \c SALOMEDS::UseCaseIterator interface is similar to the
+\c SALOMEDS::ChildIterator:
+
+\code
+interface UseCaseIterator
+{
+ // Activate or reset use case iterator; boolean parameter allLevels
+ // specifies if the iterator should browse recursively on all sub-levels or
+ // on the first sub-level only
+ void Init(in boolean allLevels);
+ // Check if iterator can browse to the next item
+ boolean More();
+ // Browse iterator to the next object
+ void Next();
+ // Get object currently pointed by the iterator
+ SObject Value();
+};
+\endcode
+
+Typical usage of the \c UseCaseIterator is as follows:
+
+\code
+// get use case builder
+SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder();
+
+// get use case iterator
+SALOMEDS::UseCaseIterator_var iter = useCaseIter->GetUseCaseIterator( sobject.in() );
+// iterate through the sub-items recursively
+for ( useCaseIter->Init( true ); useCaseIter->More(); useCaseIter->Next() ) {
+ SALOMEDS::SObject_var child = useCaseIter->Value();
+ // do something with child
+ // ...
+ // clean-up
+ child->UnRegister();
+}
+// clean-up
+useCaseIter->UnRegister();
+useCaseBuilder->UnRegister();
+\endcode
+
+\section use_case_compatibility Remark about compatibility with existing studies
+
+If one day you decided to switch your module to the use case builder approach to provide
+customization for the data tree representation, you must take care about compatibility
+with existing SALOME studies. Basically it means that you have to add simple code to your
+module's \c Load() (and \c LoadASCII() if necessary) method, that adds tree node
+attributes to all the data entities in your module's data tree. The simplest way to do
+this, is to iterate through all the data items and recursively add them to the use case
+builder:
+
+\code
+// find component
+SALOMEDS::SComponent_var comp = study->FindComponent( "MYMODULE" );
+// add tree node attributes only if component data is present in study
+if ( !CORBA::is_nil( comp ) ) {
+ // get use case builder
+ SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder();
+ // check if tree nodes are already set
+ if ( !useCaseBuilder->IsUseCaseNode( comp.in() ) ) {
+ // set use case builder's current node to the root
+ useCaseBuilder->SetRootCurrent();
+ // add component item to the top level of the use case tree
+ useCaseBuilder->Append( comp.in() );
+ // iterate through all child items recursively
+ SALOMEDS::ChildIterator_var iter = study->NewChildIterator( comp.in() );
+ for ( iter->InitEx( true ); iter->More(); iter->Next() ) {
+ SALOMEDS::SObject_var sobj = iter->Value();
+ SALOMEDS::SObject_var father = sobj->GetFather();
+ // add object to the corresponding level in the use case tree
+ useCaseBuilder->AppendTo( father.in(), sobj.in() );
+ // clean up (avoid memory leaks)
+ sobj->UnRegister();
+ father->UnRegister();
+ }
+ }
+ useCaseBuilder->UnRegister(); // clean up
+}
+\endcode
+
+\page drag_and_drop Implementing Drag and Drop functionality in SALOME module
+
+\tableofcontents
+
+<b>Drag and Drop</b> provides a simple visual mechanism which users can use to transfer
+information between and within applications.
+
+In certain aspect Drag and drop is similar in function to the clipboard's copy/cut/paste
+mechanism.
+
+Since SALOME GUI is implemented on Qt, the drag and drop functionality support
+is provided by means of the corresponding Qt mechanisms.
+
+Currently, dragging and dropping of the items can be done within Object browser only,
+however this functionality can be extended to other GUI elements also.
+
+\section enable_drag_and_drop Enabling drag and drop in SALOME module
+
+The Drag and drop functionality is enabled by default in the Object browser. However,
+to allow dragging of some data object or dropping data on it, it is necessary to redefine
+\c isDraggable() and \c isDropAccepted() methods of the corresponding class, a successor
+of the \c SUIT_DataObject. These methods are defined in the base class \c SUIT_DataObject
+and default implementation of both functions return \c false, that prevents dragging and
+dropping:
+
+\code
+bool SUIT_DataObject::isDraggable() const
+{
+ return false;
+}
+
+bool SUIT_DataObject::isDropAccepted() const
+{
+ return false;
+}
+\endcode
+
+If your data model is based on the \c SUIT_DataObject and \c SUIT_TreeModel, just
+re-implement these functions in your successor data object class and return \c true
+when it is needed (for example, depending on the data object type, state, etc).
+
+Another alternative is available if your module is directly inherited from
+\c LightApp_Module or \c SalomeApp_Module class (as majority of existing SALOME modules).
+The class \c LightApp_Module (and thus \c SalomeApp_Module also) already provides
+high-level API that can be used for enabling drag and drop functionality.
+
+To enable dragging, redefine \c isDraggable() method of your module class. In this method
+you can analyze the data object that is a subject of the drag operation and decide if
+it is necessary to enable or prevent its dragging:
+
+\code
+bool MyModuleGUI::isDraggable( const SUIT_DataObject* what ) const
+{
+ bool result = false;
+ const MyDataObject* obj = dynamic_cast<const MyDataObject*>( what );
+ if ( obj ) {
+ // check if object can be dragged
+ result = <some condition>;
+ }
+ return result;
+}
+\endcode
+
+Note, that you should not invoke here method \c isDragEnabled() of your data object class
+(in case if it inherits \c LightApp_DataObject or \c SalomeApp_DataObject), unless you
+redefine methods \c isDraggable() and \c isDropAccepted() in your data object class.
+The reason is that \c LightApp_DataObject class's implementation of these methods
+redirects calls to the \c LightApp_Module - be careful to avoid entering endless
+recursion loop.
+
+To alllow dropping of the data to some object (the object under the mouse cursor in the
+Object browser during the drag operation) redefine \c isDropAccepted() method of your
+module class:
+
+\code
+bool MyModuleGUI::isDropAccepted( const SUIT_DataObject* where ) const
+{
+ bool result = false;
+ const MyDataObject* obj = dynamic_cast<const MyDataObject*>( where );
+ if ( obj ) {
+ // check if object supports dropping
+ result = <some condition>;
+ }
+ return result;
+}
+\endcode
+
+The caution about avoiding recursive loop mentioned above is also valid for
+\c isDropAccepted() method.
+
+\section handle_data_dropping Handling data dropping
+
+When dragging operation is completed (data is dropped to some object) the module owning
+the item on which data is dropped to is notified by invoking its \c dropObjects() method:
+
+\code
+void LightApp_Module::dropObjects( const DataObjectList& what,
+ SUIT_DataObject* where,
+ const int row,
+ Qt::DropAction action )
+{
+}
+\endcode
+
+Default implementation does nothing. However, this method can be redifined in the
+successor class and handle the operation properly. The list of data objects being dropped
+is passed via \c what parameter. The data object on which data is being dropped is passed
+via \c where parameter. The parameter \c row specifies the position in the children list
+of \c where data object at which data is dropped; it this parameter is equal to -1, the
+data is dropped to the end of children list. Drop action being performed is passed
+via \c action parameter; possible values are \c Qt::CopyAction and \c Qt::MoveAction
+(other actions are currently unsupported).
+
+The method \c dropObjects() should analyze the parameters and apply corresponding actions
+for rearrangement of the data tree, copying or moving the data items depending on the
+operation being performed. For example:
+
+\code
+void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
+ const int row, Qt::DropAction action )
+{
+ if ( action != Qt::CopyAction && action != Qt::MoveAction )
+ return; // unsupported action
+
+ // get parent object
+ MyDataObject* whereObj = dynamic_cast<MyDataObject*>( where );
+ if ( !dataObj ) return; // wrong parent
+
+ // iterate through all objects being dropped
+ for ( int i = 0; i < what.count(); i++ ) {
+ MyDataObject* whatObj = dynamic_cast<MyDataObject*>( what[i] );
+ if ( !whatObj ) continue; // skip wrong objects
+ // perform copying or moving
+ copyOrMove( whatObj, // data object being copied/moved
+ whereObj, // target data object
+ row, // index in the target data object
+ action == Qt::CopyAction ); // true if copying is done
+ }
+ // update Object browser
+ getApp()->updateObjectBrowser( false );
+}
+\endcode
+
+In the above code the function \c copyOrMove() performs actual data tree rearrangement.
+
+\section drag_drop_light_modules Drag and Drop in "light" modules
+
+The data model of the \a light (not having CORBA engine) SALOME module is usually
+based on the custom tree of data objects. The general approach is to inherit own data
+object class from the \c LightApp_DataObject and own data model from the
+\c LightApp_DataModel class. The data model class is responsible for building of the
+appropriate presentation of the data tree in the Object browser.
+
+Thus, the implementation of the drag and drop functionality in a \a light module (more
+precisely, the method \a dropObjects() as described above), consists in copying data
+entities (by creating new instances of the corresponding data object class) or moving
+existing data objects to the new position in a tree. The Object browser will update the
+tree representation automatically, as soon as \c updateObjectBrowser() function is called.
+
+\section drag_drop_full_modules Using UseCaseBuilder for Drag and Drop handling in "full" modules
+
+Drag and drop operation requires underlying data model to allow flexible re-arrangement of
+the data entities inside the data tree. However, in a \a full (CORBA engine based) SALOME
+module, which data model is usually based on the hierarchy of \c SALOMEDS::SObject entities
+provided by the data server functionality, re-arrangement of the data tree is not a trivial
+task.
+
+However, SALOME data server (\c SALOMEDS) CORBA module proposes mechanism that can be used
+to customize data tree representation in a simple and flexible way - \ref use_case_builder
+"use case builder".
+
+With use case builder, the \c dropObjects() function can be easily implemented. For example:
+
+\code
+// GUI part: process objects dropping
+void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
+ const int row, Qt::DropAction action )
+{
+ if ( action != Qt::CopyAction && action != Qt::MoveAction )
+ return; // unsupported action
+
+ // get parent object
+ SalomeApp_DataObject* dataObj = dynamic_cast<SalomeApp_DataObject*>( where );
+ if ( !dataObj ) return; // wrong parent
+ _PTR(SObject) parentObj = dataObj->object();
+
+ // collect objects being dropped
+ MYMODULE_ORB::object_list_var objects = new MYMODULE_ORB::object_list();
+ objects->length( what.count() );
+ int count = 0;
+ for ( int i = 0; i < what.count(); i++ ) {
+ dataObj = dynamic_cast<SalomeApp_DataObject*>( what[i] );
+ if ( !dataObj ) continue; // skip wrong objects
+ _PTR(SObject) sobj = dataObj->object();
+ objects[i] = _CAST(SObject, sobj)->GetSObject();
+ count++;
+ }
+ objects->length( count );
+
+ // call engine function
+ engine()->copyOrMove( objects.in(), // what
+ _CAST(SObject, parentObj)->GetSObject(), // where
+ row, // row
+ action == Qt::CopyAction ); // isCopy
+
+ // update Object browser
+ getApp()->updateObjectBrowser( false );
+}
+
+// Engine part: perform actual data copying / moving
+void MyModule::copyOrMove( const MYMODULE_ORB::object_list& what,
+ SALOMEDS::SObject_ptr where,
+ CORBA::Long row, CORBA::Boolean isCopy )
+{
+ if ( CORBA::is_nil( where ) ) return; // bad parent
+
+ SALOMEDS::Study_var study = where->GetStudy(); // study
+ SALOMEDS::StudyBuilder_var studyBuilder = study->NewBuilder(); // study builder
+ SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder(); // use case builder
+ SALOMEDS::SComponent_var father = where->GetFatherComponent(); // father component
+ std::string dataType = father->ComponentDataType();
+ if ( dataType != "MYMODULE" ) return; // not a MYMODULE component
+
+ SALOMEDS::SObject_var objAfter;
+ if ( row >= 0 && useCaseBuilder->HasChildren( where ) ) {
+ // insert at given row -> find insertion position
+ SALOMEDS::UseCaseIterator_var useCaseIt = useCaseBuilder->GetUseCaseIterator( where );
+ int i;
+ for ( i = 0; i < row && useCaseIt->More(); i++, useCaseIt->Next() );
+ if ( i == row && useCaseIt->More() ) {
+ objAfter = useCaseIt->Value();
+ }
+ }
+ // process all objects in a given list
+ for ( int i = 0; i < what.length(); i++ ) {
+ SALOMEDS::SObject_var sobj = what[i];
+ if ( CORBA::is_nil( sobj ) ) continue; // skip bad object
+ if ( isCopy ) {
+ // copying is performed
+ // get name of the object
+ CORBA::String_var name = sobj->GetName();
+ // create new object, as a child of the component object
+ SALOMEDS::SObject_var new_sobj = studyBuilder->NewObject( father );
+ new_sobj->SetAttrString( "AttributeName", name.in() );
+ sobj = new_sobj;
+ // ... perform other necessary data copying like
+ // adding corresponding attributes or creation
+ // of a servant data entities...
+ }
+ // insert the object or its copy to the use case tree
+ if ( !CORBA::is_nil( objAfter ) )
+ useCaseBuilder->InsertBefore( sobj, objAfter ); // insert at given row
+ else
+ useCaseBuilder->AppendTo( where, sobj ); // append to the end of list
+ }
+}
+
+\endcode
+
+*/