Salome HOME
Redesign SALOME documentation
[modules/gui.git] / doc / salome / gui / input / drag_and_drop.rst
1 .. _drag_and_drop:
2
3 *********************************************************
4 Implementing Drag and Drop functionality in SALOME module
5 *********************************************************
6
7 .. contents:: Table of Contents
8
9 **Drag and Drop** provides a simple visual mechanism to transfer
10 information between and within applications. 
11
12 In some aspects Drag and drop operates similarly to the clipboard copy/cut/paste
13 mechanism.
14
15 Since SALOME GUI is implemented on Qt, the drag and drop functionality support
16 is provided by means of the corresponding Qt mechanisms.
17
18 Currently dragging and dropping of the items can be done within Object browser only,
19 however this functionality can be extended to other GUI elements as well.
20
21 .. _enable_drag_and_drop: 
22
23 Enabling drag and drop in SALOME module
24 =======================================
25
26 The Drag and drop functionality is enabled by default in the Object browser. However,
27 to allow dragging of a data object or dropping data on it, it is necessary to redefine
28 **isDraggable()** and **isDropAccepted()** methods of the corresponding class, a successor
29 of the **SUIT_DataObject**. These methods are defined in the base class **SUIT_DataObject**
30 and default implementation of both functions returns **false**, which prevents dragging and
31 dropping:
32
33 ::
34
35         bool SUIT_DataObject::isDraggable() const
36         {
37           return false;
38         }
39
40         bool SUIT_DataObject::isDropAccepted() const
41         {
42           return false;
43         }
44
45 If your data model is based on the **SUIT_DataObject** and **SUIT_TreeModel**, just
46 re-implement these functions in your successor data object class and return **true**
47 when it is needed (for example, depending on the data object type, state, etc).
48
49 Another alternative is available if your module is directly inherited from
50 **LightApp_Module** or **SalomeApp_Module** class (as the majority of existing SALOME modules).
51 The class **LightApp_Module** (and thus **SalomeApp_Module** also) already provides
52 high-level API that can be used for enabling drag and drop functionality.
53
54 To enable dragging, redefine **isDraggable()** method of your module class. In this method
55 you can analyze the data object subject to the drag operation and decide if
56 it is necessary to enable or prevent its dragging:
57
58 ::
59
60         bool MyModuleGUI::isDraggable( const SUIT_DataObject* what ) const
61         {
62           bool result = false;
63           const MyDataObject* obj = dynamic_cast<const MyDataObject*>( what );
64           if ( obj ) {
65             // check if object can be dragged
66             result = <some condition>;
67           }
68           return result;
69         }
70
71 Note, that you should not invoke here method **isDragEnabled()** of your data object class
72 (in case if it inherits **LightApp_DataObject** or **SalomeApp_DataObject**), unless you
73 redefine methods **isDraggable()** and **isDropAccepted()** in your data object class. 
74 The reason is that the implementation of these methods in **LightApp_DataObject** class
75 redirects calls to the **LightApp_Module** - be careful to avoid entering endless
76 recursion loop.
77
78 To allow data dropping to an object (the object under the mouse cursor in the
79 Object browser during the drag operation) redefine **isDropAccepted()** method of your
80 module class:
81
82 ::
83
84         bool MyModuleGUI::isDropAccepted( const SUIT_DataObject* where ) const
85         {
86           bool result = false;
87           const MyDataObject* obj = dynamic_cast<const MyDataObject*>( where );
88           if ( obj ) {
89             // check if object supports dropping
90             result = <some condition>;
91           }
92           return result;
93         }
94
95 The caution about avoiding recursive loop mentioned above is also valid for
96 **isDropAccepted()** method.
97
98 .. _handle_data_dropping: 
99
100 Handling data dropping
101 ======================
102
103 When dragging operation is completed (the data is dropped to an object) the module owning
104 the item on which the data is dropped is notified by invoking its **dropObjects()** method:
105
106 ::
107
108         void LightApp_Module::dropObjects( const DataObjectList& what,
109                                            SUIT_DataObject* where,
110                                            const int row,
111                                            Qt::DropAction action )
112         {
113         }
114
115 The default implementation does nothing. However, this method can be redifined in the
116 successor class and handle the operation properly. The list of dropped
117 data objects is passed via **what** parameter. The data object on which
118 the data is dropped is passed via **where** parameter. The parameter **row** specifies in the children list
119 the position of object where data is dropped; if this parameter is equal to -1, the
120 data is dropped to the end of the children list. Performed drop action is passed
121 via **action** parameter; possible values are **Qt::CopyAction** and **Qt::MoveAction**
122 (other actions are currently unsupported).
123
124 The method **dropObjects()** should analyze the parameters and apply
125 the corresponding actions for rearrangement of the data tree, copying or moving the data items depending on the
126 operation performed. For example:
127
128 ::
129
130         void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
131                                        const int row, Qt::DropAction action )
132         {
133           if ( action != Qt::CopyAction && action != Qt::MoveAction )
134             return; // unsupported action
135
136           // get parent object
137           MyDataObject* whereObj = dynamic_cast<MyDataObject*>( where );
138           if ( !dataObj ) return; // wrong parent
139
140           // iterate through all objects being dropped
141           for ( int i = 0; i < what.count(); i++ ) {
142             MyDataObject* whatObj = dynamic_cast<MyDataObject*>( what[i] );
143             if ( !whatObj ) continue;                // skip wrong objects
144             // perform copying or moving
145             copyOrMove( whatObj,                     // data object being copied/moved
146                         whereObj,                    // target data object
147                         row,                         // index in the target data object
148                         action == Qt::CopyAction );  // true if copying is done
149           }
150           // update Object browser
151           getApp()->updateObjectBrowser( false );
152         }
153
154
155 In the above code the function **copyOrMove()** performs actual data tree rearrangement.
156
157 .. _drag_drop_light_modules: 
158
159
160 Drag and Drop in "light" modules
161 ================================
162
163 The data model of the **light** (not having CORBA engine) SALOME module is usually
164 based on the custom tree of data objects. The general approach is to
165 inherit a custom data
166 object class from the **LightApp_DataObject** and a custom data model from the
167 **LightApp_DataModel** class. The data model class is responsible for building the
168 appropriate presentation of the data tree in the Object browser.
169
170 Thus, the implementation of the drag and drop functionality in a **light** module (more
171 precisely, the method **dropObjects()** as described above), consists in copying data
172 entities (by creating new instances of the corresponding data object class) or moving
173 existing data objects to the new position in a tree. The Object browser will update the
174 tree representation automatically, as soon as **updateObjectBrowser()** function is called.
175
176
177 .. _drag_drop_full_modules:
178
179 Using UseCaseBuilder for Drag and Drop handling in "full" modules
180 =================================================================
181
182 Drag and drop operation requires underlying data model to allow flexible re-arrangement of
183 the data entities inside the data tree. In a **full** (CORBA engine based) SALOME
184 module, which data model is usually based on the hierarchy of **SALOMEDS::SObject** entities
185 provided by the data server functionality, re-arrangement of the data
186 tree is not a trivial task.
187
188 However, SALOME data server (**SALOMEDS**) CORBA module proposes a mechanism that can be used
189 to customize data tree representation in a simple and flexible way -
190 :ref:`use_case_builder`.
191
192 With use case builder, the **dropObjects()** function can be easily implemented. For example:
193
194 ::
195
196         // GUI part: process objects dropping
197         void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
198                                        const int row, Qt::DropAction action )
199         {
200           if ( action != Qt::CopyAction && action != Qt::MoveAction )
201             return; // unsupported action
202
203           // get parent object
204           SalomeApp_DataObject* dataObj = dynamic_cast<SalomeApp_DataObject*>( where );
205           if ( !dataObj ) return; // wrong parent
206           _PTR(SObject) parentObj = dataObj->object();
207
208           // collect objects being dropped
209           MYMODULE_ORB::object_list_var objects = new MYMODULE_ORB::object_list();
210           objects->length( what.count() );
211           int count = 0;
212           for ( int i = 0; i < what.count(); i++ ) {
213             dataObj = dynamic_cast<SalomeApp_DataObject*>( what[i] );
214             if ( !dataObj ) continue;  // skip wrong objects
215             _PTR(SObject) sobj = dataObj->object();
216             objects[i] = _CAST(SObject, sobj)->GetSObject();
217             count++;
218           }
219           objects->length( count );
220
221           // call engine function
222           engine()->copyOrMove( objects.in(),                              // what
223                                 _CAST(SObject, parentObj)->GetSObject(),   // where
224                                 row,                                       // row
225                                 action == Qt::CopyAction );                // isCopy
226
227           // update Object browser
228           getApp()->updateObjectBrowser( false );
229         }
230
231         // Engine part: perform actual data copying / moving
232         void MyModule::copyOrMove( const MYMODULE_ORB::object_list& what,
233                                    SALOMEDS::SObject_ptr where,
234                                    CORBA::Long row, CORBA::Boolean isCopy )
235         {
236           if ( CORBA::is_nil( where ) ) return; // bad parent
237
238           SALOMEDS::Study_var study = where->GetStudy();                               // study
239           SALOMEDS::StudyBuilder_var studyBuilder = study->NewBuilder();               // study builder
240           SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder();    // use case builder
241           SALOMEDS::SComponent_var father = where->GetFatherComponent();               // father component
242           std::string dataType = father->ComponentDataType();
243           if ( dataType != "MYMODULE" ) return; // not a MYMODULE component
244   
245           SALOMEDS::SObject_var objAfter;
246           if ( row >= 0 && useCaseBuilder->HasChildren( where ) ) {
247             // insert at a given row -> find insertion position
248             SALOMEDS::UseCaseIterator_var useCaseIt = useCaseBuilder->GetUseCaseIterator( where );
249             int i;
250             for ( i = 0; i < row && useCaseIt->More(); i++, useCaseIt->Next() );
251             if ( i == row && useCaseIt->More() ) {
252               objAfter = useCaseIt->Value();
253             }
254           }
255           // process all objects in a given list
256           for ( int i = 0; i < what.length(); i++ ) {
257             SALOMEDS::SObject_var sobj = what[i];
258             if ( CORBA::is_nil( sobj ) ) continue; // skip bad object
259             if ( isCopy ) {
260               // copying is performed
261               // get the name of the object
262               CORBA::String_var name = sobj->GetName();
263               // create a new object, as a child of the component object
264               SALOMEDS::SObject_var new_sobj = studyBuilder->NewObject( father );
265               new_sobj->SetAttrString( "AttributeName", name.in() );
266               sobj = new_sobj;
267               // ... perform other necessary data copying like
268               // adding the corresponding attributes or creation
269               // of servant data entities...
270             }
271             // insert the object or its copy to the use case tree
272             if ( !CORBA::is_nil( objAfter ) )
273               useCaseBuilder->InsertBefore( sobj, objAfter ); // insert at a given row
274             else
275               useCaseBuilder->AppendTo( where, sobj );        // append to the
276           end of the list
277           }
278         }
279
280
281