Salome HOME
Redesign SALOME documentation
[modules/gui.git] / doc / salome / gui / input / drag_and_drop.rst
diff --git a/doc/salome/gui/input/drag_and_drop.rst b/doc/salome/gui/input/drag_and_drop.rst
new file mode 100755 (executable)
index 0000000..f322123
--- /dev/null
@@ -0,0 +1,281 @@
+.. _drag_and_drop:
+
+*********************************************************
+Implementing Drag and Drop functionality in SALOME module
+*********************************************************
+
+.. contents:: Table of Contents
+
+**Drag and Drop** provides a simple visual mechanism to transfer
+information between and within applications. 
+
+In some aspects Drag and drop operates similarly to the clipboard 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 as well.
+
+.. _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 a data object or dropping data on it, it is necessary to redefine
+**isDraggable()** and **isDropAccepted()** methods of the corresponding class, a successor
+of the **SUIT_DataObject**. These methods are defined in the base class **SUIT_DataObject**
+and default implementation of both functions returns **false**, which prevents dragging and
+dropping:
+
+::
+
+       bool SUIT_DataObject::isDraggable() const
+       {
+         return false;
+       }
+
+       bool SUIT_DataObject::isDropAccepted() const
+       {
+         return false;
+       }
+
+If your data model is based on the **SUIT_DataObject** and **SUIT_TreeModel**, just
+re-implement these functions in your successor data object class and return **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
+**LightApp_Module** or **SalomeApp_Module** class (as the majority of existing SALOME modules).
+The class **LightApp_Module** (and thus **SalomeApp_Module** also) already provides
+high-level API that can be used for enabling drag and drop functionality.
+
+To enable dragging, redefine **isDraggable()** method of your module class. In this method
+you can analyze the data object subject to the drag operation and decide if
+it is necessary to enable or prevent its dragging:
+
+::
+
+       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;
+       }
+
+Note, that you should not invoke here method **isDragEnabled()** of your data object class
+(in case if it inherits **LightApp_DataObject** or **SalomeApp_DataObject**), unless you
+redefine methods **isDraggable()** and **isDropAccepted()** in your data object class. 
+The reason is that the implementation of these methods in **LightApp_DataObject** class
+redirects calls to the **LightApp_Module** - be careful to avoid entering endless
+recursion loop.
+
+To allow data dropping to an object (the object under the mouse cursor in the
+Object browser during the drag operation) redefine **isDropAccepted()** method of your
+module class:
+
+::
+
+       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;
+       }
+
+The caution about avoiding recursive loop mentioned above is also valid for
+**isDropAccepted()** method.
+
+.. _handle_data_dropping: 
+
+Handling data dropping
+======================
+
+When dragging operation is completed (the data is dropped to an object) the module owning
+the item on which the data is dropped is notified by invoking its **dropObjects()** method:
+
+::
+
+       void LightApp_Module::dropObjects( const DataObjectList& what,
+                                          SUIT_DataObject* where,
+                                          const int row,
+                                          Qt::DropAction action )
+       {
+       }
+
+The default implementation does nothing. However, this method can be redifined in the
+successor class and handle the operation properly. The list of dropped
+data objects is passed via **what** parameter. The data object on which
+the data is dropped is passed via **where** parameter. The parameter **row** specifies in the children list
+the position of object where data is dropped; if this parameter is equal to -1, the
+data is dropped to the end of the children list. Performed drop action is passed
+via **action** parameter; possible values are **Qt::CopyAction** and **Qt::MoveAction**
+(other actions are currently unsupported).
+
+The method **dropObjects()** should analyze the parameters and apply
+the corresponding actions for rearrangement of the data tree, copying or moving the data items depending on the
+operation performed. For example:
+
+::
+
+       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 );
+       }
+
+
+In the above code the function **copyOrMove()** performs actual data tree rearrangement.
+
+.. _drag_drop_light_modules: 
+
+
+Drag and Drop in "light" modules
+================================
+
+The data model of the **light** (not having CORBA engine) SALOME module is usually
+based on the custom tree of data objects. The general approach is to
+inherit a custom data
+object class from the **LightApp_DataObject** and a custom data model from the
+**LightApp_DataModel** class. The data model class is responsible for building the
+appropriate presentation of the data tree in the Object browser.
+
+Thus, the implementation of the drag and drop functionality in a **light** module (more
+precisely, the method **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 **updateObjectBrowser()** function is called.
+
+
+.. _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. In a **full** (CORBA engine based) SALOME
+module, which data model is usually based on the hierarchy of **SALOMEDS::SObject** entities
+provided by the data server functionality, re-arrangement of the data
+tree is not a trivial task.
+
+However, SALOME data server (**SALOMEDS**) CORBA module proposes a mechanism that can be used
+to customize data tree representation in a simple and flexible way -
+:ref:`use_case_builder`.
+
+With use case builder, the **dropObjects()** function can be easily implemented. For example:
+
+::
+
+       // 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 a 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 the name of the object
+             CORBA::String_var name = sobj->GetName();
+             // create a 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 the corresponding attributes or creation
+             // of 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 a given row
+           else
+             useCaseBuilder->AppendTo( where, sobj );        // append to the
+         end of the list
+         }
+       }
+
+
+