Difference between revisions of "Going further with Dialog and Component"
SergeMoutou (Talk | contribs) m (→Using Radio buttons) |
m (→Our starting OOoBasic program) |
||
(195 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
− | In the previous chapter we use and program only two kinds of control in our dialog. We intend in this chapter to further and use a lot of | + | In the previous chapter we use and program only two kinds of control in our dialog (Text edit controls and buttons). We intend in this chapter to go further and use a lot of controls and then see how things work in this area. You cannot read this chapter before reading [[Component_and_Dialog|the previous one]], particularly because only code snippets are presented. |
We begin again with our counter. | We begin again with our counter. | ||
=Counter with Numeric Field= | =Counter with Numeric Field= | ||
− | When | + | When designing the [[Component_and_Dialog#Adding_a_Dialog_to_the_Counter|dialog working with our counter]], we take two text controls for our numeric data (as input with "setCount" button and as output with "getCount" button). Because text controls are often used our preceding example is important, but it's time now to improve our design and use a numeric field control. Because the dialog aspect is unchanged we don't provide a snapshot of the dialog. |
Have a look at <idl>com.sun.star.awt.XNumericField</idl> interface and see what can be done with this interface : only <code>getvalue</code> and <code>setValue</code> will be used in this section, but you can play with other methods. | Have a look at <idl>com.sun.star.awt.XNumericField</idl> interface and see what can be done with this interface : only <code>getvalue</code> and <code>setValue</code> will be used in this section, but you can play with other methods. | ||
− | We provide only the <code>callHandlerMethod</code> function method in the listing below (see [[Component_and_Dialog# | + | We provide only the <code>callHandlerMethod</code> function method in the listing below (see [[Component_and_Dialog#Bounding_the_Component_Methods_with_XDialogEventHandler_Interface|the previous chapter]] and also the corresponding <idl>com.sun.star.awt.XDialogEventHandler</idl> interface). The corresponding code is as follows. |
<source lang="cpp"> | <source lang="cpp"> | ||
// Listing 1 | // Listing 1 | ||
Line 36: | Line 36: | ||
} | } | ||
</source> | </source> | ||
− | + | There is no need to go into this listing in full detail here. We turn now to another interesting example of control : the radio button. | |
=Using Radio buttons= | =Using Radio buttons= | ||
− | + | An option button control <idl>com.sun.star.awt.UnoControlRadioButton</idl> is a simple switch with two states, that is selected by the user. Usually option buttons are used in groups to display several options, that the user may select. While option buttons and check boxes seem to be similar, selecting one option button deselects all the other option buttons in the same group. | |
+ | |||
+ | We want use radio button to select the increment or decrement (between 1, 5 and 10). Here is a snapshot of our dialog : | ||
[[Image:CounterDialog2.png|center|thumb|600px|Our Second Counter Dialog]] | [[Image:CounterDialog2.png|center|thumb|600px|Our Second Counter Dialog]] | ||
+ | |||
+ | where you see a decrement or increment of value five is selected. | ||
For this problem the <idl>com.sun.star.awt.XRadioButton</idl> interface is your friend. We intend to provide two solutions for this example. We begin with a solution which use only the four methods of the counter. | For this problem the <idl>com.sun.star.awt.XRadioButton</idl> interface is your friend. We intend to provide two solutions for this example. We begin with a solution which use only the four methods of the counter. | ||
Line 95: | Line 99: | ||
} | } | ||
</source> | </source> | ||
− | As you can see only <code>increment()</code> and <code>decrement()</code> methods are looking for the radiobutton which is set. No event is fired when setting or cleared a radio button in this example. But it is possible to call a method when status is changed : note in this case, you have to provide a method for every buttons. Let's us now examine this second solution | + | As you can see only <code>increment()</code> and <code>decrement()</code> methods are looking for the radiobutton which is set. The corresponding checking code is completely similar in both method, then it would be better to put it in a function. No event is fired when setting or cleared a radio button in this example. But it is possible to call a method when status is changed : note in this case, you have to provide a method for every buttons. Let's us now examine this second solution. |
− | + | ||
− | + | ||
− | =This Document Home Page= | + | ==Managing Radio Buttons Events== |
+ | Every times a user set or clear a radio button a method is fired : you have to edit the corresponding property of your option button. That means you have to manage an event when clicking with mouse button (see again [[Documentation/DevGuide/WritingUNO/Assigning_Component_Methods_to_Control_Events|Developer's Guide]]). Because there are three radio buttons we have to add three methods. We see this kind of solution could become too complex while the number of button is increasing. | ||
+ | |||
+ | The code is as follows : | ||
+ | <source lang="cpp"> | ||
+ | // C++ | ||
+ | // Listing 3 | ||
+ | sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ | ||
+ | if (MethodName.equalsAscii("foo1")){//increment | ||
+ | increment(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo2")){//decrement | ||
+ | decrement(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo3")){ //setCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | setCount((sal_Int32)xNumericField->getValue()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo4")){ //getCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | xNumericField->setValue(getCount()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo5")){ //incr/decr +/-1 | ||
+ | if (m_nDelta!=1) m_nDelta=1; | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo6")){ //incr/decr +/-5 | ||
+ | if (m_nDelta!=5) m_nDelta=5; | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo7")){ //incr/decr +/-10 | ||
+ | if (m_nDelta!=10) m_nDelta=10; | ||
+ | return sal_True; | ||
+ | } | ||
+ | return sal_False; | ||
+ | } | ||
+ | |||
+ | Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ | ||
+ | Sequence< OUString > SeqOUStr(7); | ||
+ | SeqOUStr[0]=OUString::createFromAscii("foo1"); | ||
+ | SeqOUStr[1]=OUString::createFromAscii("foo2"); | ||
+ | SeqOUStr[2]=OUString::createFromAscii("foo3"); | ||
+ | SeqOUStr[3]=OUString::createFromAscii("foo4"); | ||
+ | SeqOUStr[4]=OUString::createFromAscii("foo5"); | ||
+ | SeqOUStr[5]=OUString::createFromAscii("foo6"); | ||
+ | SeqOUStr[6]=OUString::createFromAscii("foo7"); | ||
+ | return SeqOUStr; | ||
+ | } | ||
+ | </source> | ||
+ | This code is working without reading the state of the radio button but I cannot certify it could be the case in any situation you will encounter. Note that we have already provided a code to access to the state of a radio button in the previous section. Note also <code>m_nDelta</code> is a new member value set to 1 by the constructor (corresponding to the default selected radio button). | ||
+ | |||
+ | =Using a listbox in a dialog= | ||
+ | In this section, we replace the radio button control with a list box. Have a look in <idl>com.sun.star.awt.XListBox</idl> inteface. We only use the <code>getSelectedItemPos()</code> method in the following code. | ||
+ | <source lang="cpp"> | ||
+ | // XDialogEventHandler implementation | ||
+ | // C++ | ||
+ | // Listing 4 | ||
+ | sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ | ||
+ | if (MethodName.equalsAscii("foo1")){//increment | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1")); | ||
+ | Reference< XListBox > xListBox(xControl,UNO_QUERY); | ||
+ | switch (xListBox->getSelectedItemPos()){ | ||
+ | case 0: m_nDelta=1;break; | ||
+ | case 1: m_nDelta=5;break; | ||
+ | case 2: m_nDelta=10;break; | ||
+ | } | ||
+ | increment(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo2")){//decrement | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1")); | ||
+ | Reference< XListBox > xListBox(xControl,UNO_QUERY); | ||
+ | switch (xListBox->getSelectedItemPos()){ | ||
+ | case 0: m_nDelta=1;break; | ||
+ | case 1: m_nDelta=5;break; | ||
+ | case 2: m_nDelta=10;break; | ||
+ | } | ||
+ | decrement(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo3")){ //setCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | setCount((sal_Int32)xNumericField->getValue()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("foo4")){ //getCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | xNumericField->setValue(getCount()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | return sal_False; | ||
+ | } | ||
+ | </source> | ||
+ | which yields the dialog below : | ||
+ | |||
+ | [[Image:CounterDialog3.png|center|thumb|600px|Our Third Counter Dialog]] | ||
+ | |||
+ | =Multi-Page Dialog= | ||
+ | The multi-page dialog is tackled [[Documentation/BASIC_Guide/Properties| in OOBasic Document]]. Our goal is to use a multipage dialog with our counter and see how it works. Our second page will be very similar to the simple dialog we have already encountered, and the first one manage increment step. The great difference is we have to manage more buttons, particularly the "cancel", "<<previous", "next>>" and "OK" buttons. Because I never make multi-page dialog working before writing this section, I begin with the corresponding OOBasic program. | ||
+ | {{Note|As already explained, I think it's a good practice to solve a problem in OOoBasic before solving it in C++. Before starting this document, OOBasic was already documented in [[Documentation/BASIC_Guide|a BASIC Guide]] (and also in books) but you have only few examples in the SDK to learn the corresponding C++ part. In my knowledge there is no C++/UNO book. The other great reason is the need of OOBasic GUI when constructing the dialog : and then writing a OOBasic program is only a little step.}} | ||
+ | ==OOBasic program== | ||
+ | We first give the OOBasic program. The first part after the main is corresponding to the Counter, and the second part is corresponding to the next previous (Step) management. This management is realized with two Sub : | ||
+ | *"cmdPrev_Initiated" sub, | ||
+ | *"cmdNext_Initiated" sub. | ||
+ | When you are in Step=1 the previous button is disabled, while in the Step=2 the next button has to be disabled. | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | 'Listing 5 | ||
+ | Dim Dlg As Object | ||
+ | Dim Count As integer | ||
+ | Dim IncrDecr As integer | ||
+ | |||
+ | Sub Main | ||
+ | DialogLibraries.LoadLibrary("Standard") | ||
+ | Dlg = CreateUnoDialog(DialogLibraries.Standard.Dialog2) | ||
+ | 'Because of Multi-page don't forget to manage Step : | ||
+ | Dlg.Model.Step=1 | ||
+ | Dlg.Execute() | ||
+ | Dlg.dispose() | ||
+ | End Sub | ||
+ | |||
+ | 'Our counter begins here <----------- | ||
+ | Sub Increment | ||
+ | Count = Count + IncrDecr | ||
+ | End Sub | ||
+ | |||
+ | Sub Decrement | ||
+ | Count = Count - IncrDecr | ||
+ | End Sub | ||
+ | |||
+ | Sub getCount | ||
+ | Dim oNumericField | ||
+ | oNumericField = Dlg.getControl("NumericField2") | ||
+ | oNumericField.Value = Count | ||
+ | End Sub | ||
+ | |||
+ | Sub setCount | ||
+ | Dim oNumericField | ||
+ | oNumericField = Dlg.getControl("NumericField1") | ||
+ | Count = oNumericField.Value | ||
+ | End Sub | ||
+ | ' Our Counter ends here <------------ | ||
+ | |||
+ | Sub cmdNext_Initiated | ||
+ | Dim cmdNext As Object | ||
+ | Dim cmdPrev As Object | ||
+ | cmdPrev = Dlg.getControl("cmdPrev") | ||
+ | 'inspect(cmdPrev) | ||
+ | cmdNext = Dlg.getControl("cmdNext") | ||
+ | cmdPrev.Model.Enabled = Not cmdPrev.Model.Enabled | ||
+ | cmdNext.Model.Enabled = False | ||
+ | 'if Next update IncrDecr | ||
+ | If Dlg.Model.Step = 1 Then | ||
+ | If Dlg.getControl("OptionButton1").State Then | ||
+ | IncrDecr = 1 | ||
+ | End If | ||
+ | If Dlg.getControl("OptionButton2").State Then | ||
+ | IncrDecr = 5 | ||
+ | End If | ||
+ | If Dlg.getControl("OptionButton3").State Then | ||
+ | IncrDecr = 10 | ||
+ | End If | ||
+ | End If | ||
+ | Dlg.Model.Step = Dlg.Model.Step + 1 | ||
+ | End Sub | ||
+ | |||
+ | Sub cmdPrev_Initiated | ||
+ | Dim cmdNext As Object | ||
+ | Dim cmdPrev As Object | ||
+ | cmdPrev = Dlg.getControl("cmdPrev") | ||
+ | cmdNext = Dlg.getControl("cmdNext") | ||
+ | cmdPrev.Model.Enabled = False | ||
+ | cmdNext.Model.Enabled = True | ||
+ | Dlg.Model.Step = Dlg.Model.Step - 1 | ||
+ | End Sub | ||
+ | </syntaxhighlight> | ||
+ | Note also that the radio buttons are checked only when clicking on the Next button in Step 1. The dialog then changes its appearance as follows: | ||
+ | |||
+ | [[Image:CounterDialogTAB1.png|center|thumb|600px|Page 1 (Step 1 in OOBasic code)]] | ||
+ | where you see "<<Previous" button disabled and | ||
+ | [[Image:CounterDialogTAB2.png|center|thumb|600px|Page 2 (Step 2 in OOBasic code)]] | ||
+ | where you see "Next>>" button is disabled. | ||
+ | |||
+ | ==C++ program== | ||
+ | Our goal in this section is to reuse the previous multi-page dialog but with C++. It was easy for me to manage the Next Previous buttons in OOBasic but spend many hours for translating the simple OOBasic line : | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | Dlg.Model.Step = Dlg.Model.Step + 1 | ||
+ | </syntaxhighlight> | ||
+ | Finally [[Object Inspector|Java Inspector]] C++ automatic code generation shows me the way. The code is as follows : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // C++ | ||
+ | // Listing 6 | ||
+ | // XDialogEventHandler implementation | ||
+ | sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ | ||
+ | if (MethodName.equalsAscii("increment")){//increment | ||
+ | increment(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("decrement")){//decrement | ||
+ | decrement(); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("setCount")){ //setCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | setCount((sal_Int32)xNumericField->getValue()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("getCount")){ //getCount | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); | ||
+ | Reference< XNumericField > xNumericField(xControl,UNO_QUERY); | ||
+ | xNumericField->setValue(getCount()); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("cmdPrev_Initiated")){ //<<Previous | ||
+ | // xDialog is a parameter | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev")); | ||
+ | //com.sun.star.awt.XConrolModel | ||
+ | Reference< XControlModel> xControlModel= xControl->getModel(); | ||
+ | Any any; | ||
+ | sal_Bool Boolval; | ||
+ | Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); | ||
+ | any=xPropertySet->getPropertyValue(OUString::createFromAscii("Enabled")); | ||
+ | any >>= Boolval; | ||
+ | Boolval = !Boolval; | ||
+ | any <<= Boolval; | ||
+ | xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any); | ||
+ | |||
+ | xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext")); | ||
+ | xControlModel= xControl->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY); | ||
+ | any=xPropertySet2->getPropertyValue(OUString::createFromAscii("Enabled")); | ||
+ | any >>= Boolval; | ||
+ | Boolval = !Boolval; | ||
+ | any <<= Boolval; | ||
+ | xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any); | ||
+ | |||
+ | // found with JavaInspector C++ code generation :Step property management | ||
+ | sal_Int32 step; | ||
+ | Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW); | ||
+ | xControlModel= xControl2->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY); | ||
+ | step = 1; | ||
+ | any <<= step; | ||
+ | xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any); | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("cmdNext_Initiated")){ //Next>> | ||
+ | // xDialog is a parameter | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev")); | ||
+ | //com.sun.star.awt.XConrolModel | ||
+ | Reference< XControlModel> xControlModel= xControl->getModel(); | ||
+ | Any any; | ||
+ | sal_Bool Boolval; | ||
+ | Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); | ||
+ | any <<= sal_True; | ||
+ | xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any); | ||
+ | |||
+ | xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext")); | ||
+ | xControlModel= xControl->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY); | ||
+ | any <<= sal_False; | ||
+ | xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any); | ||
+ | // RadioButton management : not great with such if ! | ||
+ | xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1")); | ||
+ | Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY); | ||
+ | if (xRadioButton->getState()) m_nDelta=1; | ||
+ | xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2")); | ||
+ | Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY); | ||
+ | if (xRadioButton2->getState()) m_nDelta=5; | ||
+ | xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3")); | ||
+ | Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY); | ||
+ | if (xRadioButton3->getState()) m_nDelta=10; | ||
+ | // found with JavaInspector C++ code generation | ||
+ | sal_Int32 step; | ||
+ | // the trick is here : not line below | ||
+ | // Reference< XControl >xControl2=xControlContainer->getControl(OUString::createFromAscii("Dialog1")) | ||
+ | Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW); | ||
+ | xControlModel= xControl2->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY); | ||
+ | step = 2; | ||
+ | any <<= step; | ||
+ | xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any); | ||
+ | return sal_True; | ||
+ | } | ||
+ | return sal_False; | ||
+ | } | ||
+ | Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ | ||
+ | Sequence< OUString > SeqOUStr(6); | ||
+ | SeqOUStr[0]=OUString::createFromAscii("increment"); | ||
+ | SeqOUStr[1]=OUString::createFromAscii("decrement"); | ||
+ | SeqOUStr[2]=OUString::createFromAscii("setCount"); | ||
+ | SeqOUStr[3]=OUString::createFromAscii("getCount"); | ||
+ | SeqOUStr[4]=OUString::createFromAscii("cmdNext_Initiated"); | ||
+ | SeqOUStr[5]=OUString::createFromAscii("cmdPrev_Initiated"); | ||
+ | return SeqOUStr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | See <idl>com.sun.star.awt.XControl</idl>, <idl>com.sun.star.awt.XControlContainer</idl>, <idl>com.sun.star.awt.XControlModel</idl> and <idl>com.sun.star.beans.XPropertySet</idl>, Interfaces. | ||
+ | |||
+ | We turn now to other examples where we leave our Counter only because it's too simple. | ||
+ | |||
+ | = The New Tree Control= | ||
+ | The New Tree Contol [[Treecontrol|is tackled here]] but the corresponding article is under construction (since 2006). Frankly speaking I am not completly satisfied with this control at the moment for different reasons : | ||
+ | *the tree expanded listener is useless if your node has no child (not fired). This raises probably problems when wanting to manage a [[Going_further_with_Dialog_and_Component#Dynamic_Trees|dynamic tree]] (for instance for an introspection tool) | ||
+ | *The event property in OpenOffice.org dialog editor is too simple and is unable to replace event listener. For instance there is no event to manage a selection change as in Radio Button. | ||
+ | You probably wonder why I take time to write a documentation in such a situation. It's only because I think if the documentation lack nobody will use this control and then nobody will improve the code of this control. And also, I finally achieve to make [[Going_further_with_Dialog_and_Component#Dynamic_Trees|dynamic trees]] working with only adding a button (without event listener and without an expanding event). I by-pass then both drawbacks previously evocated. | ||
+ | |||
+ | The most important concept underlying the Tree Control is that of complete separation between data and how that | ||
+ | data is displayed on the screen. This is commonly known as Model/View/Controller-design (MVC) | ||
+ | *<idl>com.sun.star.awt.tree.XMutableTreeDataModel</idl> and <idl>com.sun.star.awt.tree.XMutableTreeNode</idl> interfaces are related to the model, | ||
+ | *<idl>com.sun.star.awt.tree.XTreeControl</idl> is related to the control and view. | ||
+ | |||
+ | ==Our starting OOoBasic program== | ||
+ | rvc44 posted in [https://web.archive.org/web/20130430112424/http://www.oooforum.org/forum/viewtopic.phtml?t=67005&highlight= OOOForum (Thu Dec 20, 2007)] an example in OOoBasic which serves me as starting point. I have added event listener and obtain the code snippet as follows : | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | 'Listing 7 | ||
+ | Option Explicit | ||
+ | 'Most of this code belongs to rvc44 http://www.oooforum.org/forum/viewtopic.phtml?t=67005&highlight= | ||
+ | Private oDlg as Object, oLbDescription as Object | ||
+ | Private oTreeCtrl as Object, oTreeModel as Object | ||
+ | Private oMutableTreeDataModel as Object | ||
+ | Private oRootNode as Object, oChildNode as Object, ogNode as Object | ||
+ | |||
+ | |||
+ | Sub mainTree '[Simple example: collapsing and expanding folders] | ||
+ | DialogLibraries.loadLibrary("Standard") | ||
+ | |||
+ | 'create the dialog | ||
+ | oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog) | ||
+ | If IsNull(oDlg) Then Exit Sub | ||
+ | |||
+ | 'get a refrence to the control/s and its model/s | ||
+ | oLbDescription = oDlg.getControl("lbDescription") | ||
+ | oLbDescription.setText( "A very simple tree control with listeners." & _ | ||
+ | chr(13) & chr(13) &_ | ||
+ | "Expanding and collapsing happends automatically" ) | ||
+ | |||
+ | oTreeCtrl = oDlg.getControl("TreeControl1") | ||
+ | oTreeModel = oTreeCtrl.Model | ||
+ | |||
+ | 'XRay oTreeModel | ||
+ | 'instantiate the MutableTreeDataModel service | ||
+ | oMutableTreeDataModel = createUnoService(_ | ||
+ | "com.sun.star.awt.tree.MutableTreeDataModel") | ||
+ | 'XRay oMutableTreeDataModel | ||
+ | 'MutableTreeDataModel implemets XMutableTreeDataModel | ||
+ | 'use XMutableTreeNode::createNode( | ||
+ | ' [in] any DisplayValue, | ||
+ | ' [in] boolean ChildsOnDemand ) | ||
+ | 'to create the root node | ||
+ | 'If you pass to the boolean parameter TRUE as a argument, | ||
+ | 'the created node will be treated as a non-leaf (branch) node | ||
+ | 'by the XTreeControl , even when it has no child nodes. | ||
+ | 'If false, the node will be a leaf. | ||
+ | 'The root should obviously be a non-leaf node. | ||
+ | oRootNode = oMutableTreeDataModel.createNode( "Root", true ) | ||
+ | 'use XMutableTreeNode::setRoot( [in] XMutableTreeNode RootNode ) | ||
+ | 'to set this node as the root of the model | ||
+ | oMutableTreeDataModel.setRoot(oRootNode) | ||
+ | |||
+ | 'now create the children of the root | ||
+ | |||
+ | Dim oChildNode1 | ||
+ | oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true ) | ||
+ | oRootNode.appendChild(oChildNode1) | ||
+ | 'Create this child's own children | ||
+ | 'In two steps: | ||
+ | Dim oSubChildNode | ||
+ | oSubChildNode = oMutableTreeDataModel.createNode(_ | ||
+ | "Child 1", true ) | ||
+ | oChildNode1.appendChild(oSubChildNode) | ||
+ | 'In only one: | ||
+ | 'Go deeper in the hierarchical structure | ||
+ | oSubChildNode.appendChild( _ | ||
+ | oMutableTreeDataModel.createNode( _ | ||
+ | "Grandson 1", false ) ) | ||
+ | oSubChildNode.appendChild( _ | ||
+ | oMutableTreeDataModel.createNode( _ | ||
+ | "Grandson 2", false ) ) | ||
+ | |||
+ | 'In only one: | ||
+ | oChildNode1.appendChild( oMutableTreeDataModel.createNode( _ | ||
+ | "Child 2", true ) ) | ||
+ | |||
+ | '======================================================================= | ||
+ | 'As you see, the Data Model always creates the nodes | ||
+ | '(XMutableDataModel::createNode) | ||
+ | 'and then you add it to the node you want, taking care that | ||
+ | 'the node created and the parent node belong to the same data model | ||
+ | |||
+ | Dim oChildNode2 | ||
+ | oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true ) | ||
+ | oRootNode.appendChild(oChildNode2) | ||
+ | |||
+ | Dim oChildNode3 | ||
+ | oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE ) | ||
+ | 'oChildNode3.setCollapsedGraphicURL( ANY_DOC ) | ||
+ | 'oChildNode3.setExpandedGraphicURL( ANY_DOC ) | ||
+ | 'oChildNode3.setNodeGraphicURL( BASIC_BRAKE ) | ||
+ | oRootNode.appendChild(oChildNode3) | ||
+ | |||
+ | Dim oChildNode4 | ||
+ | oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true ) | ||
+ | oRootNode.appendChild(oChildNode4) | ||
+ | |||
+ | Dim oChildNode5 | ||
+ | oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true ) | ||
+ | oRootNode.appendChild(oChildNode5) | ||
+ | |||
+ | Dim oChildNode6 | ||
+ | oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE ) | ||
+ | oRootNode.appendChild(oChildNode6) | ||
+ | 'Although the API refrence states the following: | ||
+ | ' "If you want to add child nodes to your tree on demand | ||
+ | ' you can .[..] Make sure the parent node returns true | ||
+ | ' for XTreeNode::hasChildsOnDemand() either by implementing | ||
+ | ' XTreeNode yourself or, if you use the MutableTreeDataModel , | ||
+ | ' use XMutableTreeNode::setHasChildsOnDemand() ." | ||
+ | 'https://www.openoffice.org/api/docs/common/ref/com/sun/star/awt/tree/TreeControl.html | ||
+ | Dim bHasChildernOnDemand as Boolean | ||
+ | 'in this case hasChildsOnDemand returns FALSE | ||
+ | 'bHasChildernOnDemand = oChildNode6.hasChildsOnDemand() | ||
+ | Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE ) | ||
+ | 'BUT the node is appended to this node that has no children on demand | ||
+ | oChildNode6.appendChild(aChild) | ||
+ | 'Should it throw an exception? | ||
+ | |||
+ | 'set the data model at the TreeControlModel::DataModel property. | ||
+ | oTreeModel.DataModel = oMutableTreeDataModel | ||
+ | |||
+ | ' oTreeCtrl.DefaultExpandedGraphicURL = FOLDER_OPEN | ||
+ | 'oTreeCtrl.DefaultCollapsedGraphicURL = FOLDER_CLOSED | ||
+ | |||
+ | Dim oListener,oListener2 | ||
+ | oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener") | ||
+ | oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener") | ||
+ | 'Xray oListener2 | ||
+ | oTreeCtrl.addTreeExpansionListener(oListener) | ||
+ | oTreeCtrl.addSelectionChangeListener(oListener2) | ||
+ | oDlg.execute() | ||
+ | oTreeCtrl.removeSelectionChangeListener(oListener2) | ||
+ | oTreeCtrl.removeTreeExpansionListener(oListener) | ||
+ | oDlg.dispose() | ||
+ | End Sub | ||
+ | |||
+ | Sub Tree_treeExpanding(oEvt) | ||
+ | 'oEvt.Node has methods : | ||
+ | 'getChildAt Returns the child tree node at Index . | ||
+ | 'getChildCount Returns the number of child nodes. | ||
+ | 'getParent Returns the parent node of this node. | ||
+ | 'getIndex Returns the index of Node in this instances children. | ||
+ | 'hasChildsOnDemand Returns true if the children of this node are created on demand. | ||
+ | 'getDisplayValue If not empty, the textual representation of this any is used as the text part of this node. | ||
+ | 'getNodeGraphicURL The URL for a graphic that is rendered before the text part of this node. | ||
+ | 'getExpandedGraphicURL The URL for a graphic that is rendered to visualize expanded non leaf nodes. | ||
+ | 'getCollapsedGraphicURL The URL for a graphic that is rendered to visualize collapsed non leaf nodes. | ||
+ | MsgBox("Node : " & oEvt.Node.getDisplayValue &" expanding") | ||
+ | ogNode = oEvt.Node | ||
+ | end sub | ||
+ | Sub Tree_treeExpanded(oEvt) | ||
+ | ' MsgBox("Tree expanded") | ||
+ | end sub | ||
+ | Sub Tree_treeCollapsing(oEvt) | ||
+ | 'MsgBox("Tree collapsing") | ||
+ | end sub | ||
+ | Sub Tree_treeCollapsed(oEvt) | ||
+ | ' MsgBox("Tree collapsed") | ||
+ | end sub | ||
+ | Sub Tree_requestChildNodes(oEvt) | ||
+ | 'print oEvt.Node.getIndex(oEvt.Node) | ||
+ | 'XRay oEvt.Node | ||
+ | ' Msgbox ("Tree_requestChildNodes") | ||
+ | End Sub | ||
+ | Sub Tree_disposing(oEvt) | ||
+ | ' Msgbox ("Tree_disposing") | ||
+ | End Sub | ||
+ | Sub Select_selectionChanged(oEvt) | ||
+ | MsgBox("Selection Changed") | ||
+ | end sub | ||
+ | </syntaxhighlight> | ||
+ | Here is the corresponding snapshot | ||
+ | [[Image:ComposantDialogTree.png|center|thumb|600px|Our tree control with treeExpanding listener messageBox]] | ||
+ | where you see a "treeExpanding" listener at work, and the second with another listener ("selection change") at work. | ||
+ | [[Image:ComposantDialogTree1.png|center|thumb|600px|Our tree control with selectionChanged listener messageBox]] | ||
+ | If you want to construct such a tree with another programming language, you have to investigate the corresponding interfaces : | ||
+ | * <idl>com.sun.star.awt.tree.XMutableTreeDataModel</idl> with <code>createNode</code> and <code>setRoot</code> methods, | ||
+ | * <idl>com.sun.star.awt.tree.XMutableTreeNode</idl> with <code>appendChild</code> method, | ||
+ | * <idl>com.sun.star.awt.tree.XTreeControl</idl> -|> <idl>com.sun.star.view.XMultiSelectionSupplier</idl> -|> <idl>com.sun.star.view.XSelectionSupplier</idl> with <code>getSelection</code> method (-|> is a simplified inheritance notation). | ||
+ | It's time to examine the C++ translation of this example. | ||
+ | |||
+ | ==C++ Code without Listener== | ||
+ | As usual we plan to use the previous OOoBasic dialog but with C++ code. Because we only want a demonstration we will simplify the Tree. Our goal is to retrieve the expanded node but as mentioned in the section title we don't want to use listener that means we want only to manage events of the tree dialog control with our member functions. | ||
+ | We will investigate first with OOoBasic. | ||
+ | ===Managing Events of Tree Control with OOoBasic=== | ||
+ | Our goal is simple : retrieve the selected node of the tree. We first remove all the event listener of the OOoBasic code : | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | 'Listing 8 | ||
+ | ... | ||
+ | |||
+ | Sub mainTree '[Simple example: collapsing and expanding folders] | ||
+ | |||
+ | ..... | ||
+ | 'Dim oListener,oListener2 | ||
+ | 'oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener") | ||
+ | 'oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener") | ||
+ | 'Xray oListener2 | ||
+ | 'oTreeCtrl.addTreeExpansionListener(oListener) | ||
+ | 'oTreeCtrl.addSelectionChangeListener(oListener2) | ||
+ | oDlg.execute() | ||
+ | 'oTreeCtrl.removeSelectionChangeListener(oListener2) | ||
+ | 'oTreeCtrl.removeTreeExpansionListener(oListener) | ||
+ | oDlg.dispose() | ||
+ | End Sub | ||
+ | </syntaxhighlight> | ||
+ | We choose among the events shown below | ||
+ | |||
+ | [[Image:EventDialogTree.png|center|thumb|600px|The manageable Events of a Tree Control]] | ||
+ | |||
+ | where you can see the Losing Focus Event ("perte de focus" in French) is used with the sub : | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | 'Listing 9 | ||
+ | Sub LosingFocus(Event As Object) | ||
+ | 'inspect(oTreeCtrl) | ||
+ | 'getSelection is member function of com.sun.star.awt.tree.XTreeControl interface | ||
+ | 'getDisplayValue is member function of com.sun.star.awt.tree.XTreeNode interface | ||
+ | MsgBox(oTreeCtrl.getSelection.getDisplayValue()) | ||
+ | End Sub | ||
+ | </syntaxhighlight> | ||
+ | When expanding the tree and selecting a node, you only click in the Text control to see the result | ||
+ | [[Image:EventDialogTree1.png|center|thumb|600px|Retrieving the Name of the selected Node]] | ||
+ | where you see the LosingFocus Sub above is fired. | ||
+ | |||
+ | ===Managing Events of Tree Control with a C++ member function=== | ||
+ | We change the preceding OOoBasic example in four ways : | ||
+ | # we simplify the tree to be displayed, | ||
+ | # we display the selection in the already present control text instead of in a message Box, | ||
+ | # because we are dealing with components (and not addon) we have to start from an IDL file. I have keept the counter IDL file even if my new Dialog is unable to make it working. | ||
+ | # the "losing focus" event property is bound to a component method called "LosingFocus" (see [[Documentation/DevGuide/WritingUNO/Assigning_Component_Methods_to_Control_Events|Developper's Guide]]). | ||
+ | |||
+ | ====The new Constructor==== | ||
+ | The constructor has to display the tree in the corresponding control. To say it differently we shall find almost of the [[Going_further_with_Dialog_and_Component#Our_starting_OOoBasic_program|OOoBasic "mainTree"]] sub of the previous example in this constructor. The code is then as follows : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // cpp | ||
+ | // Listing 10 | ||
+ | inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw () | ||
+ | : m_xContext( xContext ) { | ||
+ | m_xMCF=m_xContext->getServiceManager(); | ||
+ | Reference< XDesktop > xDesktop( | ||
+ | m_xMCF->createInstanceWithContext( | ||
+ | OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ), | ||
+ | xContext ), UNO_QUERY_THROW ); | ||
+ | Reference< XComponent > xcomponent = xDesktop->getCurrentComponent(); | ||
+ | Sequence< Any> Args(1); | ||
+ | Args[0] <<= xcomponent; | ||
+ | Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithArgumentsAndContext( | ||
+ | OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), | ||
+ | Args, | ||
+ | xContext), | ||
+ | UNO_QUERY_THROW ); | ||
+ | // Uncomment below and comment above if you want the OOoDesktop as parent of your dialog | ||
+ | // Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithContext( | ||
+ | // OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), | ||
+ | // xContext), | ||
+ | // UNO_QUERY_THROW ); | ||
+ | Reference< XDialog > xDialog=xDialog2->createDialogWithHandler( | ||
+ | OUString::createFromAscii( "vnd.sun.star.script:Standard.SimpleTreeDialog?location=application" ), | ||
+ | (XCountable *)this); | ||
+ | // Constructing the Tree (17th Jul 2009) | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); | ||
+ | Reference< XMutableTreeDataModel > xTreeMutDatMod (m_xMCF->createInstanceWithContext( | ||
+ | OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),xContext),UNO_QUERY_THROW); | ||
+ | // Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office | ||
+ | Any any; | ||
+ | any <<= OUString::createFromAscii("root"); | ||
+ | Reference< XMutableTreeNode > xMTN=xTreeMutDatMod->createNode(any,sal_True); | ||
+ | xTreeMutDatMod->setRoot(xMTN); | ||
+ | any <<= OUString::createFromAscii("Parent 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode = xTreeMutDatMod->createNode(any,sal_True); | ||
+ | xMTN->appendChild(xChildNode); | ||
+ | any <<= OUString::createFromAscii("Child 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode2 = xTreeMutDatMod->createNode(any,sal_True); | ||
+ | xChildNode->appendChild(xChildNode2); | ||
+ | any <<= OUString::createFromAscii("Grandson 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode3 = xTreeMutDatMod->createNode(any,sal_False); | ||
+ | xChildNode2->appendChild(xChildNode3); | ||
+ | any <<= OUString::createFromAscii("Grandson 2"); | ||
+ | xChildNode3 = xTreeMutDatMod->createNode(any,sal_False); | ||
+ | xChildNode2->appendChild(xChildNode3); | ||
+ | any <<= OUString::createFromAscii("Child 2"); | ||
+ | xChildNode2 = xTreeMutDatMod->createNode(any,sal_True); | ||
+ | xChildNode->appendChild(xChildNode2); | ||
+ | any <<= OUString::createFromAscii("Parent 2"); | ||
+ | xChildNode=xTreeMutDatMod->createNode(any,sal_True); | ||
+ | xMTN->appendChild(xChildNode); | ||
+ | // set the model to see the tree in control (two hours to find the four lines below) | ||
+ | // translation of oTreeModel = oTreeCtrl.Model and oTreeModel.DataModel = oMutableTreeDataModel | ||
+ | Reference< XControlModel > xControlModel=xControl->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); | ||
+ | any <<= xTreeMutDatMod; | ||
+ | xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any); | ||
+ | // execute dialog | ||
+ | xDialog->execute(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ====The "LosingFocus" method==== | ||
+ | The LosingFocus sub in OOoBasic was very simple. I have translated it with Introspection tools and IDL documentation like <idl>com.sun.star.awt.tree.XTreeControl</idl>, <idl>com.sun.star.awt.tree.XTreeNode</idl> and <idl>com.sun.star.awt.XTextComponent</idl>. | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // C++ | ||
+ | // Listing 11 | ||
+ | sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ | ||
+ | if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); | ||
+ | // com.sun.star.awt.tree.XTreeControl | ||
+ | Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY); | ||
+ | Any any=xTreeControl->getSelection(); | ||
+ | Reference< XTreeNode > xTreeNode; | ||
+ | any >>= xTreeNode; | ||
+ | // put the result of selection in text control | ||
+ | xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription")); | ||
+ | Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY); | ||
+ | any = xTreeNode->getDisplayValue(); | ||
+ | OUString OUStr; | ||
+ | any >>= OUStr; | ||
+ | xTextComponent->setText(OUStr + OUString::createFromAscii(" -- DONE --")); | ||
+ | return sal_True; | ||
+ | } | ||
+ | return sal_False; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | This method is called when losing focus : click in the text control, for instance. | ||
+ | |||
+ | Here is a snapshot of the corresponding dialog : | ||
+ | [[Image:EventDialogTree2.png|center|thumb|600px|The new Tree Control at work]] | ||
+ | Because only a method is used, you have also to change the <code>getSupportedMethodNames()</code> member function : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // C++ | ||
+ | // Listing 12 | ||
+ | Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ | ||
+ | Sequence< OUString > SeqOUStr(1); | ||
+ | SeqOUStr[0]=OUString::createFromAscii("LosingFocus"); | ||
+ | // SeqOUStr[1]=OUString::createFromAscii("foo2"); | ||
+ | // SeqOUStr[2]=OUString::createFromAscii("foo3"); | ||
+ | // SeqOUStr[3]=OUString::createFromAscii("foo4"); | ||
+ | return SeqOUStr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{Warn|I am not sure <code>xml2cmp</code> tools which proceeds the <code>CppComponent.uno.xml</code> file is able to work with a type as follows : | ||
+ | <syntaxhighlight lang="xml"> | ||
+ | <type>com.sun.star.awt.tree.XTreeControl</type> | ||
+ | </syntaxhighlight> | ||
+ | In this type, there is four level (com/sun/star/tree) before the module name, instead of usually three. Is the problem here ? | ||
+ | |||
+ | Have added it to MakeFile to make it working. If you confirm or infirm that, please contact me.[[User:SergeMoutou|SergeMoutou]] 16:30, 17 July 2009 (UTC)}} | ||
+ | |||
+ | ==C++ code with listener== | ||
+ | Altough I like event listener in OOoBasic I dislike them in C++ : they are easy to use in OOoBasic but hard in C++. Then I don't know when I will write such a documentation. | ||
+ | |||
+ | TO DO | ||
+ | |||
+ | ==Dynamic Trees== | ||
+ | In this section, we want to Investigate how to manage a dynamic tree. A dynamic tree is a tree that is constructed on the demand. | ||
+ | ===Dynamic Trees in OOoBasic=== | ||
+ | In fact I begun to write the next section first. Because I encounter some problem I decided to write this section. After many hours I was not able to make dynamic tree working in C++ while it takes me only few minutes to make it working in OOoBasic ! FYI : dynamic Trees in C++ [[Going_further_with_Dialog_and_Component#Dynamic_Trees_in_C.2B.2B|in the next section]] are working now. | ||
+ | |||
+ | I keep the [[Going_further_with_Dialog_and_Component#Our_starting_OOoBasic_program|initial OOoBasic tree]] and add two buttons : "OK" button and "add Child" button. You select a node and when clicking on "add Child" button a child is added. Its name is the name of the selected node followed with ".n" where n is the number of the child. This is a dynamic tree because its size is not fixed. Its code is as follows: | ||
+ | <syntaxhighlight lang="oobas"> | ||
+ | REM ***** BASIC ***** | ||
+ | 'Listing 14 | ||
+ | Option Explicit | ||
+ | |||
+ | Private oDlg as Object, oLbDescription as Object | ||
+ | Private oTreeCtrl as Object, oTreeModel as Object | ||
+ | Private oMutableTreeDataModel as Object | ||
+ | Private oRootNode as Object, oChildNode as Object, ogNode as Object | ||
+ | |||
+ | |||
+ | Sub mainTree '[Simple example: collapsing and expanding folders] | ||
+ | DialogLibraries.loadLibrary("Standard") | ||
+ | |||
+ | oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog) | ||
+ | If IsNull(oDlg) Then Exit Sub | ||
+ | |||
+ | oTreeCtrl = oDlg.getControl("TreeControl1") | ||
+ | oTreeModel = oTreeCtrl.Model | ||
+ | |||
+ | oMutableTreeDataModel = createUnoService(_ | ||
+ | "com.sun.star.awt.tree.MutableTreeDataModel") | ||
+ | |||
+ | oRootNode = oMutableTreeDataModel.createNode( "Root", true ) | ||
+ | |||
+ | oMutableTreeDataModel.setRoot(oRootNode) | ||
+ | |||
+ | Dim oChildNode1 | ||
+ | oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true ) | ||
+ | oRootNode.appendChild(oChildNode1) | ||
+ | 'Create this child's own children | ||
+ | 'In two steps: | ||
+ | Dim oSubChildNode | ||
+ | oSubChildNode = oMutableTreeDataModel.createNode(_ | ||
+ | "Child 1", true ) | ||
+ | oChildNode1.appendChild(oSubChildNode) | ||
+ | 'In only one: | ||
+ | 'Go deeper in the hierarchical structure | ||
+ | oSubChildNode.appendChild( _ | ||
+ | oMutableTreeDataModel.createNode( _ | ||
+ | "Grandson 1", false ) ) | ||
+ | oSubChildNode.appendChild( _ | ||
+ | oMutableTreeDataModel.createNode( _ | ||
+ | "Grandson 2", false ) ) | ||
+ | |||
+ | 'In only one: | ||
+ | oChildNode1.appendChild( oMutableTreeDataModel.createNode( _ | ||
+ | "Child 2", true ) ) | ||
+ | |||
+ | Dim oChildNode2 | ||
+ | oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true ) | ||
+ | oRootNode.appendChild(oChildNode2) | ||
+ | |||
+ | Dim oChildNode3 | ||
+ | oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE ) | ||
+ | oRootNode.appendChild(oChildNode3) | ||
+ | |||
+ | Dim oChildNode4 | ||
+ | oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true ) | ||
+ | oRootNode.appendChild(oChildNode4) | ||
+ | |||
+ | Dim oChildNode5 | ||
+ | oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true ) | ||
+ | oRootNode.appendChild(oChildNode5) | ||
+ | |||
+ | Dim oChildNode6 | ||
+ | oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE ) | ||
+ | oRootNode.appendChild(oChildNode6) | ||
+ | Dim bHasChildernOnDemand as Boolean | ||
+ | Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE ) | ||
+ | oChildNode6.appendChild(aChild) | ||
+ | |||
+ | oTreeModel.DataModel = oMutableTreeDataModel | ||
+ | |||
+ | oDlg.execute() | ||
+ | oDlg.dispose() | ||
+ | End Sub | ||
+ | |||
+ | Sub addChild(Event As Object) | ||
+ | Dim oParentNode, oChildNode | ||
+ | Dim nb as long | ||
+ | Dim oChildName as String | ||
+ | oParentNode = oTreeCtrl.getSelection() | ||
+ | nb = oParentNode.getChildCount() | ||
+ | nb=nb+1 'I want numerotation begins with 1 | ||
+ | oChildName = oParentNode.getDisplayValue() + "." + nb | ||
+ | oChildNode = oMutableTreeDataModel.createNode(oChildName , TRUE ) | ||
+ | oParentNode.appendChild(oChildNode) | ||
+ | oLbDescription = oDlg.getControl("lbDescription") | ||
+ | oLbDescription.setText(oChildName) | ||
+ | End Sub | ||
+ | </syntaxhighlight> | ||
+ | The sub "addChild" is bound with the corresponding button. Here is a snapshot of a result: | ||
+ | [[Image:EventDialogTree4.png|center|thumb|600px|Dynamic Tree in a Tree Control (OOoBasic)]] | ||
+ | Question : what is the last created node ? Answer see the Text control : "Root.7.1.2". | ||
+ | |||
+ | Let's turn to the corresponding C++ example. | ||
+ | |||
+ | ===Dynamic Trees in C++=== | ||
+ | I start from the previous [[Going_further_with_Dialog_and_Component#Managing_Events_of_Tree_Control_with_a_C.2B.2B_member_function|tree (C++)]] with the same functioning as in previous section. When a node is selected, "add Child" button creates a child with name nodename.i where i is the number of the child and nodename the name of the node. For debuging intention, I put also the name of the added node in the text control. | ||
+ | ====Our new constructor==== | ||
+ | The [[Going_further_with_Dialog_and_Component#The_new_Constructor|constructor]] of previous section is slighty modified. Here is the corresponding code : | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // c++ | ||
+ | // Listing 15 | ||
+ | // added this constructor 06/06/09 | ||
+ | inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw () | ||
+ | : m_xContext( xContext ) { | ||
+ | m_xMCF=m_xContext->getServiceManager(); | ||
+ | // Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office : see below | ||
+ | Reference< XMutableTreeDataModel > xMutTreeDatMod(m_xMCF->createInstanceWithContext( | ||
+ | OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),m_xContext),UNO_QUERY_THROW); | ||
+ | m_xMutTreeDatMod = xMutTreeDatMod; | ||
+ | // If you want to use java Inspector | ||
+ | Reference< XInstanceInspector > xInspector(m_xMCF->createInstanceWithContext( | ||
+ | OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.InstanceInspector" ) ), | ||
+ | m_xContext), | ||
+ | UNO_QUERY_THROW ); | ||
+ | m_xInspector = xInspector; | ||
+ | Reference< XDesktop > xDesktop( | ||
+ | m_xMCF->createInstanceWithContext( | ||
+ | OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ), | ||
+ | xContext ), UNO_QUERY_THROW ); | ||
+ | Reference< XComponent > xcomponent = xDesktop->getCurrentComponent(); | ||
+ | Sequence< Any> Args(1); | ||
+ | Args[0] <<= xcomponent; | ||
+ | Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithArgumentsAndContext( | ||
+ | OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), | ||
+ | Args, | ||
+ | xContext), | ||
+ | UNO_QUERY_THROW ); | ||
+ | // Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithContext( | ||
+ | // OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), | ||
+ | // xContext), | ||
+ | // UNO_QUERY_THROW ); | ||
+ | Reference< XDialog > xDialog=xDialog2->createDialogWithHandler( | ||
+ | OUString::createFromAscii( "vnd.sun.star.script:Standard.SimpleTreeDialog?location=application" ), | ||
+ | (XCountable *)this); | ||
+ | // Constructing the Tree 21th Jul 2009 (dynamic Tree) | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); | ||
+ | |||
+ | Any any; | ||
+ | any <<= OUString::createFromAscii("root"); | ||
+ | Reference< XMutableTreeNode > xMTN=m_xMutTreeDatMod->createNode(any,sal_True); | ||
+ | m_xMutTreeDatMod->setRoot(xMTN); | ||
+ | any <<= OUString::createFromAscii("Parent 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode = m_xMutTreeDatMod->createNode(any,sal_True); | ||
+ | xMTN->appendChild(xChildNode); | ||
+ | any <<= OUString::createFromAscii("Child 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True); | ||
+ | xChildNode->appendChild(xChildNode2); | ||
+ | any <<= OUString::createFromAscii("Grandson 1"); | ||
+ | Reference< XMutableTreeNode > xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False); | ||
+ | xChildNode2->appendChild(xChildNode3); | ||
+ | any <<= OUString::createFromAscii("Grandson 2"); | ||
+ | xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False); | ||
+ | xChildNode2->appendChild(xChildNode3); | ||
+ | any <<= OUString::createFromAscii("Child 2"); | ||
+ | xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True); | ||
+ | xChildNode->appendChild(xChildNode2); | ||
+ | any <<= OUString::createFromAscii("Parent 2"); | ||
+ | xChildNode=m_xMutTreeDatMod->createNode(any,sal_True); | ||
+ | xMTN->appendChild(xChildNode); | ||
+ | // set the model to see the tree in control (two hours to find four lines below) | ||
+ | // translation of oTreeModel = oTreeCtrl.Model and oTreeModel.DataModel = oMutableTreeDataModel | ||
+ | Reference< XControlModel > xControlModel=xControl->getModel(); | ||
+ | Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); | ||
+ | any <<= m_xMutTreeDatMod; | ||
+ | xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any); | ||
+ | // execute dialog | ||
+ | xDialog->execute(); | ||
+ | } | ||
+ | // end of constructor | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ====The new callHandlerMethod==== | ||
+ | As you can see in the code below only "addChild" method is used in this example: | ||
+ | <syntaxhighlight lang="cpp"> | ||
+ | // C++ | ||
+ | // Listing 16 | ||
+ | // XDialogEventHandler implementation | ||
+ | sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ | ||
+ | if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control | ||
+ | // not used in this example | ||
+ | return sal_True; | ||
+ | } | ||
+ | if (MethodName.equalsAscii("addChild")){//add a child to the selected node | ||
+ | // First retrieve the selection | ||
+ | Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); | ||
+ | Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); | ||
+ | // com.sun.star.awt.tree.XTreeControl | ||
+ | Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY); | ||
+ | Any any=xTreeControl->getSelection(); | ||
+ | // Parent | ||
+ | Reference< XMutableTreeNode > xParentNode; | ||
+ | any >>= xParentNode; | ||
+ | // adding | ||
+ | sal_Int32 nb=xParentNode->getChildCount(); | ||
+ | // Child | ||
+ | OUString OUStr; | ||
+ | |||
+ | any=xParentNode->getDisplayValue(); | ||
+ | any >>= OUStr; | ||
+ | OUStr = OUStr + OUString::createFromAscii(".") + OUString::valueOf((sal_Int32)++nb); | ||
+ | // put the new node name in text control | ||
+ | xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription")); | ||
+ | Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY); | ||
+ | xTextComponent->setText(OUStr); | ||
+ | any <<= OUStr; | ||
+ | Reference< XMutableTreeNode > xChildMutTreeNode = m_xMutTreeDatMod->createNode(any ,sal_True); | ||
+ | // Introspection | ||
+ | // any <<= xChildMutTreeNode; | ||
+ | // m_xInspector->inspect(any,OUString::createFromAscii("Inspector")); | ||
+ | xParentNode->appendChild(xChildMutTreeNode); | ||
+ | return sal_True; | ||
+ | } | ||
+ | return sal_False; | ||
+ | } | ||
+ | Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ | ||
+ | Sequence< OUString > SeqOUStr(2); | ||
+ | SeqOUStr[0]=OUString::createFromAscii("LosingFocus"); | ||
+ | SeqOUStr[1]=OUString::createFromAscii("addChild"); | ||
+ | return SeqOUStr; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Here is a snapshot | ||
+ | [[Image:EventDialogTree3.png|center|thumb|600px|Dynamic Tree in a Tree Control]] | ||
+ | where you can see all the nodes added. | ||
+ | |||
+ | See [[Writing_Professional_Components#My_own_Inspector|The Cpp Inspector]] to see an application of a dynamic Tree in a dialog. | ||
+ | |||
+ | =General Remark= | ||
+ | {{Warn|In this chapter I always use the component constructor to print out the dialog. I am not sure it's a good choice for professional components. I plan to write a chapter on [[Writing Professional Components|writing professional components]] but it would be better with a lot of practice that I haven't at the moment.}} | ||
+ | |||
+ | =Home Page= | ||
[[Image:HomePageCpp.png]] [[Using_Cpp_with_the_OOo_SDK|Return to Document Home page]] | [[Image:HomePageCpp.png]] [[Using_Cpp_with_the_OOo_SDK|Return to Document Home page]] | ||
=See also= | =See also= | ||
+ | *[[FR/Documentation/Plus_loin_avec_Composants_et_boites_de_dialogue|French version of this chapter]] | ||
*[[Constructing_Components|Constructing Components]] | *[[Constructing_Components|Constructing Components]] | ||
*[[Component_and_Dialog|Component and Dialog]] | *[[Component_and_Dialog|Component and Dialog]] | ||
− | *[[Treecontrol|The New Tree Contol]] | + | *[[Treecontrol|The New Tree Contol]] (in construction) |
*The corresponding paragraph [[Documentation/DevGuide/WritingUNO/Accessing_Dialogs|in Developer's Guide]] | *The corresponding paragraph [[Documentation/DevGuide/WritingUNO/Accessing_Dialogs|in Developer's Guide]] | ||
*[[Framework/Article/Generic_UNO_Interfaces_for_complex_toolbar_controls|Generic UNO Interfaces for complex toolbar controls]] | *[[Framework/Article/Generic_UNO_Interfaces_for_complex_toolbar_controls|Generic UNO Interfaces for complex toolbar controls]] | ||
Line 119: | Line 1,049: | ||
*[[BASIC/UNO_Object_Browser|BASIC UNO Object Browser]] : You can see the corresponding code as a complex component. | *[[BASIC/UNO_Object_Browser|BASIC UNO Object Browser]] : You can see the corresponding code as a complex component. | ||
*[[API/Samples/Java/Office/MinimalComponent|Minimal Java Component]] | *[[API/Samples/Java/Office/MinimalComponent|Minimal Java Component]] | ||
− | |||
*Managing [[Documentation/BASIC_Guide/Dialogs|Dialogs]] in OOoBasic. | *Managing [[Documentation/BASIC_Guide/Dialogs|Dialogs]] in OOoBasic. | ||
− | *[[Documentation/BASIC_Guide | + | *[[Documentation/BASIC_Guide|OOoBasic Guide]] |
+ | |||
− | |||
[[Category:Cpp]] | [[Category:Cpp]] | ||
[[Category:Uno]] | [[Category:Uno]] | ||
[[Category:Tutorial]] | [[Category:Tutorial]] | ||
[[Category:Extensions]] | [[Category:Extensions]] | ||
− | [[Category: | + | [[Category:Samples]] |
− | + |
Latest revision as of 12:38, 17 May 2022
In the previous chapter we use and program only two kinds of control in our dialog (Text edit controls and buttons). We intend in this chapter to go further and use a lot of controls and then see how things work in this area. You cannot read this chapter before reading the previous one, particularly because only code snippets are presented.
We begin again with our counter.
Contents
Counter with Numeric Field
When designing the dialog working with our counter, we take two text controls for our numeric data (as input with "setCount" button and as output with "getCount" button). Because text controls are often used our preceding example is important, but it's time now to improve our design and use a numeric field control. Because the dialog aspect is unchanged we don't provide a snapshot of the dialog.
Have a look at com.sun.star.awt.XNumericField interface and see what can be done with this interface : only getvalue
and setValue
will be used in this section, but you can play with other methods.
We provide only the callHandlerMethod
function method in the listing below (see the previous chapter and also the corresponding com.sun.star.awt.XDialogEventHandler interface). The corresponding code is as follows.
// Listing 1 // c++ sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("foo1")){//increment increment(); return sal_True; } if (MethodName.equalsAscii("foo2")){//decrement decrement(); return sal_True; } if (MethodName.equalsAscii("foo3")){ //setCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); setCount((sal_Int32)xNumericField->getValue()); return sal_True; } if (MethodName.equalsAscii("foo4")){ //getCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); xNumericField->setValue(getCount()); return sal_True; } return sal_False; }
There is no need to go into this listing in full detail here. We turn now to another interesting example of control : the radio button.
Using Radio buttons
An option button control com.sun.star.awt.UnoControlRadioButton is a simple switch with two states, that is selected by the user. Usually option buttons are used in groups to display several options, that the user may select. While option buttons and check boxes seem to be similar, selecting one option button deselects all the other option buttons in the same group.
We want use radio button to select the increment or decrement (between 1, 5 and 10). Here is a snapshot of our dialog :
where you see a decrement or increment of value five is selected.
For this problem the com.sun.star.awt.XRadioButton interface is your friend. We intend to provide two solutions for this example. We begin with a solution which use only the four methods of the counter.
A simple solution
This solution keeps the four previous methods managed by callHandlerMethod()
named "foo1", "foo2", .. "foo4" of the com.sun.star.awt.XDialogEventHandler interface. To put it differently no new event is managed by the dialog. We present now the corresponding code, and again with only the callHandlerMethod
. Have a look at the previous chapter if you want to remember you how it works.
// Listing 2 // C++ sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("foo1")){//increment Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1")); Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY); if (xRadioButton->getState()) m_nDelta=1; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2")); Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY); if (xRadioButton2->getState()) m_nDelta=5; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3")); Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY); if (xRadioButton3->getState()) m_nDelta=10; increment(); return sal_True; } if (MethodName.equalsAscii("foo2")){//decrement Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1")); Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY); if (xRadioButton->getState()) m_nDelta=1; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2")); Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY); if (xRadioButton2->getState()) m_nDelta=5; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3")); Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY); if (xRadioButton3->getState()) m_nDelta=10; decrement(); return sal_True; } if (MethodName.equalsAscii("foo3")){ //setCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); setCount((sal_Int32)xNumericField->getValue()); return sal_True; } if (MethodName.equalsAscii("foo4")){ //getCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); xNumericField->setValue(getCount()); return sal_True; } return sal_False; }
As you can see only increment()
and decrement()
methods are looking for the radiobutton which is set. The corresponding checking code is completely similar in both method, then it would be better to put it in a function. No event is fired when setting or cleared a radio button in this example. But it is possible to call a method when status is changed : note in this case, you have to provide a method for every buttons. Let's us now examine this second solution.
Managing Radio Buttons Events
Every times a user set or clear a radio button a method is fired : you have to edit the corresponding property of your option button. That means you have to manage an event when clicking with mouse button (see again Developer's Guide). Because there are three radio buttons we have to add three methods. We see this kind of solution could become too complex while the number of button is increasing.
The code is as follows :
// C++ // Listing 3 sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("foo1")){//increment increment(); return sal_True; } if (MethodName.equalsAscii("foo2")){//decrement decrement(); return sal_True; } if (MethodName.equalsAscii("foo3")){ //setCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); setCount((sal_Int32)xNumericField->getValue()); return sal_True; } if (MethodName.equalsAscii("foo4")){ //getCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); xNumericField->setValue(getCount()); return sal_True; } if (MethodName.equalsAscii("foo5")){ //incr/decr +/-1 if (m_nDelta!=1) m_nDelta=1; return sal_True; } if (MethodName.equalsAscii("foo6")){ //incr/decr +/-5 if (m_nDelta!=5) m_nDelta=5; return sal_True; } if (MethodName.equalsAscii("foo7")){ //incr/decr +/-10 if (m_nDelta!=10) m_nDelta=10; return sal_True; } return sal_False; } Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ Sequence< OUString > SeqOUStr(7); SeqOUStr[0]=OUString::createFromAscii("foo1"); SeqOUStr[1]=OUString::createFromAscii("foo2"); SeqOUStr[2]=OUString::createFromAscii("foo3"); SeqOUStr[3]=OUString::createFromAscii("foo4"); SeqOUStr[4]=OUString::createFromAscii("foo5"); SeqOUStr[5]=OUString::createFromAscii("foo6"); SeqOUStr[6]=OUString::createFromAscii("foo7"); return SeqOUStr; }
This code is working without reading the state of the radio button but I cannot certify it could be the case in any situation you will encounter. Note that we have already provided a code to access to the state of a radio button in the previous section. Note also m_nDelta
is a new member value set to 1 by the constructor (corresponding to the default selected radio button).
Using a listbox in a dialog
In this section, we replace the radio button control with a list box. Have a look in com.sun.star.awt.XListBox inteface. We only use the getSelectedItemPos()
method in the following code.
// XDialogEventHandler implementation // C++ // Listing 4 sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("foo1")){//increment Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1")); Reference< XListBox > xListBox(xControl,UNO_QUERY); switch (xListBox->getSelectedItemPos()){ case 0: m_nDelta=1;break; case 1: m_nDelta=5;break; case 2: m_nDelta=10;break; } increment(); return sal_True; } if (MethodName.equalsAscii("foo2")){//decrement Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("ListBox1")); Reference< XListBox > xListBox(xControl,UNO_QUERY); switch (xListBox->getSelectedItemPos()){ case 0: m_nDelta=1;break; case 1: m_nDelta=5;break; case 2: m_nDelta=10;break; } decrement(); return sal_True; } if (MethodName.equalsAscii("foo3")){ //setCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); setCount((sal_Int32)xNumericField->getValue()); return sal_True; } if (MethodName.equalsAscii("foo4")){ //getCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); xNumericField->setValue(getCount()); return sal_True; } return sal_False; }
which yields the dialog below :
Multi-Page Dialog
The multi-page dialog is tackled in OOBasic Document. Our goal is to use a multipage dialog with our counter and see how it works. Our second page will be very similar to the simple dialog we have already encountered, and the first one manage increment step. The great difference is we have to manage more buttons, particularly the "cancel", "<<previous", "next>>" and "OK" buttons. Because I never make multi-page dialog working before writing this section, I begin with the corresponding OOBasic program.
As already explained, I think it's a good practice to solve a problem in OOoBasic before solving it in C++. Before starting this document, OOBasic was already documented in a BASIC Guide (and also in books) but you have only few examples in the SDK to learn the corresponding C++ part. In my knowledge there is no C++/UNO book. The other great reason is the need of OOBasic GUI when constructing the dialog : and then writing a OOBasic program is only a little step. |
OOBasic program
We first give the OOBasic program. The first part after the main is corresponding to the Counter, and the second part is corresponding to the next previous (Step) management. This management is realized with two Sub :
- "cmdPrev_Initiated" sub,
- "cmdNext_Initiated" sub.
When you are in Step=1 the previous button is disabled, while in the Step=2 the next button has to be disabled.
REM ***** BASIC ***** 'Listing 5 Dim Dlg As Object Dim Count As integer Dim IncrDecr As integer Sub Main DialogLibraries.LoadLibrary("Standard") Dlg = CreateUnoDialog(DialogLibraries.Standard.Dialog2) 'Because of Multi-page don't forget to manage Step : Dlg.Model.Step=1 Dlg.Execute() Dlg.dispose() End Sub 'Our counter begins here <----------- Sub Increment Count = Count + IncrDecr End Sub Sub Decrement Count = Count - IncrDecr End Sub Sub getCount Dim oNumericField oNumericField = Dlg.getControl("NumericField2") oNumericField.Value = Count End Sub Sub setCount Dim oNumericField oNumericField = Dlg.getControl("NumericField1") Count = oNumericField.Value End Sub ' Our Counter ends here <------------ Sub cmdNext_Initiated Dim cmdNext As Object Dim cmdPrev As Object cmdPrev = Dlg.getControl("cmdPrev") 'inspect(cmdPrev) cmdNext = Dlg.getControl("cmdNext") cmdPrev.Model.Enabled = Not cmdPrev.Model.Enabled cmdNext.Model.Enabled = False 'if Next update IncrDecr If Dlg.Model.Step = 1 Then If Dlg.getControl("OptionButton1").State Then IncrDecr = 1 End If If Dlg.getControl("OptionButton2").State Then IncrDecr = 5 End If If Dlg.getControl("OptionButton3").State Then IncrDecr = 10 End If End If Dlg.Model.Step = Dlg.Model.Step + 1 End Sub Sub cmdPrev_Initiated Dim cmdNext As Object Dim cmdPrev As Object cmdPrev = Dlg.getControl("cmdPrev") cmdNext = Dlg.getControl("cmdNext") cmdPrev.Model.Enabled = False cmdNext.Model.Enabled = True Dlg.Model.Step = Dlg.Model.Step - 1 End Sub
Note also that the radio buttons are checked only when clicking on the Next button in Step 1. The dialog then changes its appearance as follows:
where you see "<<Previous" button disabled and
where you see "Next>>" button is disabled.
C++ program
Our goal in this section is to reuse the previous multi-page dialog but with C++. It was easy for me to manage the Next Previous buttons in OOBasic but spend many hours for translating the simple OOBasic line :
REM ***** BASIC ***** Dlg.Model.Step = Dlg.Model.Step + 1
Finally Java Inspector C++ automatic code generation shows me the way. The code is as follows :
// C++ // Listing 6 // XDialogEventHandler implementation sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("increment")){//increment increment(); return sal_True; } if (MethodName.equalsAscii("decrement")){//decrement decrement(); return sal_True; } if (MethodName.equalsAscii("setCount")){ //setCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField1")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); setCount((sal_Int32)xNumericField->getValue()); return sal_True; } if (MethodName.equalsAscii("getCount")){ //getCount Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("NumericField2")); Reference< XNumericField > xNumericField(xControl,UNO_QUERY); xNumericField->setValue(getCount()); return sal_True; } if (MethodName.equalsAscii("cmdPrev_Initiated")){ //<<Previous // xDialog is a parameter Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev")); //com.sun.star.awt.XConrolModel Reference< XControlModel> xControlModel= xControl->getModel(); Any any; sal_Bool Boolval; Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); any=xPropertySet->getPropertyValue(OUString::createFromAscii("Enabled")); any >>= Boolval; Boolval = !Boolval; any <<= Boolval; xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any); xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext")); xControlModel= xControl->getModel(); Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY); any=xPropertySet2->getPropertyValue(OUString::createFromAscii("Enabled")); any >>= Boolval; Boolval = !Boolval; any <<= Boolval; xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any); // found with JavaInspector C++ code generation :Step property management sal_Int32 step; Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW); xControlModel= xControl2->getModel(); Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY); step = 1; any <<= step; xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any); return sal_True; } if (MethodName.equalsAscii("cmdNext_Initiated")){ //Next>> // xDialog is a parameter Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl=xControlContainer->getControl(OUString::createFromAscii("cmdPrev")); //com.sun.star.awt.XConrolModel Reference< XControlModel> xControlModel= xControl->getModel(); Any any; sal_Bool Boolval; Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); any <<= sal_True; xPropertySet->setPropertyValue(OUString::createFromAscii("Enabled"),any); xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext")); xControlModel= xControl->getModel(); Reference< XPropertySet > xPropertySet2( xControlModel,UNO_QUERY); any <<= sal_False; xPropertySet2->setPropertyValue(OUString::createFromAscii("Enabled"),any); // RadioButton management : not great with such if ! xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton1")); Reference< XRadioButton > xRadioButton(xControl,UNO_QUERY); if (xRadioButton->getState()) m_nDelta=1; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton2")); Reference< XRadioButton > xRadioButton2(xControl,UNO_QUERY); if (xRadioButton2->getState()) m_nDelta=5; xControl=xControlContainer->getControl(OUString::createFromAscii("OptionButton3")); Reference< XRadioButton > xRadioButton3(xControl,UNO_QUERY); if (xRadioButton3->getState()) m_nDelta=10; // found with JavaInspector C++ code generation sal_Int32 step; // the trick is here : not line below // Reference< XControl >xControl2=xControlContainer->getControl(OUString::createFromAscii("Dialog1")) Reference< XControl > xControl2 (xDialog,UNO_QUERY_THROW); xControlModel= xControl2->getModel(); Reference< XPropertySet > xPropertySet3( xControlModel,UNO_QUERY); step = 2; any <<= step; xPropertySet3->setPropertyValue(OUString::createFromAscii("Step"),any); return sal_True; } return sal_False; } Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ Sequence< OUString > SeqOUStr(6); SeqOUStr[0]=OUString::createFromAscii("increment"); SeqOUStr[1]=OUString::createFromAscii("decrement"); SeqOUStr[2]=OUString::createFromAscii("setCount"); SeqOUStr[3]=OUString::createFromAscii("getCount"); SeqOUStr[4]=OUString::createFromAscii("cmdNext_Initiated"); SeqOUStr[5]=OUString::createFromAscii("cmdPrev_Initiated"); return SeqOUStr; }
See com.sun.star.awt.XControl, com.sun.star.awt.XControlContainer, com.sun.star.awt.XControlModel and com.sun.star.beans.XPropertySet, Interfaces.
We turn now to other examples where we leave our Counter only because it's too simple.
The New Tree Control
The New Tree Contol is tackled here but the corresponding article is under construction (since 2006). Frankly speaking I am not completly satisfied with this control at the moment for different reasons :
- the tree expanded listener is useless if your node has no child (not fired). This raises probably problems when wanting to manage a dynamic tree (for instance for an introspection tool)
- The event property in OpenOffice.org dialog editor is too simple and is unable to replace event listener. For instance there is no event to manage a selection change as in Radio Button.
You probably wonder why I take time to write a documentation in such a situation. It's only because I think if the documentation lack nobody will use this control and then nobody will improve the code of this control. And also, I finally achieve to make dynamic trees working with only adding a button (without event listener and without an expanding event). I by-pass then both drawbacks previously evocated.
The most important concept underlying the Tree Control is that of complete separation between data and how that data is displayed on the screen. This is commonly known as Model/View/Controller-design (MVC)
- com.sun.star.awt.tree.XMutableTreeDataModel and com.sun.star.awt.tree.XMutableTreeNode interfaces are related to the model,
- com.sun.star.awt.tree.XTreeControl is related to the control and view.
Our starting OOoBasic program
rvc44 posted in OOOForum (Thu Dec 20, 2007) an example in OOoBasic which serves me as starting point. I have added event listener and obtain the code snippet as follows :
REM ***** BASIC ***** 'Listing 7 Option Explicit 'Most of this code belongs to rvc44 http://www.oooforum.org/forum/viewtopic.phtml?t=67005&highlight= Private oDlg as Object, oLbDescription as Object Private oTreeCtrl as Object, oTreeModel as Object Private oMutableTreeDataModel as Object Private oRootNode as Object, oChildNode as Object, ogNode as Object Sub mainTree '[Simple example: collapsing and expanding folders] DialogLibraries.loadLibrary("Standard") 'create the dialog oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog) If IsNull(oDlg) Then Exit Sub 'get a refrence to the control/s and its model/s oLbDescription = oDlg.getControl("lbDescription") oLbDescription.setText( "A very simple tree control with listeners." & _ chr(13) & chr(13) &_ "Expanding and collapsing happends automatically" ) oTreeCtrl = oDlg.getControl("TreeControl1") oTreeModel = oTreeCtrl.Model 'XRay oTreeModel 'instantiate the MutableTreeDataModel service oMutableTreeDataModel = createUnoService(_ "com.sun.star.awt.tree.MutableTreeDataModel") 'XRay oMutableTreeDataModel 'MutableTreeDataModel implemets XMutableTreeDataModel 'use XMutableTreeNode::createNode( ' [in] any DisplayValue, ' [in] boolean ChildsOnDemand ) 'to create the root node 'If you pass to the boolean parameter TRUE as a argument, 'the created node will be treated as a non-leaf (branch) node 'by the XTreeControl , even when it has no child nodes. 'If false, the node will be a leaf. 'The root should obviously be a non-leaf node. oRootNode = oMutableTreeDataModel.createNode( "Root", true ) 'use XMutableTreeNode::setRoot( [in] XMutableTreeNode RootNode ) 'to set this node as the root of the model oMutableTreeDataModel.setRoot(oRootNode) 'now create the children of the root Dim oChildNode1 oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true ) oRootNode.appendChild(oChildNode1) 'Create this child's own children 'In two steps: Dim oSubChildNode oSubChildNode = oMutableTreeDataModel.createNode(_ "Child 1", true ) oChildNode1.appendChild(oSubChildNode) 'In only one: 'Go deeper in the hierarchical structure oSubChildNode.appendChild( _ oMutableTreeDataModel.createNode( _ "Grandson 1", false ) ) oSubChildNode.appendChild( _ oMutableTreeDataModel.createNode( _ "Grandson 2", false ) ) 'In only one: oChildNode1.appendChild( oMutableTreeDataModel.createNode( _ "Child 2", true ) ) '======================================================================= 'As you see, the Data Model always creates the nodes '(XMutableDataModel::createNode) 'and then you add it to the node you want, taking care that 'the node created and the parent node belong to the same data model Dim oChildNode2 oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true ) oRootNode.appendChild(oChildNode2) Dim oChildNode3 oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE ) 'oChildNode3.setCollapsedGraphicURL( ANY_DOC ) 'oChildNode3.setExpandedGraphicURL( ANY_DOC ) 'oChildNode3.setNodeGraphicURL( BASIC_BRAKE ) oRootNode.appendChild(oChildNode3) Dim oChildNode4 oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true ) oRootNode.appendChild(oChildNode4) Dim oChildNode5 oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true ) oRootNode.appendChild(oChildNode5) Dim oChildNode6 oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE ) oRootNode.appendChild(oChildNode6) 'Although the API refrence states the following: ' "If you want to add child nodes to your tree on demand ' you can .[..] Make sure the parent node returns true ' for XTreeNode::hasChildsOnDemand() either by implementing ' XTreeNode yourself or, if you use the MutableTreeDataModel , ' use XMutableTreeNode::setHasChildsOnDemand() ." 'https://www.openoffice.org/api/docs/common/ref/com/sun/star/awt/tree/TreeControl.html Dim bHasChildernOnDemand as Boolean 'in this case hasChildsOnDemand returns FALSE 'bHasChildernOnDemand = oChildNode6.hasChildsOnDemand() Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE ) 'BUT the node is appended to this node that has no children on demand oChildNode6.appendChild(aChild) 'Should it throw an exception? 'set the data model at the TreeControlModel::DataModel property. oTreeModel.DataModel = oMutableTreeDataModel ' oTreeCtrl.DefaultExpandedGraphicURL = FOLDER_OPEN 'oTreeCtrl.DefaultCollapsedGraphicURL = FOLDER_CLOSED Dim oListener,oListener2 oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener") oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener") 'Xray oListener2 oTreeCtrl.addTreeExpansionListener(oListener) oTreeCtrl.addSelectionChangeListener(oListener2) oDlg.execute() oTreeCtrl.removeSelectionChangeListener(oListener2) oTreeCtrl.removeTreeExpansionListener(oListener) oDlg.dispose() End Sub Sub Tree_treeExpanding(oEvt) 'oEvt.Node has methods : 'getChildAt Returns the child tree node at Index . 'getChildCount Returns the number of child nodes. 'getParent Returns the parent node of this node. 'getIndex Returns the index of Node in this instances children. 'hasChildsOnDemand Returns true if the children of this node are created on demand. 'getDisplayValue If not empty, the textual representation of this any is used as the text part of this node. 'getNodeGraphicURL The URL for a graphic that is rendered before the text part of this node. 'getExpandedGraphicURL The URL for a graphic that is rendered to visualize expanded non leaf nodes. 'getCollapsedGraphicURL The URL for a graphic that is rendered to visualize collapsed non leaf nodes. MsgBox("Node : " & oEvt.Node.getDisplayValue &" expanding") ogNode = oEvt.Node end sub Sub Tree_treeExpanded(oEvt) ' MsgBox("Tree expanded") end sub Sub Tree_treeCollapsing(oEvt) 'MsgBox("Tree collapsing") end sub Sub Tree_treeCollapsed(oEvt) ' MsgBox("Tree collapsed") end sub Sub Tree_requestChildNodes(oEvt) 'print oEvt.Node.getIndex(oEvt.Node) 'XRay oEvt.Node ' Msgbox ("Tree_requestChildNodes") End Sub Sub Tree_disposing(oEvt) ' Msgbox ("Tree_disposing") End Sub Sub Select_selectionChanged(oEvt) MsgBox("Selection Changed") end sub
Here is the corresponding snapshot
where you see a "treeExpanding" listener at work, and the second with another listener ("selection change") at work.
If you want to construct such a tree with another programming language, you have to investigate the corresponding interfaces :
- com.sun.star.awt.tree.XMutableTreeDataModel with
createNode
andsetRoot
methods, - com.sun.star.awt.tree.XMutableTreeNode with
appendChild
method, - com.sun.star.awt.tree.XTreeControl -|> com.sun.star.view.XMultiSelectionSupplier -|> com.sun.star.view.XSelectionSupplier with
getSelection
method (-|> is a simplified inheritance notation).
It's time to examine the C++ translation of this example.
C++ Code without Listener
As usual we plan to use the previous OOoBasic dialog but with C++ code. Because we only want a demonstration we will simplify the Tree. Our goal is to retrieve the expanded node but as mentioned in the section title we don't want to use listener that means we want only to manage events of the tree dialog control with our member functions. We will investigate first with OOoBasic.
Managing Events of Tree Control with OOoBasic
Our goal is simple : retrieve the selected node of the tree. We first remove all the event listener of the OOoBasic code :
REM ***** BASIC ***** 'Listing 8 ... Sub mainTree '[Simple example: collapsing and expanding folders] ..... 'Dim oListener,oListener2 'oListener = CreateUnoListener("Tree_","com.sun.star.awt.tree.XTreeExpansionListener") 'oListener2 = createUnoListener("Select_","com.sun.star.view.XSelectionChangeListener") 'Xray oListener2 'oTreeCtrl.addTreeExpansionListener(oListener) 'oTreeCtrl.addSelectionChangeListener(oListener2) oDlg.execute() 'oTreeCtrl.removeSelectionChangeListener(oListener2) 'oTreeCtrl.removeTreeExpansionListener(oListener) oDlg.dispose() End Sub
We choose among the events shown below
where you can see the Losing Focus Event ("perte de focus" in French) is used with the sub :
REM ***** BASIC ***** 'Listing 9 Sub LosingFocus(Event As Object) 'inspect(oTreeCtrl) 'getSelection is member function of com.sun.star.awt.tree.XTreeControl interface 'getDisplayValue is member function of com.sun.star.awt.tree.XTreeNode interface MsgBox(oTreeCtrl.getSelection.getDisplayValue()) End Sub
When expanding the tree and selecting a node, you only click in the Text control to see the result
where you see the LosingFocus Sub above is fired.
Managing Events of Tree Control with a C++ member function
We change the preceding OOoBasic example in four ways :
- we simplify the tree to be displayed,
- we display the selection in the already present control text instead of in a message Box,
- because we are dealing with components (and not addon) we have to start from an IDL file. I have keept the counter IDL file even if my new Dialog is unable to make it working.
- the "losing focus" event property is bound to a component method called "LosingFocus" (see Developper's Guide).
The new Constructor
The constructor has to display the tree in the corresponding control. To say it differently we shall find almost of the OOoBasic "mainTree" sub of the previous example in this constructor. The code is then as follows :
// cpp // Listing 10 inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw () : m_xContext( xContext ) { m_xMCF=m_xContext->getServiceManager(); Reference< XDesktop > xDesktop( m_xMCF->createInstanceWithContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ), xContext ), UNO_QUERY_THROW ); Reference< XComponent > xcomponent = xDesktop->getCurrentComponent(); Sequence< Any> Args(1); Args[0] <<= xcomponent; Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithArgumentsAndContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), Args, xContext), UNO_QUERY_THROW ); // Uncomment below and comment above if you want the OOoDesktop as parent of your dialog // Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithContext( // OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), // xContext), // UNO_QUERY_THROW ); Reference< XDialog > xDialog=xDialog2->createDialogWithHandler( OUString::createFromAscii( "vnd.sun.star.script:Standard.SimpleTreeDialog?location=application" ), (XCountable *)this); // Constructing the Tree (17th Jul 2009) Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); Reference< XMutableTreeDataModel > xTreeMutDatMod (m_xMCF->createInstanceWithContext( OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),xContext),UNO_QUERY_THROW); // Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office Any any; any <<= OUString::createFromAscii("root"); Reference< XMutableTreeNode > xMTN=xTreeMutDatMod->createNode(any,sal_True); xTreeMutDatMod->setRoot(xMTN); any <<= OUString::createFromAscii("Parent 1"); Reference< XMutableTreeNode > xChildNode = xTreeMutDatMod->createNode(any,sal_True); xMTN->appendChild(xChildNode); any <<= OUString::createFromAscii("Child 1"); Reference< XMutableTreeNode > xChildNode2 = xTreeMutDatMod->createNode(any,sal_True); xChildNode->appendChild(xChildNode2); any <<= OUString::createFromAscii("Grandson 1"); Reference< XMutableTreeNode > xChildNode3 = xTreeMutDatMod->createNode(any,sal_False); xChildNode2->appendChild(xChildNode3); any <<= OUString::createFromAscii("Grandson 2"); xChildNode3 = xTreeMutDatMod->createNode(any,sal_False); xChildNode2->appendChild(xChildNode3); any <<= OUString::createFromAscii("Child 2"); xChildNode2 = xTreeMutDatMod->createNode(any,sal_True); xChildNode->appendChild(xChildNode2); any <<= OUString::createFromAscii("Parent 2"); xChildNode=xTreeMutDatMod->createNode(any,sal_True); xMTN->appendChild(xChildNode); // set the model to see the tree in control (two hours to find the four lines below) // translation of oTreeModel = oTreeCtrl.Model and oTreeModel.DataModel = oMutableTreeDataModel Reference< XControlModel > xControlModel=xControl->getModel(); Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); any <<= xTreeMutDatMod; xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any); // execute dialog xDialog->execute(); }
The "LosingFocus" method
The LosingFocus sub in OOoBasic was very simple. I have translated it with Introspection tools and IDL documentation like com.sun.star.awt.tree.XTreeControl, com.sun.star.awt.tree.XTreeNode and com.sun.star.awt.XTextComponent.
// C++ // Listing 11 sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); // com.sun.star.awt.tree.XTreeControl Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY); Any any=xTreeControl->getSelection(); Reference< XTreeNode > xTreeNode; any >>= xTreeNode; // put the result of selection in text control xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription")); Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY); any = xTreeNode->getDisplayValue(); OUString OUStr; any >>= OUStr; xTextComponent->setText(OUStr + OUString::createFromAscii(" -- DONE --")); return sal_True; } return sal_False; }
This method is called when losing focus : click in the text control, for instance.
Here is a snapshot of the corresponding dialog :
Because only a method is used, you have also to change the getSupportedMethodNames()
member function :
// C++ // Listing 12 Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ Sequence< OUString > SeqOUStr(1); SeqOUStr[0]=OUString::createFromAscii("LosingFocus"); // SeqOUStr[1]=OUString::createFromAscii("foo2"); // SeqOUStr[2]=OUString::createFromAscii("foo3"); // SeqOUStr[3]=OUString::createFromAscii("foo4"); return SeqOUStr; }
I am not sure xml2cmp tools which proceeds the CppComponent.uno.xml file is able to work with a type as follows :
<type>com.sun.star.awt.tree.XTreeControl</type> In this type, there is four level (com/sun/star/tree) before the module name, instead of usually three. Is the problem here ? Have added it to MakeFile to make it working. If you confirm or infirm that, please contact me.SergeMoutou 16:30, 17 July 2009 (UTC) |
C++ code with listener
Altough I like event listener in OOoBasic I dislike them in C++ : they are easy to use in OOoBasic but hard in C++. Then I don't know when I will write such a documentation.
TO DO
Dynamic Trees
In this section, we want to Investigate how to manage a dynamic tree. A dynamic tree is a tree that is constructed on the demand.
Dynamic Trees in OOoBasic
In fact I begun to write the next section first. Because I encounter some problem I decided to write this section. After many hours I was not able to make dynamic tree working in C++ while it takes me only few minutes to make it working in OOoBasic ! FYI : dynamic Trees in C++ in the next section are working now.
I keep the initial OOoBasic tree and add two buttons : "OK" button and "add Child" button. You select a node and when clicking on "add Child" button a child is added. Its name is the name of the selected node followed with ".n" where n is the number of the child. This is a dynamic tree because its size is not fixed. Its code is as follows:
REM ***** BASIC ***** 'Listing 14 Option Explicit Private oDlg as Object, oLbDescription as Object Private oTreeCtrl as Object, oTreeModel as Object Private oMutableTreeDataModel as Object Private oRootNode as Object, oChildNode as Object, ogNode as Object Sub mainTree '[Simple example: collapsing and expanding folders] DialogLibraries.loadLibrary("Standard") oDlg = CreateUnoDialog(DialogLibraries.Standard.SimpleTreeDialog) If IsNull(oDlg) Then Exit Sub oTreeCtrl = oDlg.getControl("TreeControl1") oTreeModel = oTreeCtrl.Model oMutableTreeDataModel = createUnoService(_ "com.sun.star.awt.tree.MutableTreeDataModel") oRootNode = oMutableTreeDataModel.createNode( "Root", true ) oMutableTreeDataModel.setRoot(oRootNode) Dim oChildNode1 oChildNode1 = oMutableTreeDataModel.createNode( "Parent 1", true ) oRootNode.appendChild(oChildNode1) 'Create this child's own children 'In two steps: Dim oSubChildNode oSubChildNode = oMutableTreeDataModel.createNode(_ "Child 1", true ) oChildNode1.appendChild(oSubChildNode) 'In only one: 'Go deeper in the hierarchical structure oSubChildNode.appendChild( _ oMutableTreeDataModel.createNode( _ "Grandson 1", false ) ) oSubChildNode.appendChild( _ oMutableTreeDataModel.createNode( _ "Grandson 2", false ) ) 'In only one: oChildNode1.appendChild( oMutableTreeDataModel.createNode( _ "Child 2", true ) ) Dim oChildNode2 oChildNode2 = oMutableTreeDataModel.createNode( "Parent 2", true ) oRootNode.appendChild(oChildNode2) Dim oChildNode3 oChildNode3 = oMutableTreeDataModel.createNode( "Parent 3", FALSE ) oRootNode.appendChild(oChildNode3) Dim oChildNode4 oChildNode4 = oMutableTreeDataModel.createNode( "Parent 4", true ) oRootNode.appendChild(oChildNode4) Dim oChildNode5 oChildNode5 = oMutableTreeDataModel.createNode( "Parent 5", true ) oRootNode.appendChild(oChildNode5) Dim oChildNode6 oChildNode6 = oMutableTreeDataModel.createNode( "Parent 6", FALSE ) oRootNode.appendChild(oChildNode6) Dim bHasChildernOnDemand as Boolean Dim aChild : aChild = oMutableTreeDataModel.createNode( "Grandson", FALSE ) oChildNode6.appendChild(aChild) oTreeModel.DataModel = oMutableTreeDataModel oDlg.execute() oDlg.dispose() End Sub Sub addChild(Event As Object) Dim oParentNode, oChildNode Dim nb as long Dim oChildName as String oParentNode = oTreeCtrl.getSelection() nb = oParentNode.getChildCount() nb=nb+1 'I want numerotation begins with 1 oChildName = oParentNode.getDisplayValue() + "." + nb oChildNode = oMutableTreeDataModel.createNode(oChildName , TRUE ) oParentNode.appendChild(oChildNode) oLbDescription = oDlg.getControl("lbDescription") oLbDescription.setText(oChildName) End Sub
The sub "addChild" is bound with the corresponding button. Here is a snapshot of a result:
Question : what is the last created node ? Answer see the Text control : "Root.7.1.2".
Let's turn to the corresponding C++ example.
Dynamic Trees in C++
I start from the previous tree (C++) with the same functioning as in previous section. When a node is selected, "add Child" button creates a child with name nodename.i where i is the number of the child and nodename the name of the node. For debuging intention, I put also the name of the added node in the text control.
Our new constructor
The constructor of previous section is slighty modified. Here is the corresponding code :
// c++ // Listing 15 // added this constructor 06/06/09 inline MyCounterImpl( Reference< XComponentContext > const & xContext) throw () : m_xContext( xContext ) { m_xMCF=m_xContext->getServiceManager(); // Reference< XMutableTreeDataModel > xTreeMutDatMod(xTreeControl,UNO_QUERY); //NO NO NO:hangs the office : see below Reference< XMutableTreeDataModel > xMutTreeDatMod(m_xMCF->createInstanceWithContext( OUString::createFromAscii("com.sun.star.awt.tree.MutableTreeDataModel"),m_xContext),UNO_QUERY_THROW); m_xMutTreeDatMod = xMutTreeDatMod; // If you want to use java Inspector Reference< XInstanceInspector > xInspector(m_xMCF->createInstanceWithContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "org.openoffice.InstanceInspector" ) ), m_xContext), UNO_QUERY_THROW ); m_xInspector = xInspector; Reference< XDesktop > xDesktop( m_xMCF->createInstanceWithContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ), xContext ), UNO_QUERY_THROW ); Reference< XComponent > xcomponent = xDesktop->getCurrentComponent(); Sequence< Any> Args(1); Args[0] <<= xcomponent; Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithArgumentsAndContext( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), Args, xContext), UNO_QUERY_THROW ); // Reference< XDialogProvider2 > xDialog2(m_xMCF->createInstanceWithContext( // OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.awt.DialogProvider2" ) ), // xContext), // UNO_QUERY_THROW ); Reference< XDialog > xDialog=xDialog2->createDialogWithHandler( OUString::createFromAscii( "vnd.sun.star.script:Standard.SimpleTreeDialog?location=application" ), (XCountable *)this); // Constructing the Tree 21th Jul 2009 (dynamic Tree) Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); Any any; any <<= OUString::createFromAscii("root"); Reference< XMutableTreeNode > xMTN=m_xMutTreeDatMod->createNode(any,sal_True); m_xMutTreeDatMod->setRoot(xMTN); any <<= OUString::createFromAscii("Parent 1"); Reference< XMutableTreeNode > xChildNode = m_xMutTreeDatMod->createNode(any,sal_True); xMTN->appendChild(xChildNode); any <<= OUString::createFromAscii("Child 1"); Reference< XMutableTreeNode > xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True); xChildNode->appendChild(xChildNode2); any <<= OUString::createFromAscii("Grandson 1"); Reference< XMutableTreeNode > xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False); xChildNode2->appendChild(xChildNode3); any <<= OUString::createFromAscii("Grandson 2"); xChildNode3 = m_xMutTreeDatMod->createNode(any,sal_False); xChildNode2->appendChild(xChildNode3); any <<= OUString::createFromAscii("Child 2"); xChildNode2 = m_xMutTreeDatMod->createNode(any,sal_True); xChildNode->appendChild(xChildNode2); any <<= OUString::createFromAscii("Parent 2"); xChildNode=m_xMutTreeDatMod->createNode(any,sal_True); xMTN->appendChild(xChildNode); // set the model to see the tree in control (two hours to find four lines below) // translation of oTreeModel = oTreeCtrl.Model and oTreeModel.DataModel = oMutableTreeDataModel Reference< XControlModel > xControlModel=xControl->getModel(); Reference< XPropertySet > xPropertySet( xControlModel,UNO_QUERY); any <<= m_xMutTreeDatMod; xPropertySet->setPropertyValue(OUString::createFromAscii("DataModel"),any); // execute dialog xDialog->execute(); } // end of constructor
The new callHandlerMethod
As you can see in the code below only "addChild" method is used in this example:
// C++ // Listing 16 // XDialogEventHandler implementation sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){ if (MethodName.equalsAscii("LosingFocus")){//When Losing Focus in Tree control // not used in this example return sal_True; } if (MethodName.equalsAscii("addChild")){//add a child to the selected node // First retrieve the selection Reference< XControlContainer > xControlContainer(xDialog,UNO_QUERY); Reference< XControl > xControl = xControlContainer->getControl(OUString::createFromAscii("TreeControl")); // com.sun.star.awt.tree.XTreeControl Reference< XTreeControl > xTreeControl(xControl,UNO_QUERY); Any any=xTreeControl->getSelection(); // Parent Reference< XMutableTreeNode > xParentNode; any >>= xParentNode; // adding sal_Int32 nb=xParentNode->getChildCount(); // Child OUString OUStr; any=xParentNode->getDisplayValue(); any >>= OUStr; OUStr = OUStr + OUString::createFromAscii(".") + OUString::valueOf((sal_Int32)++nb); // put the new node name in text control xControl = xControlContainer->getControl(OUString::createFromAscii("lbDescription")); Reference< XTextComponent > xTextComponent(xControl,UNO_QUERY); xTextComponent->setText(OUStr); any <<= OUStr; Reference< XMutableTreeNode > xChildMutTreeNode = m_xMutTreeDatMod->createNode(any ,sal_True); // Introspection // any <<= xChildMutTreeNode; // m_xInspector->inspect(any,OUString::createFromAscii("Inspector")); xParentNode->appendChild(xChildMutTreeNode); return sal_True; } return sal_False; } Sequence< OUString > SAL_CALL MyCounterImpl::getSupportedMethodNames() throw (RuntimeException){ Sequence< OUString > SeqOUStr(2); SeqOUStr[0]=OUString::createFromAscii("LosingFocus"); SeqOUStr[1]=OUString::createFromAscii("addChild"); return SeqOUStr; }
Here is a snapshot
where you can see all the nodes added.
See The Cpp Inspector to see an application of a dynamic Tree in a dialog.
General Remark
In this chapter I always use the component constructor to print out the dialog. I am not sure it's a good choice for professional components. I plan to write a chapter on writing professional components but it would be better with a lot of practice that I haven't at the moment. |
Home Page
See also
- French version of this chapter
- Constructing Components
- Component and Dialog
- The New Tree Contol (in construction)
- The corresponding paragraph in Developer's Guide
- Generic UNO Interfaces for complex toolbar controls
- Popup Menu Controller and C++
- Statusbar Controler and C++
- C++ and UNO tutorial
- Service Declaration
- Registering properties
- UNO tutorial
- UNO IDL
- Extensions Packager (BasicAddonBuilder from Paolo Mantovani)
- BASIC UNO Object Browser : You can see the corresponding code as a complex component.
- Minimal Java Component
- Managing Dialogs in OOoBasic.
- OOoBasic Guide