Going further with Dialog and Component

From Apache OpenOffice Wiki
Revision as of 10:21, 14 July 2009 by SergeMoutou (Talk | contribs)

Jump to: navigation, search

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 control and then see how things work in this area. You cannot read this chapter befor reading the previous one.

We begin again with our counter.

Counter with Numeric Field

When we have designed the dialog working with our counter, we have take two text controls for our numeric data. 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 :

Our Second Counter 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 code is completely similar and it would be beter 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;
}

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.

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 :

Our Third Counter Dialog

Multi-Page Dialog

The multi-page dialog with OOoBasic is tackled here. Our goal is to use a multipage dialiog with our counter and see how it works. Our second page will be very similar as 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 OOoBasic program. Template:Documentation/Note

OOoBasic program

We first give the OOoBasic program. The first part after the main is correponding to the Counter, and the seond 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:

Page 1 (Step 1 in OOoBasic code)

where you see "<<Previous" button disabled and

Page 2 (Step 2 in OOoBasic 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++.

Documentation caution.png This section is under construction. The C++ code is not checked.
sal_Bool SAL_CALL MyCounterImpl::callHandlerMethod(const Reference< XDialog >& xDialog,const Any& EventObject,const OUString & MethodName ) throw(WrappedTargetException, RuntimeException ){
//...
  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= xContol->getModel();
	Any any;
// possible probleme ici ... parcqu'en fait entre parentheses type any
	if (xControlModel->getPropertyValue(OUString::createFromAscii("Enabled")){
		any <<= sal_False;
		xControlModel->setPropertyValue(OUString::createFromAscii("Enabled"),any);
	} else {
		any <<= sal_True;
		xControlModel->setPropertyValue(OUString::createFromAscii("Enabled"),any);
	}
	xControl=xControlContainer->getControl(OUString::createFromAscii("cmdNext"));
	xControlModel= xContol->getModel();
	if (xControlModel->getPropertyValue(OUString::createFromAscii("Enabled")){
		any <<= sal_False;
		xControlModel->setPropertyValue(OUString::createFromAscii("Enabled"),any);
	} else {
		any <<= sal_True;
		xControlModel->setPropertyValue(OUString::createFromAscii("Enabled"),any);
	}
	sal_Int32 step;
	step <<= xControlModel->getPropertyValue(OUString::createFromAscii("Step"))
	step--;
	any <<= step;
	xControlModel->setPropertyValue(OUString::createFromAscii("Step"),any)
  	return salTrue;
  }

Tracing our Counter in a Document

TO DO : this section.

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 satisfied with this control at the moment for different reasons :

  • the select problem : I am not able to see an other node selected than root node. To put it differently if you select a leaf you will never see it as selected in the tree control.
  • the tree expanded listener is usefulness if your node has no child. This gives us a problem when wanting to manage a dynamic tree (for instance for an introspection tool)
  • The event property in OpenOffice.org dialog editor is unable to replace event listener.

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.

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  *****
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() ."
      'http://api.openoffice.org/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 childs 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)
  'oEvt.Source.Select(ogNode) 'HANGS THE OFFICE !!!!
  'XRay oEvt.Source
  'XRay.XRay ogNode
  'MsgBox("Selection Changed")
end sub

Here is the corresponding snapshot

Our tree control with event listener

Home Page

HomePageCpp.png Return to Document Home page

See also

Personal tools