3 *********************************************************
4 Implementing Drag and Drop functionality in SALOME module
5 *********************************************************
7 .. contents:: Table of Contents
9 **Drag and Drop** provides a simple visual mechanism to transfer
10 information between and within applications.
12 In some aspects Drag and drop operates similarly to the clipboard copy/cut/paste
15 Since SALOME GUI is implemented on Qt, the drag and drop functionality support
16 is provided by means of the corresponding Qt mechanisms.
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.
21 .. _enable_drag_and_drop:
23 Enabling drag and drop in SALOME module
24 =======================================
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
35 bool SUIT_DataObject::isDraggable() const
40 bool SUIT_DataObject::isDropAccepted() const
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).
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.
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:
60 bool MyModuleGUI::isDraggable( const SUIT_DataObject* what ) const
63 const MyDataObject* obj = dynamic_cast<const MyDataObject*>( what );
65 // check if object can be dragged
66 result = <some condition>;
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
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
84 bool MyModuleGUI::isDropAccepted( const SUIT_DataObject* where ) const
87 const MyDataObject* obj = dynamic_cast<const MyDataObject*>( where );
89 // check if object supports dropping
90 result = <some condition>;
95 The caution about avoiding recursive loop mentioned above is also valid for
96 **isDropAccepted()** method.
98 .. _handle_data_dropping:
100 Handling data dropping
101 ======================
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:
108 void LightApp_Module::dropObjects( const DataObjectList& what,
109 SUIT_DataObject* where,
111 Qt::DropAction action )
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).
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:
130 void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
131 const int row, Qt::DropAction action )
133 if ( action != Qt::CopyAction && action != Qt::MoveAction )
134 return; // unsupported action
137 MyDataObject* whereObj = dynamic_cast<MyDataObject*>( where );
138 if ( !dataObj ) return; // wrong parent
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
150 // update Object browser
151 getApp()->updateObjectBrowser( false );
155 In the above code the function **copyOrMove()** performs actual data tree rearrangement.
157 .. _drag_drop_light_modules:
160 Drag and Drop in "light" modules
161 ================================
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.
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.
177 .. _drag_drop_full_modules:
179 Using UseCaseBuilder for Drag and Drop handling in "full" modules
180 =================================================================
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.
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`.
192 With use case builder, the **dropObjects()** function can be easily implemented. For example:
196 // GUI part: process objects dropping
197 void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
198 const int row, Qt::DropAction action )
200 if ( action != Qt::CopyAction && action != Qt::MoveAction )
201 return; // unsupported action
204 SalomeApp_DataObject* dataObj = dynamic_cast<SalomeApp_DataObject*>( where );
205 if ( !dataObj ) return; // wrong parent
206 _PTR(SObject) parentObj = dataObj->object();
208 // collect objects being dropped
209 MYMODULE_ORB::object_list_var objects = new MYMODULE_ORB::object_list();
210 objects->length( what.count() );
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();
219 objects->length( count );
221 // call engine function
222 engine()->copyOrMove( objects.in(), // what
223 _CAST(SObject, parentObj)->GetSObject(), // where
225 action == Qt::CopyAction ); // isCopy
227 // update Object browser
228 getApp()->updateObjectBrowser( false );
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 )
236 if ( CORBA::is_nil( where ) ) return; // bad parent
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
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 );
250 for ( i = 0; i < row && useCaseIt->More(); i++, useCaseIt->Next() );
251 if ( i == row && useCaseIt->More() ) {
252 objAfter = useCaseIt->Value();
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
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() );
267 // ... perform other necessary data copying like
268 // adding the corresponding attributes or creation
269 // of servant data entities...
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
275 useCaseBuilder->AppendTo( where, sobj ); // append to the