3 \page howtos How-To's and Best Practices
5 These documents provide guidelines and best practices explaining different aspects
6 and usage examples of SALOME platform.
8 - \subpage use_case_builder
9 - \subpage drag_and_drop
10 - \subpage using_pluginsmanager
12 \page use_case_builder Customize data tree representation in the Object browser by means of use case builder
16 In SALOME, the representation of the data tree in the Object browser for the \a full
17 (CORBA-based) module is done basing on the study contents as it is supplied by SALOME
18 data server (SALOMEDS). In contrast to the \a light module which data tree is completely
19 defined and can be easily attuned by the module specific implementation, \a full module
20 must publish its data in the CORBA study by means of the corresponding API of SALOME
21 data server, namely \c SALOMEDS::StudyBuilder. As soon as data entities are published
22 in the study, they are shown in the Object browser, in the same order as they appear
23 in the study tree. Re-arrangement of the data entities with such approach is not a
24 trivial task: for example, when copying/moving any data entity at the new position
25 within the tree, it is necessary to copy all its attributes as well
26 and to clear (in case of move operation) the data entity at the original position. Also, it is not possible to
27 make some data items in the tree "invisible" for the user (though it might be useful).
29 Use case builder provides an alternative and more flexible way for customizing the
30 data tree representation. It implements another approach to the data tree hierarchy,
31 based on the tree node attributes. With use case builder it is possible to arrange
32 and easily re-arrange the data items in the data tree in any appropriate way.
34 For example, with use case builder it is easy to implement such operations as
35 \ref drag_and_drop "Drag and Drop" and Copy/Cut/Paste. With use case builder approach
36 it is not important how data entities are arranged in the study tree, they even may
37 lie on the same level - use case builder allows providing custom data tree
38 representation, completely indepedent on the study data tree itself. It is even possible
39 to hide some data entities in the tree representation while still keeping them in the
40 study (to store specific module data).
42 Object browser automatically checks it the module root data object
43 contains a tree node attribute and switches to the browsing of the
44 data tree for such module using the use case
45 builder. Otherwise, it browses data using an ordinary study tree iterator. Thus, it is
46 possible to have in the same study some modules based on use case builder approach and
49 \section use_case_builder_usage Use case builder usage
51 To obtain a reference to the use case builder, the function \c GetUseCaseBuilder() of the
52 \c SALOMEDS::Study interface can be used:
57 // Get reference to the use case builder
58 UseCaseBuilder GetUseCaseBuilder();
62 \c SALOMEDS::UseCaseBuilder interface of the \c SALOMEDS CORBA module provides several
63 methods that can be used to build a custom data tree. Its API is
65 \c SALOMEDS::StudyBuilder interface - it operates with terms \a "father object" and
66 \a "child object". In addition, use case builder uses term \a "current object" that is
67 used as a parent of the children objects added if the parent is not explicitly
71 interface UseCaseBuilder
73 // Set top-level root object of the use case tree as the current one.
74 // This method is usually used to add SComponent items to the top level of the tree
75 boolean SetRootCurrent();
77 // Set the object theObject as the current object of the use case builder
78 boolean SetCurrentObject(in SObject theObject);
80 // Append object SObject to the end of children list of the current object
81 boolean Append(in SObject theObject);
83 // Append object SObject to the end of children list of the parent object theFather
84 boolean AppendTo(in SObject theFather, in SObject theObject);
86 // Insert object theFirst before the object theNext (under the same parent object)
87 boolean InsertBefore(in SObject theFirst, in SObject theNext);
89 // Remove object from the use case tree (without removing it from the study)
90 boolean Remove(in SObject theObject);
92 // Check if the object theObject has children (in the use case tree)
93 boolean HasChildren(in SObject theObject);
95 // Get father object of the given object theObject in the use cases tree
96 SObject GetFather(in SObject theObject);
98 // Check if given object theObject is added to the use case tree
99 boolean IsUseCaseNode(in SObject theObject);
101 // Get the current object of the use case builder
102 SObject GetCurrentObject();
106 \section browse_use_case_tree Browsing use case data tree
108 Browsing the use case tree can be done by means of the use case iterator, that is
109 provided by the \c SALOMEDS::UseCaseIterator interface of the \c SALOMEDS CORBA
110 module. Access to the use case iterator can be done via \c SALOMEDS::UseCaseBuilder
114 interface UseCaseBuilder
116 // Get a reference to the use case iterator and initialize it
117 // by the given object theObject
118 UseCaseIterator GetUseCaseIterator(in SObject theObject);
122 The API of the \c SALOMEDS::UseCaseIterator interface is similar to the
123 \c SALOMEDS::ChildIterator:
126 interface UseCaseIterator
128 // Activate or reset use case iterator; boolean parameter allLevels
129 // specifies if the iterator should browse recursively on all sub-levels or
130 // on the first sub-level only.
131 void Init(in boolean allLevels);
132 // Check if the iterator can browse to the next item
134 // Browse the iterator to the next object
136 // Get the object currently pointed by the iterator
141 Typical usage of the \c UseCaseIterator is as follows:
144 // get use case builder
145 SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder();
147 // get the use case iterator
148 SALOMEDS::UseCaseIterator_var iter = useCaseIter->GetUseCaseIterator( sobject.in() );
149 // iterate through the sub-items recursively
150 for ( useCaseIter->Init( true ); useCaseIter->More(); useCaseIter->Next() ) {
151 SALOMEDS::SObject_var child = useCaseIter->Value();
152 // do something with the child
158 useCaseIter->UnRegister();
159 useCaseBuilder->UnRegister();
162 \section use_case_compatibility Remark about compatibility with existing studies
164 If you decide to switch your module to the use case builder approach to provide
165 customization for the data tree representation, you must take care of compatibility
166 with existing SALOME studies. Basically it means that you have to add
167 a simple code to \c Load() (and \c LoadASCII() if necessary) method
168 of your module, which adds tree node attributes to all data entities
169 in the data tree of your module. The simplest way to do
170 this is to iterate through all data items and recursively add them to
171 the use case builder:
175 SALOMEDS::SComponent_var comp = study->FindComponent( "MYMODULE" );
176 // add tree node attributes only if component data is present in the study
177 if ( !CORBA::is_nil( comp ) ) {
178 // get the use case builder
179 SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder();
180 // check if tree nodes are already set
181 if ( !useCaseBuilder->IsUseCaseNode( comp.in() ) ) {
182 // set the current node of the use case builder to the root
183 useCaseBuilder->SetRootCurrent();
184 // add component item to the top level of the use case tree
185 useCaseBuilder->Append( comp.in() );
186 // iterate through all child items recursively
187 SALOMEDS::ChildIterator_var iter = study->NewChildIterator( comp.in() );
188 for ( iter->InitEx( true ); iter->More(); iter->Next() ) {
189 SALOMEDS::SObject_var sobj = iter->Value();
190 SALOMEDS::SObject_var father = sobj->GetFather();
191 // add an object to the corresponding level in the use case tree
192 useCaseBuilder->AppendTo( father.in(), sobj.in() );
193 // clean up (avoid memory leaks)
195 father->UnRegister();
198 useCaseBuilder->UnRegister(); // clean up
202 \page drag_and_drop Implementing Drag and Drop functionality in SALOME module
206 <b>Drag and Drop</b> provides a simple visual mechanism to transfer
207 information between and within applications.
209 In some aspects Drag and drop operates similarly to the clipboard copy/cut/paste
212 Since SALOME GUI is implemented on Qt, the drag and drop functionality support
213 is provided by means of the corresponding Qt mechanisms.
215 Currently dragging and dropping of the items can be done within Object browser only,
216 however this functionality can be extended to other GUI elements as well.
218 \section enable_drag_and_drop Enabling drag and drop in SALOME module
220 The Drag and drop functionality is enabled by default in the Object browser. However,
221 to allow dragging of a data object or dropping data on it, it is necessary to redefine
222 \c isDraggable() and \c isDropAccepted() methods of the corresponding class, a successor
223 of the \c SUIT_DataObject. These methods are defined in the base class \c SUIT_DataObject
224 and default implementation of both functions returns \c false, which prevents dragging and
228 bool SUIT_DataObject::isDraggable() const
233 bool SUIT_DataObject::isDropAccepted() const
239 If your data model is based on the \c SUIT_DataObject and \c SUIT_TreeModel, just
240 re-implement these functions in your successor data object class and return \c true
241 when it is needed (for example, depending on the data object type, state, etc).
243 Another alternative is available if your module is directly inherited from
244 \c LightApp_Module or \c SalomeApp_Module class (as the majority of existing SALOME modules).
245 The class \c LightApp_Module (and thus \c SalomeApp_Module also) already provides
246 high-level API that can be used for enabling drag and drop functionality.
248 To enable dragging, redefine \c isDraggable() method of your module class. In this method
249 you can analyze the data object subject to the drag operation and decide if
250 it is necessary to enable or prevent its dragging:
253 bool MyModuleGUI::isDraggable( const SUIT_DataObject* what ) const
256 const MyDataObject* obj = dynamic_cast<const MyDataObject*>( what );
258 // check if object can be dragged
259 result = <some condition>;
265 Note, that you should not invoke here method \c isDragEnabled() of your data object class
266 (in case if it inherits \c LightApp_DataObject or \c SalomeApp_DataObject), unless you
267 redefine methods \c isDraggable() and \c isDropAccepted() in your data object class.
268 The reason is that the implementation of these methods in \c LightApp_DataObject class
269 redirects calls to the \c LightApp_Module - be careful to avoid entering endless
272 To allow data dropping to an object (the object under the mouse cursor in the
273 Object browser during the drag operation) redefine \c isDropAccepted() method of your
277 bool MyModuleGUI::isDropAccepted( const SUIT_DataObject* where ) const
280 const MyDataObject* obj = dynamic_cast<const MyDataObject*>( where );
282 // check if object supports dropping
283 result = <some condition>;
289 The caution about avoiding recursive loop mentioned above is also valid for
290 \c isDropAccepted() method.
292 \section handle_data_dropping Handling data dropping
294 When dragging operation is completed (the data is dropped to an object) the module owning
295 the item on which the data is dropped is notified by invoking its \c dropObjects() method:
298 void LightApp_Module::dropObjects( const DataObjectList& what,
299 SUIT_DataObject* where,
301 Qt::DropAction action )
306 The default implementation does nothing. However, this method can be redifined in the
307 successor class and handle the operation properly. The list of dropped
308 data objects is passed via \c what parameter. The data object on which
309 the data is dropped is passed via \c where parameter. The parameter \c row specifies in the children list
310 the position of object where data is dropped; if this parameter is equal to -1, the
311 data is dropped to the end of the children list. Performed drop action is passed
312 via \c action parameter; possible values are \c Qt::CopyAction and \c Qt::MoveAction
313 (other actions are currently unsupported).
315 The method \c dropObjects() should analyze the parameters and apply
316 the corresponding actions for rearrangement of the data tree, copying or moving the data items depending on the
317 operation performed. For example:
320 void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
321 const int row, Qt::DropAction action )
323 if ( action != Qt::CopyAction && action != Qt::MoveAction )
324 return; // unsupported action
327 MyDataObject* whereObj = dynamic_cast<MyDataObject*>( where );
328 if ( !dataObj ) return; // wrong parent
330 // iterate through all objects being dropped
331 for ( int i = 0; i < what.count(); i++ ) {
332 MyDataObject* whatObj = dynamic_cast<MyDataObject*>( what[i] );
333 if ( !whatObj ) continue; // skip wrong objects
334 // perform copying or moving
335 copyOrMove( whatObj, // data object being copied/moved
336 whereObj, // target data object
337 row, // index in the target data object
338 action == Qt::CopyAction ); // true if copying is done
340 // update Object browser
341 getApp()->updateObjectBrowser( false );
345 In the above code the function \c copyOrMove() performs actual data tree rearrangement.
347 \section drag_drop_light_modules Drag and Drop in "light" modules
349 The data model of the \a light (not having CORBA engine) SALOME module is usually
350 based on the custom tree of data objects. The general approach is to
351 inherit a custom data
352 object class from the \c LightApp_DataObject and a custom data model from the
353 \c LightApp_DataModel class. The data model class is responsible for building the
354 appropriate presentation of the data tree in the Object browser.
356 Thus, the implementation of the drag and drop functionality in a \a light module (more
357 precisely, the method \a dropObjects() as described above), consists in copying data
358 entities (by creating new instances of the corresponding data object class) or moving
359 existing data objects to the new position in a tree. The Object browser will update the
360 tree representation automatically, as soon as \c updateObjectBrowser() function is called.
362 \section drag_drop_full_modules Using UseCaseBuilder for Drag and Drop handling in "full" modules
364 Drag and drop operation requires underlying data model to allow flexible re-arrangement of
365 the data entities inside the data tree. In a \a full (CORBA engine based) SALOME
366 module, which data model is usually based on the hierarchy of \c SALOMEDS::SObject entities
367 provided by the data server functionality, re-arrangement of the data
368 tree is not a trivial task.
370 However, SALOME data server (\c SALOMEDS) CORBA module proposes a mechanism that can be used
371 to customize data tree representation in a simple and flexible way -
372 \ref use_case_builder "use case builder".
374 With use case builder, the \c dropObjects() function can be easily implemented. For example:
377 // GUI part: process objects dropping
378 void MyModuleGUI::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
379 const int row, Qt::DropAction action )
381 if ( action != Qt::CopyAction && action != Qt::MoveAction )
382 return; // unsupported action
385 SalomeApp_DataObject* dataObj = dynamic_cast<SalomeApp_DataObject*>( where );
386 if ( !dataObj ) return; // wrong parent
387 _PTR(SObject) parentObj = dataObj->object();
389 // collect objects being dropped
390 MYMODULE_ORB::object_list_var objects = new MYMODULE_ORB::object_list();
391 objects->length( what.count() );
393 for ( int i = 0; i < what.count(); i++ ) {
394 dataObj = dynamic_cast<SalomeApp_DataObject*>( what[i] );
395 if ( !dataObj ) continue; // skip wrong objects
396 _PTR(SObject) sobj = dataObj->object();
397 objects[i] = _CAST(SObject, sobj)->GetSObject();
400 objects->length( count );
402 // call engine function
403 engine()->copyOrMove( objects.in(), // what
404 _CAST(SObject, parentObj)->GetSObject(), // where
406 action == Qt::CopyAction ); // isCopy
408 // update Object browser
409 getApp()->updateObjectBrowser( false );
412 // Engine part: perform actual data copying / moving
413 void MyModule::copyOrMove( const MYMODULE_ORB::object_list& what,
414 SALOMEDS::SObject_ptr where,
415 CORBA::Long row, CORBA::Boolean isCopy )
417 if ( CORBA::is_nil( where ) ) return; // bad parent
419 SALOMEDS::Study_var study = where->GetStudy(); // study
420 SALOMEDS::StudyBuilder_var studyBuilder = study->NewBuilder(); // study builder
421 SALOMEDS::UseCaseBuilder_var useCaseBuilder = study->GetUseCaseBuilder(); // use case builder
422 SALOMEDS::SComponent_var father = where->GetFatherComponent(); // father component
423 std::string dataType = father->ComponentDataType();
424 if ( dataType != "MYMODULE" ) return; // not a MYMODULE component
426 SALOMEDS::SObject_var objAfter;
427 if ( row >= 0 && useCaseBuilder->HasChildren( where ) ) {
428 // insert at a given row -> find insertion position
429 SALOMEDS::UseCaseIterator_var useCaseIt = useCaseBuilder->GetUseCaseIterator( where );
431 for ( i = 0; i < row && useCaseIt->More(); i++, useCaseIt->Next() );
432 if ( i == row && useCaseIt->More() ) {
433 objAfter = useCaseIt->Value();
436 // process all objects in a given list
437 for ( int i = 0; i < what.length(); i++ ) {
438 SALOMEDS::SObject_var sobj = what[i];
439 if ( CORBA::is_nil( sobj ) ) continue; // skip bad object
441 // copying is performed
442 // get the name of the object
443 CORBA::String_var name = sobj->GetName();
444 // create a new object, as a child of the component object
445 SALOMEDS::SObject_var new_sobj = studyBuilder->NewObject( father );
446 new_sobj->SetAttrString( "AttributeName", name.in() );
448 // ... perform other necessary data copying like
449 // adding the corresponding attributes or creation
450 // of servant data entities...
452 // insert the object or its copy to the use case tree
453 if ( !CORBA::is_nil( objAfter ) )
454 useCaseBuilder->InsertBefore( sobj, objAfter ); // insert at a given row
456 useCaseBuilder->AppendTo( where, sobj ); // append to the