Difference between revisions of "Extensions development basic"

From Apache OpenOffice Wiki
Jump to: navigation, search
(Properties)
(Introducing the OpenOffice.org API)
 
(40 intermediate revisions by 11 users not shown)
Line 6: Line 6:
  
  
=Where do I write the code?=
+
==Where do I write the code?==
 
OpenOffice.org Basic code is stored in modules within libraries. A library can be:  
 
OpenOffice.org Basic code is stored in modules within libraries. A library can be:  
  
Line 12: Line 12:
 
*Just for the current user (My Macros & Dialogs)  
 
*Just for the current user (My Macros & Dialogs)  
 
*Within a document or template, so that its code is only available when that document is open.
 
*Within a document or template, so that its code is only available when that document is open.
 +
 +
Libraries stored in a document can easily be copied, transported and distributed with the document.
  
 
Libraries not stored in a document or template (that is libraries that are shared or for the current user) are referred to as '''OpenOffice.org libraries'''.  
 
Libraries not stored in a document or template (that is libraries that are shared or for the current user) are referred to as '''OpenOffice.org libraries'''.  
Line 28: Line 30:
 
For more information see online help for: Modules and Libraries.
 
For more information see online help for: Modules and Libraries.
  
=Accessing the IDE=
+
==Accessing the IDE==
 
To access the IDE for the first time:  
 
To access the IDE for the first time:  
  
Line 45: Line 47:
 
You should now see something like:  
 
You should now see something like:  
  
<code>[oobas]
+
<source lang="oobas">
 
REM  *****  BASIC  *****
 
REM  *****  BASIC  *****
 
   
 
   
Line 55: Line 57:
 
   
 
   
 
End Sub
 
End Sub
</code>
+
</source>
 
The cursor will be positioned at the start of the <tt>Sub HelloWorld</tt> line.  
 
The cursor will be positioned at the start of the <tt>Sub HelloWorld</tt> line.  
  
Line 64: Line 66:
 
|}
 
|}
  
 
+
==Entering the code==
 
+
=Entering the code=
+
 
Select and delete:  
 
Select and delete:  
  
<code>[oobas]
+
<source lang="oobas">
 
REM  *****  BASIC  *****
 
REM  *****  BASIC  *****
 
   
 
   
Line 75: Line 75:
 
   
 
   
 
End Sub
 
End Sub
</code>
+
</source>
 
Below the line "Sub HelloWorld" type <tt>msgbox "Hello World!"</tt>, so that the editor looks like:  
 
Below the line "Sub HelloWorld" type <tt>msgbox "Hello World!"</tt>, so that the editor looks like:  
  
<code>[oobas]
+
<source lang="oobas">
 
Sub HelloWorld
 
Sub HelloWorld
 
   msgbox "Hello World!"
 
   msgbox "Hello World!"
 
End Sub
 
End Sub
</code>
+
</source>
 
{| border="1"
 
{| border="1"
 
|'''Notes:'''
 
|'''Notes:'''
Line 89: Line 89:
 
|}
 
|}
  
=Running the code=
+
==Running the code==
 
There are several ways of running the Basic code, these include:  
 
There are several ways of running the Basic code, these include:  
  
Directly from the IDE. There is a run button on the macro bar (by default the third control on the second toolbar). This will run the first macro in the current module.  
+
*Directly from the IDE. There is a run button on the macro bar (by default the third control on the second toolbar). This will run the first macro in the current module.  
  
From the tools menu:
+
*From the tools menu:
  
(Version 1.1.x) '''Tools > Macros > Macro…''';
+
**(Version 1.1.x) '''Tools > Macros > Macro…''';
  
(Version 1.9.x and above) '''Tools > Macro > Run Macro…'''  
+
**(Version 1.9.x and above) '''Tools > Macro > Run Macro…'''  
  
Assigning the macro to a [[ key press]].  
+
*Assigning the macro to a [[ key press]].  
  
Assigning the macro to a [[ menu entry]].  
+
*Assigning the macro to a [[ menu entry]].  
  
Assigning the macro to a toolbar button.  
+
*Assigning the macro to a toolbar button.  
  
Creating a [[ control in a document]].  
+
*Creating a [[ control in a document]].  
  
Assigning the macro to an event.  
+
*Assigning the macro to an event.  
  
 
For now try running the "HelloWorld" subroutine by clicking the '''Run''' button. A small dialog titled "soffice" with the text "Hello World!" and an OK button should be displayed.
 
For now try running the "HelloWorld" subroutine by clicking the '''Run''' button. A small dialog titled "soffice" with the text "Hello World!" and an OK button should be displayed.
  
=Saving the code=
+
==Saving the code==
 
The code gets automatically saved whenever the container for the code gets saved. Thus, if the code is in an OpenOffice.org library (shared or users) then it gets automatically saved when OpenOffice.org exits. If the code is in a library which is part of a document it gets saved whenever the document is saved.  
 
The code gets automatically saved whenever the container for the code gets saved. Thus, if the code is in an OpenOffice.org library (shared or users) then it gets automatically saved when OpenOffice.org exits. If the code is in a library which is part of a document it gets saved whenever the document is saved.  
  
Line 118: Line 118:
  
  
=Variables=
+
==Variables==
It is possible to force variable declaration with <tt>Option Explicit</tt> at the top of the module. For a discussion on whether to declare variables or not see: [[http://www.oooforum.org/forum/viewtopic.phtml?t=5845 this discussion]].  
+
It is possible to force variable declaration with <tt>Option Explicit</tt> at the top of the module. For a discussion on whether to declare variables or not, see: [[http://www.oooforum.org/forum/viewtopic.phtml?t=5845 this discussion]].  
  
 
In that discussion the initial author of this wiki thought that variables should always be declared. Since then he has changed to not declaring them. In short it is generally a personal preference. In either case, it is the initial authors preference that variables be named according to the following convention, which is used in the examples in this Wiki:  
 
In that discussion the initial author of this wiki thought that variables should always be declared. Since then he has changed to not declaring them. In short it is generally a personal preference. In either case, it is the initial authors preference that variables be named according to the following convention, which is used in the examples in this Wiki:  
Line 162: Line 162:
  
  
Use long descriptive variable names making use of camel case  
+
Use long descriptive variable names making use of camel case: <tt>nMsgBoxReturn</tt>
  
 
{| border="1"
 
{| border="1"
 
|'''Note:'''
 
|'''Note:'''
|User defined OpenOffice.org Basic variables are not case sensitive. But UNO-API constants are case sensitive.
+
|User-defined OpenOffice.org Basic variables are not case sensitive. But UNO-API constants ''are'' case sensitive.
 
|}
 
|}
  
  
Convention exceptions to long descriptive names is for index variables where i, j, and k are commonly used, and for when a string is being built-up, where s is commonly used.  
+
Convention exceptions to long descriptive names are for index variables where <tt>i, j,</tt> and <tt>k</tt> are commonly used, and for when a string is being built-up, where <tt>s</tt> is commonly used.  
  
 
Edit sub HelloWorld so that it looks like the following and run it:  
 
Edit sub HelloWorld so that it looks like the following and run it:  
  
<code>[oobas]
+
<source lang="oobas">
 
sub HelloWorld
 
sub HelloWorld
 
dim i as integer 'This line is optional
 
dim i as integer 'This line is optional
 
   for i = 0 to 2
 
   for i = 0 to 2
     'These lines are indented for ease of reading only
+
     'These lines are indented for ease of reading only.
     'all your code should be like this for lonog time survival
+
     'All your code should be like this for long-term survival.
 
     msgbox "Hello World " & i
 
     msgbox "Hello World " & i
 
   next i
 
   next i
 
end sub
 
end sub
</code>
+
</source>
 
For more information on variables see the online help for "using variables".
 
For more information on variables see the online help for "using variables".
  
=Understanding the OpenOffice.org API=
+
==Understanding the OpenOffice.org API==
 
This section will start with an example. The remainder of this section aims to give information so that the example can be understood and expanded upon.  
 
This section will start with an example. The remainder of this section aims to give information so that the example can be understood and expanded upon.  
  
 
Try running the following code with different types of documents being active.  
 
Try running the following code with different types of documents being active.  
  
<code>[oobas]
+
<source lang="oobas">
 
sub main
 
sub main
 
'BasicLibraries.loadLibrary("XrayTool")
 
'BasicLibraries.loadLibrary("XrayTool")
Line 201: Line 201:
 
function fnWhichComponent(oDoc) as string
 
function fnWhichComponent(oDoc) as string
 
if HasUnoInterfaces(oDoc, "com.sun.star.lang.XServiceInfo") then  
 
if HasUnoInterfaces(oDoc, "com.sun.star.lang.XServiceInfo") then  
   if thisComponent.supportsService ("com.sun.star.text.GenericTextDocument") then
+
   if oDoc.supportsService ("com.sun.star.text.GenericTextDocument") then
 
       fnWhichComponent = "Text"
 
       fnWhichComponent = "Text"
   elseif thisComponent.supportsService("com.sun.star.sheet.SpreadsheetDocument") then
+
   elseif oDoc.supportsService("com.sun.star.sheet.SpreadsheetDocument") then
 
       fnWhichComponent = "Spreadsheet"
 
       fnWhichComponent = "Spreadsheet"
   elseif thisComponent.supportsService("com.sun.star.presentation.PresentationDocument") then
+
   elseif oDoc.supportsService("com.sun.star.presentation.PresentationDocument") then
 
       fnWhichComponent = "Presentation"
 
       fnWhichComponent = "Presentation"
   elseif thisComponent.supportsService("com.sun.star.drawing.GenericDrawingDocument") then
+
   elseif oDoc.supportsService("com.sun.star.drawing.GenericDrawingDocument") then
 
       fnWhichComponent = "Drawing"
 
       fnWhichComponent = "Drawing"
 
   else
 
   else
Line 216: Line 216:
 
end if
 
end if
 
End function
 
End function
</code>
+
</source>
  
==Subroutine naming convention==
+
===Subroutine naming convention===
 
In the above example the user defined function has a name that starts with the letters "fn". This is the initial author's convention so that people know that this is a user defined function. Similarly, the initial author uses the convention that user defined subroutines start with the letters "sub". When learning a language and an API it can be difficult to know what is built-in and what has been defined elsewhere. This document/wiki will use this convention for naming functions and subroutines.
 
In the above example the user defined function has a name that starts with the letters "fn". This is the initial author's convention so that people know that this is a user defined function. Similarly, the initial author uses the convention that user defined subroutines start with the letters "sub". When learning a language and an API it can be difficult to know what is built-in and what has been defined elsewhere. This document/wiki will use this convention for naming functions and subroutines.
  
==Introducing the OpenOffice.org API==
+
===Introducing the OpenOffice.org API===
 
This section introduces the following terms:  
 
This section introduces the following terms:  
  
Line 242: Line 242:
 
An OpenOffice.org object may have a service, which implements an interface, in which a method description says that another OpenOffice.org object is returned.
 
An OpenOffice.org object may have a service, which implements an interface, in which a method description says that another OpenOffice.org object is returned.
  
==Introspection==
+
Further clarification can be found in [https://wiki.openoffice.org/wiki/Documentation/DevGuide/FirstSteps/Objects,_Interfaces,_and_Services Objects, Interfaces and Services]
HasUnoInterfaces is an OpenOffice.org Basic function for introspection. See this [[http://www.oooforum.org/forum/viewtopic.phtml?t=7068 link]] for information on introspection in other languages.
+
  
HasUnoInterfaces returns true if all of the specified interfaces are available for the specified object.  
+
===Introspection===
 +
<tt>HasUnoInterfaces</tt> is an OpenOffice.org Basic function for introspection. See this [[http://www.oooforum.org/forum/viewtopic.phtml?t=7068 link]] for information on introspection in other languages.  
  
Most OpenOffice.org objects provide the method supportsService because they have the interface com.sun.star.lang.XServiceInfo.  
+
<tt>HasUnoInterfaces</tt> returns true if all of the specified interfaces are available for the specified object.  
  
In the above example, the OpenOffice.org Basic command, '''HasUnoInterfaces''' checks that the current document has the interface com.sun.star.lang.XServiceInfo, because if it doesn't have that interface then it doesn't have the method supportsService, and a run time error would occur if such an object tried to access its nonexistent method.  
+
Most OpenOffice.org objects provide the method <tt>supportsService</tt> because they have the interface <idl>com.sun.star.lang.XServiceInfo</idl>.  
  
'''SupportsService''' is a method which returns true if the specified service is available. The above examples checks for a service to determine the type of document that is currently active.
+
In the above example, the OpenOffice.org Basic command <tt>HasUnoInterfaces</tt> checks that the current document has the interface <idl>com.sun.star.lang.XServiceInfo</idl>, because if it doesn't have that interface then it doesn't have the method <tt>supportsService</tt>, and a run time error would occur if such an object tried to access its nonexistent method.  
  
 +
<tt>SupportsService</tt> is a method which returns true if the specified service is available. The above examples checks for a service to determine the type of document that is currently active.
  
==X-Ray tool==
 
Using HasUnoInterfaces and supportsService gives information about an object at run time, but checking an object like this would be a nightmare for learning? Thankfully Bernard Marcelly has come to our rescue with the X-Ray tool. The X-Ray tool is available from: [[http://ooomacros.org/dev.php#101416 ooomacros]]. Download the zip file, unzip the document, open the document in OpenOffice.org, follow the instructions for installation and set-up.
 
  
Part of the X-Ray tool set-up is to specify a local copy of the OpenOffice.org SDK. Download the [http://www.openoffice.org/dev_docs/source/sdk/ OpenOffice.org SDK] and extract it.  
+
===Xray tool===
 +
Using <tt>HasUnoInterfaces</tt> and <tt>supportsService</tt> gives information about an object at run time, but checking an object like this would be a nightmare for learning? Thankfully, Bernard Marcelly has come to our rescue with the Xray tool. The Xray tool is available from: [[http://berma.pagesperso-orange.fr/Files_en/XrayTool60_en.odt odt installer(en)]]. Download the odt file, open the document in OpenOffice.org, follow the instructions for installation and set-up.
 +
 
 +
Part of the Xray tool set-up is to specify a local copy of the OpenOffice.org SDK. Download the [http://www.openoffice.org/download/other.html#tested-sdk Apache OpenOffice 3.4 SDK] and extract it.  
  
 
In the above example, at the start of the code, there are two commented lines (comments start with an apostrophe):  
 
In the above example, at the start of the code, there are two commented lines (comments start with an apostrophe):  
  
<code>[oobas]
+
<source lang="oobas">
 
'BasicLibraries.loadLibrary("XrayTool")
 
'BasicLibraries.loadLibrary("XrayTool")
 
'xray thisComponent
 
'xray thisComponent
</code>
+
</source>
Now that you have the X-Ray tool installed, uncomment these lines (remove the apostrophes) and rerun the macro.
+
Now that you have the Xray tool installed, uncomment these lines (remove the apostrophes) and rerun the macro.
 
   
 
   
[[image:xray_5.2_en.png]]
+
[[image:Xray60en.png]]
  
 
'''BasicLibraries''' is an OpenOffice.org Basic command that returns an object for accessing the OpenOffice.org libraries. The <tt>loadLibrary</tt> method ensures that the routines in that library are available for use.  
 
'''BasicLibraries''' is an OpenOffice.org Basic command that returns an object for accessing the OpenOffice.org libraries. The <tt>loadLibrary</tt> method ensures that the routines in that library are available for use.  
  
<tt>xray</tt> specifies the xray subroutine within the library XrayTool, thisComponent is the object that is being passed to xray for introspection.  
+
<tt>xray</tt> specifies the <tt>xray</tt> subroutine within the library <tt>XrayTool</tt>. <tt>thisComponent</tt> is the object that is being passed to <tt>xray</tt> for introspection.  
  
To find the object that you want to work with often requires finding or creating it starting from either StarDesktop or thisComponent.
+
To find the object that you want to work with often requires finding or creating it starting from either <tt>StarDesktop</tt> or <tt>thisComponent</tt>.
  
==Desktop, documents, and current selection==
+
===Desktop, documents, and current selection===
StarDesktop and ThisComponent are OpenOffice.org Basic commands that refer to the application and currently active document respectively.  
+
<tt>StarDesktop</tt> and <tt>thisComponent</tt> are OpenOffice.org Basic commands that refer to the application and currently active document respectively.  
  
 
Unlike Microsoft Office, OpenOffice.org is one application with different components. When running some code it maybe useful to check which component is currently active. The above code demonstrates how that checking process can be done.  
 
Unlike Microsoft Office, OpenOffice.org is one application with different components. When running some code it maybe useful to check which component is currently active. The above code demonstrates how that checking process can be done.  
  
The Desktop that StarDesktop refers to is conceptual (historically it actually existed) and can be thought of as the OpenOffice.org application.
+
The Desktop that <tt>StarDesktop</tt> refers to is conceptual (historically it actually existed) and can be thought of as the OpenOffice.org application.
 
+
  
 
==Creating new documents==
 
==Creating new documents==
 
To create a new text document:  
 
To create a new text document:  
  
<code>[oobas]
+
<source lang="oobas">
 
oDoc = StarDesktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, Array())
 
oDoc = StarDesktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, Array())
</code>
+
</source>
 
To create a new spreadsheet document:  
 
To create a new spreadsheet document:  
  
<code>[oobas]
+
<source lang="oobas">
 
oDoc = StarDesktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, Array())
 
oDoc = StarDesktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, Array())
</code>
+
</source>
 
An easier approach would be to write a simple function:  
 
An easier approach would be to write a simple function:  
 
+
<source lang="oobas">
<code>[oobas]
+
 
function fnNewDoc(sDocType as string)
 
function fnNewDoc(sDocType as string)
 
fnNewDoc = StarDesktop.loadComponentFromURL("private:factory/" & sDocType , "_blank", 0, Array())
 
fnNewDoc = StarDesktop.loadComponentFromURL("private:factory/" & sDocType , "_blank", 0, Array())
 
end function
 
end function
</code>
+
</source>
 
Then creating new documents can be achieved with:  
 
Then creating new documents can be achieved with:  
  
<code>[oobas]
+
<source lang="oobas">
 
oDoc = fnNewDoc("swriter")
 
oDoc = fnNewDoc("swriter")
 
oDoc = fnNewDoc("scalc")
 
oDoc = fnNewDoc("scalc")
Line 309: Line 309:
 
oDoc = fnNewDoc("sdraw")
 
oDoc = fnNewDoc("sdraw")
 
oDoc = fnNewDoc("smath")
 
oDoc = fnNewDoc("smath")
</code>
+
</source>
 
See http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XComponentLoader.html .
 
See http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XComponentLoader.html .
  
Line 315: Line 315:
 
The following example shows how to open a file. For information on URLs in OpenOffice.org see [[ URL Basics]].  
 
The following example shows how to open a file. For information on URLs in OpenOffice.org see [[ URL Basics]].  
  
<code>[oobas]
+
<source lang="oobas">
 
sFile = "C:\Documents and Settings\danny\Desktop\MyCalc.sxc" ' Windows
 
sFile = "C:\Documents and Settings\danny\Desktop\MyCalc.sxc" ' Windows
 
sFile = "/home/danny/Desktop/MyCalc.sxc" ' Linux
 
sFile = "/home/danny/Desktop/MyCalc.sxc" ' Linux
 
sURL = ConvertToURL(sFile)
 
sURL = ConvertToURL(sFile)
 
oDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())
 
oDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())
</code>
+
</source>
 
Again it may make sense to make this easier by writing a simple function:  
 
Again it may make sense to make this easier by writing a simple function:  
  
<code>[oobas]
+
<source lang="oobas">
 
function fnOpenDoc(sFile)
 
function fnOpenDoc(sFile)
 
sURL = ConvertToURL(sFile)
 
sURL = ConvertToURL(sFile)
 
fnOpenDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())
 
fnOpenDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())
 
end function
 
end function
</code>
+
</source>
 
Examples of calling the function:  
 
Examples of calling the function:  
  
<code>[oobas]
+
<source lang="oobas">
 
oDoc = fnOpenDoc("C:\Documents and Settings\danny\Desktop\MyCalc.sxc") ' Windows
 
oDoc = fnOpenDoc("C:\Documents and Settings\danny\Desktop\MyCalc.sxc") ' Windows
 
oDoc = fnOpenDoc("/home/danny/Desktop/MyCalc.sxc") ' Linux
 
oDoc = fnOpenDoc("/home/danny/Desktop/MyCalc.sxc") ' Linux
</code>
+
</source>
 +
 
 +
==Opening a new file from a template==
 +
If you want to open a file using an existing template, use the template name instead of the file name. An untitled document will be opened from the template.
 +
 
 +
Alternatively, set the "AsTemplate" property in the MediaDescriptor for the opened template to TRUE, and a new file will be opened based on the template, even if the template was stored by OO as an ordinary file, rather than a template. [[Opening a document]] has some examples.
 +
 
 +
If the template location is known, simply use it. However, OpenOffice keeps at least two sets of templates: global templates available to all users and "My Templates" available to the current user, and the locations of the directories are not obviously available. They can be found by means of the [http://www.openoffice.org/api/docs/common/ref/com/sun/star/util/PathSettings.html PathSettings service] which allows the user to examine the various directory paths known to OpenOffice. The Template property of this service returns a string which is a semi-colon separated list of URLS, each of which represents a possible location for the desired template. It is necessary to search this string and the resulting directories to find the template. The following code finds a user template and opens a document based on it:
 +
 
 +
<source lang="oobas">
 +
Dim PathService as Object
 +
Dim TemplatePath as String    ' The path list
 +
Dim SCPos as Integer          ' The position of the next semi-colon 
 +
Dim MyTemplatePath as String  ' The URL for the user template
 +
 
 +
' substitute your template name here
 +
const TemplateName = "MyTemplate.ott"
 +
 
 +
' This seems to be the place in which OO stores user templates
 +
const UserTemplateDirectory="/user/template"
 +
 
 +
 
 +
PathService=CreateUNOService("com.sun.star.util.PathSettings")
 +
TemplatePath=PathService.Template
 +
MyTemplatePath=""
 +
 
 +
do while len(TemplatePath) >0
 +
  SCPos=InStr (TemplatePath,";")
 +
  if SCPos>0 then
 +
    MyTemplatePath=Left(TemplatePath, SCPos-1)
 +
  else
 +
    MyTemplatePath=TemplatePath
 +
  end if
 +
 
 +
' NOTE: as well as file URLs there are some others which should be ignored
 +
  if InStr(myTemplatePath,"file:///")=1 And _
 +
      Right(myTemplatePath,14) =  UserTemplateDirectory then
 +
    exit do 
 +
  end if
 +
  TemplatePath=mid(TemplatePath, SCPos+1, len(TemplatePath)-SCPos)
 +
  MyTemplatePath=""       
 +
loop
 +
 
 +
if MyTemplatePath<>"" then
 +
  TemplatePath=ConverttoURL(TemplateName)
 +
  MyTemplatePath=MyTemplatePath & "/" & TemplateName
 +
  InvoiceDoc=StarDesktop.LoadComponentfromURL(MytemplatePath, "_blank", 0, Array())
 +
else
 +
  MsgBox "Cannot find the template directory"
 +
end if
 +
</source>
 +
 
 +
This code does not check that the desired template actually exists; if it doesn't, the LoadComponentfromURL call will fail.
  
 
==Current Selection==
 
==Current Selection==
It is common to want to run some code that effects the current selection. <tt>ThisComponent</tt> has the method <tt>getCurrentSelection</tt>. Since many different types of objects could possibly be selected it is common to check that the currently selected object has the service that contains the method that we want to apply to the object.  
+
It is common to want to run some code that affects the current selection. <tt>ThisComponent</tt> has the method <tt>getCurrentSelection</tt>. Since many different types of objects could possibly be selected it is common to check that the currently selected object has the service that contains the method that we want to apply to the object.  
  
 
Edit the main subroutine to the following and rerun it on a text document with different selections. (To select more than one block of text hold down the control key).  
 
Edit the main subroutine to the following and rerun it on a text document with different selections. (To select more than one block of text hold down the control key).  
  
<code>[oobas]
+
<source lang="oobas">
 
sub main
 
sub main
 
BasicLibraries.loadLibrary("XrayTool")
 
BasicLibraries.loadLibrary("XrayTool")
Line 353: Line 405:
 
end if
 
end if
 
end sub
 
end sub
</code>
+
</source>
 
With nothing selected the number of selections is one - the insertion point, with one block of text selected the count is still one, but with two blocks of text the count is three - the insertion point and the two blocks of selected text.  
 
With nothing selected the number of selections is one - the insertion point, with one block of text selected the count is still one, but with two blocks of text the count is three - the insertion point and the two blocks of selected text.  
  
Line 365: Line 417:
 
This next example demonstrates changing a property value for the current selections.  
 
This next example demonstrates changing a property value for the current selections.  
  
<code>[oobas]
+
<source lang="oobas">
 
sub main
 
sub main
basicLibraries.loadLibrary("Xray")
+
BasicLibraries.loadLibrary("XrayTool")
 
if fnWhichComponent(thisComponent) = "Text" then
 
if fnWhichComponent(thisComponent) = "Text" then
 
   oCurSelection = thisComponent.getCurrentSelection()
 
   oCurSelection = thisComponent.getCurrentSelection()
Line 381: Line 433:
 
end if
 
end if
 
end sub
 
end sub
</code>
+
</source>
 
In OpenOffice.org Basic it is possible to shorten the assignment line to:  
 
In OpenOffice.org Basic it is possible to shorten the assignment line to:  
  
<code>[oobas]
+
<source lang="oobas">
 
oCurSelection(i).CharStyleName = "Strong Emphasis"
 
oCurSelection(i).CharStyleName = "Strong Emphasis"
</code>
+
</source>
 
This wiki will try to use the full methods for both indexing and assigning properties. The rationale is that this makes converting the code to other languages easier and also helps the learner to understand what is happening (again I have not been in the habit of doing this so some examples may slip past me).  
 
This wiki will try to use the full methods for both indexing and assigning properties. The rationale is that this makes converting the code to other languages easier and also helps the learner to understand what is happening (again I have not been in the habit of doing this so some examples may slip past me).  
  
Line 393: Line 445:
 
See [[ Current selection]].
 
See [[ Current selection]].
  
=Iterative Access to Subordinate Objects (Enumeration access)=
+
==Iterative Access to Subordinate Objects (Enumeration access)==
 
Sometimes to access the desired object an enumeration is required. For example paragraphs, within a document or within a selection.  
 
Sometimes to access the desired object an enumeration is required. For example paragraphs, within a document or within a selection.  
  
When a Writer document is active and some text is selected, both <tt>thisDocument.getText()</tt> and <tt>thisComponent.getCurrentSelection().getByIndex(i)</tt> have the service: com.sun.star.text.TextRange which has the interface: com.sun.star.container.XContentEnumerationAccess, it is possible to create an enumeration of the paragraphs for the current document or for a particular selection.  
+
When a Writer document is active and some text is selected, both <tt>thisDocument.getText()</tt> and <tt>thisComponent.getCurrentSelection().getByIndex(i)</tt> have the service: <idl>com.sun.star.text.TextRange</idl>, which has the interface: <idl>com.sun.star.container.XContentEnumerationAccess</idl>. It is possible to create an enumeration of the paragraphs for the current document or for a particular selection.  
  
<code>[oobas]
+
<source lang="oobas">
 
' Create enumeration object
 
' Create enumeration object
 
oTextElementEnum = thisComponent.getText().createEnumeration()
 
oTextElementEnum = thisComponent.getText().createEnumeration()
Line 413: Line 465:
 
         end if
 
         end if
 
wend
 
wend
</code>
+
</source>
Exercise 3: Extend the above example to display in a message box all text portions that are bold.  
+
Exercise 3: Extend the above example to display in a message box all text portions that are bold.
  
 
+
==Named access==
=Named access=
+
 
Some objects provide named access to a particular type of subordinate object, some others indexed access, and some both named and indexed access.  
 
Some objects provide named access to a particular type of subordinate object, some others indexed access, and some both named and indexed access.  
  
 
For example if the current document in OpenOffice.org is a spreadsheet then to access a particular sheet can be done by index access:  
 
For example if the current document in OpenOffice.org is a spreadsheet then to access a particular sheet can be done by index access:  
  
<code>[oobas]
+
<source lang="oobas">
 
oSheet = thisComponent.getSheets.getByIndex(0)
 
oSheet = thisComponent.getSheets.getByIndex(0)
</code>
+
</source>
 
or named access:  
 
or named access:  
  
<code>[oobas]
+
<source lang="oobas">
 
oSheet = thisComponent.getSheets.getByName("Sheet1")
 
oSheet = thisComponent.getSheets.getByName("Sheet1")
</code>
+
</source>
 
To check if an object with a particular name already exists use <tt>hasByName</tt>, for example:  
 
To check if an object with a particular name already exists use <tt>hasByName</tt>, for example:  
  
<code>[oobas]
+
<source lang="oobas">
 
if thisComponent.getSheets.hasByName("Sheet1") then
 
if thisComponent.getSheets.hasByName("Sheet1") then
</code>
+
</source>
 
To loop through all the available object names can be done like:  
 
To loop through all the available object names can be done like:  
  
<code>[oobas]
+
<source lang="oobas">
 
mNames = thisComponent.getSheets.getElementnames
 
mNames = thisComponent.getSheets.getElementnames
 
for i = lbound(mNames) to ubound(mNames)
 
for i = lbound(mNames) to ubound(mNames)
 
         msgbox mNames(i)
 
         msgbox mNames(i)
 
next
 
next
</code>
+
</source>
 
   
 
   
  
Some named subordinate objects also provide the interface:  com.sun.star.container.XNameContainer. This interface defines that such objects should have the following methods: <tt>insertByName</tt>, <tt>replaceByname</tt> and <tt>removeByName</tt>.  
+
Some named subordinate objects also provide the interface:  <idl>com.sun.star.container.XNameContainer</idl>. This interface defines that such objects should have the following methods: <tt>insertByName</tt>, <tt>insertNewByName</tt>, <tt>replaceByname</tt> and <tt>removeByName</tt>.  
  
 
E.g.  
 
E.g.  
  
<code>[oobas]
+
<source lang="oobas">
thisComponent.getSheets.insertByName("NewSheet")
+
thisComponent.getSheets.insertNewByName("NewSheet", 0)
</code>
+
</source>
  
=Create new objects=
+
==Create new objects==
 
Some objects have services which implement interfaces to provide specific methods for creating a particular type of object.  
 
Some objects have services which implement interfaces to provide specific methods for creating a particular type of object.  
  
For example if the current document is a Writer document then <tt>thisComponent.getText</tt> is an object that provides the service com.sun.star.text.Text which implements the interface com.sun.star.text.XSimpleText which defines the methods <tt>createTextCursor</tt> and <tt>createTextCursorByRange</tt>. Both of these methods create a text cursor for accessing the text of the document. These cursors are quite independent of the view cursor . The view cursor is visible on the screen and is manipulated by the user (and can be manipulated by program control), where as a text cursor is not visible on the screen and is solely used by program control. The following code snippet demonstrates creating a new text cursor, such that it starts at the same location as the viewCursor and is then moved independent of the view cursor.  
+
For example if the current document is a Writer document then <tt>thisComponent.getText</tt> is an object that provides the service <idl>com.sun.star.text.Text</idl>, which implements the interface <idl>com.sun.star.text.XSimpleText</idl>, which defines the methods <tt>createTextCursor</tt> and <tt>createTextCursorByRange</tt>. Both of these methods create a text cursor for accessing the text of the document. These cursors are quite independent of the view cursor. The view cursor is visible on the screen and is manipulated by the user (and can be manipulated by program control), whereas a text cursor is not visible on the screen and is solely used by program control. The following code snippet demonstrates creating a new text cursor, such that it starts at the same location as the view cursor and is then moved independent of the view cursor.  
  
<code>[oobas]
+
<source lang="oobas">
 
oVC = thisComponent.getCurrentController.getViewCursor
 
oVC = thisComponent.getCurrentController.getViewCursor
 
oCursor = oVC.getText.createTextCursorByRange(oVC)
 
oCursor = oVC.getText.createTextCursorByRange(oVC)
Line 464: Line 515:
 
oCursor.gotoEndOfSentence(true)
 
oCursor.gotoEndOfSentence(true)
 
msgbox oCursor.getString
 
msgbox oCursor.getString
</code>
+
</source>
 +
The string returned by the <tt>oCursor.getString</tt> method is the text of the sentence in which the view cursor currently resides.
  
Some objects are context dependent and get created using the method <tt>createInstance</tt> which is defined in the interface com.sun.star.lang.XMultiServiceFactory. For example to add a rectangle to the first page of a drawing document:  
+
Some objects are context dependent and get created using the method <tt>createInstance</tt> which is defined in the interface <idl>com.sun.star.lang.XMultiServiceFactory</idl>. For example to add a rectangle to the first page of a drawing document:  
  
<code>[oobas]
+
<source lang="oobas">
 
dim aPoint as new com.sun.star.awt.Point
 
dim aPoint as new com.sun.star.awt.Point
 
dim aSize as new com.sun.star.awt.Size
 
dim aSize as new com.sun.star.awt.Size
Line 483: Line 535:
 
   
 
   
 
thisComponent.getDrawPages.getByIndex(0).add(oRectangleShape)
 
thisComponent.getDrawPages.getByIndex(0).add(oRectangleShape)
</code>
+
</source>
  
This example also uses UNO structs see below for more information on UNO stucts.  
+
This example also uses ''UNO structs''. See below for more information on UNO structs.  
  
Some objects are context independent to create these objects use the OpenOffice.org Basic command createUnoService. For example to create the equivalent to StarDesktop:  
+
Some objects are context independent; to create these objects, use the OpenOffice.org Basic command <tt>createUnoService</tt>. For example, to create the equivalent to <tt>StarDesktop</tt>:  
  
<code>[oobas]
+
<source lang="oobas">
 
oDesktop = createUnoService("com.sun.star.frame.Desktop")
 
oDesktop = createUnoService("com.sun.star.frame.Desktop")
</code>
+
</source>
  
 
The process that I use to determine how to access or create an object is as follows:  
 
The process that I use to determine how to access or create an object is as follows:  
  
Does the object already exist if so I should be able to access it from something like <tt>thisComponent</tt>.  
+
Does the object already exist, if so I should be able to access it from something like <tt>thisComponent</tt>.  
  
 
Will the new object belong to another object, if so does the owner have a specific method for creating the object, if so use it.  
 
Will the new object belong to another object, if so does the owner have a specific method for creating the object, if so use it.  
Line 504: Line 556:
  
  
==UNO structs==
+
===UNO structs===
UNO structures can be declared using the OpenOffice.org Basic command <tt>dim</tt>:  
+
UNO structures can be declared using the OpenOffice.org Basic command <tt>dim as new</tt>:  
  
<code>[oobas]
+
<source lang="oobas">
 
dim aPoint as new com.sun.star.awt.Point
 
dim aPoint as new com.sun.star.awt.Point
</code>
+
</source>
 
Or by using the OpenOffice.org Basic command <tt>createUnoStruct</tt>:  
 
Or by using the OpenOffice.org Basic command <tt>createUnoStruct</tt>:  
  
<code>[oobas]
+
<source lang="oobas">
 
aPoint = createUnoStruct("com.sun.star.awt.Point")
 
aPoint = createUnoStruct("com.sun.star.awt.Point")
</code>
+
</source>
  
 
{| border="1"
 
{| border="1"
 
|'''Note:'''
 
|'''Note:'''
|When declaring UNO structs case is important. Note that everything upto the name of the struct is lowercase, and that the name of the struct is in TitleCase.
+
|When declaring UNO structs, case is important. Note that everything up to the name of the struct is lowercase, and that the name of the struct is in TitleCase.
 
|}
 
|}
  
Line 524: Line 576:
  
  
==Creating Listeners and Handlers==
+
===Creating Listeners and Handlers===
 
Through the user interface it is possible to assign macros to some events:  
 
Through the user interface it is possible to assign macros to some events:  
  
Line 531: Line 583:
 
OpenOffice.org versions 1.9.x and above: '''Tools > Customize… > Events'''.  
 
OpenOffice.org versions 1.9.x and above: '''Tools > Customize… > Events'''.  
  
It is also possible to assign macros to a wider range of events using the OpenOffice.org Basic command CreateUnoListener. This same command is used for creating both listeners and handlers. A listener checks for an event and always allows other listeners to respond to the event as well. A handler listens for an event, and can optionally consume the event so that other listeners don't get to act on it.  
+
It is also possible to assign macros to a wider range of events using the OpenOffice.org Basic command <tt>CreateUnoListener</tt>. This same command is used for creating both listeners and handlers. A listener checks for an event and always allows other listeners to respond to the event as well. A handler listens for an event, and can optionally consume the event so that other listeners don't get to act on it.  
  
 
The following example creates a keyHandler:  
 
The following example creates a keyHandler:  
  
<code>[oobas]
+
<source lang="oobas">
 
global IannzExampleKeyHandler
 
global IannzExampleKeyHandler
 
   
 
   
Line 543: Line 595:
 
oController.addKeyHandler(IannzExampleKeyHandler) ' Register the listener
 
oController.addKeyHandler(IannzExampleKeyHandler) ' Register the listener
 
end sub
 
end sub
+
 
+
 
sub RemoveKeyHandler
 
sub RemoveKeyHandler
 
thisComponent.currentController.removeKeyHandler(IannzExampleKeyHandler)
 
thisComponent.currentController.removeKeyHandler(IannzExampleKeyHandler)
 
end sub
 
end sub
+
 
+
 
sub KeyHandler_disposing
 
sub KeyHandler_disposing
 
end sub
 
end sub
 
 
   
 
   
 
function KeyHandler_keyReleased(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
 
function KeyHandler_keyReleased(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
 
         KeyHandler_keyReleased = False     
 
         KeyHandler_keyReleased = False     
 
end function
 
end function
 
 
   
 
   
 
function KeyHandler_keyPressed(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
 
function KeyHandler_keyPressed(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
Line 568: Line 616:
 
end if
 
end if
 
end function
 
end function
</code>
+
</source>
A variable declared as global keeps its value even after the macro exits. In this case we want to be able to use this variable later to remove the handler. As variables declared globally could be used in other libraries to try and avoid conflict I start all my global variables with Iannz my registered name for the OpenOffice.org web site.  
+
A variable declared as global keeps its value even after the macro exits. In this case we want to be able to use this variable later to remove the handler. As variables declared globally could be used in other libraries, to try and avoid conflict I start all my global variables with Iannz my registered name for the OpenOffice.org web site.  
  
<tt>sub SetupKeyHandler</tt> sets up the handler. The first parameter to CreateUnoListener is the staring name for the methods that will be called when that type of event occurs in this example .<tt>"KeyHandler_"</tt>.  
+
<tt>sub SetupKeyHandler</tt> sets up the handler. The first parameter to <tt>CreateUnoListener</tt> is the starting name for the methods that will be called when that type of event occurs: in this example, <tt>"KeyHandler_"</tt>.  
  
The second parameter is the name of the interface for the listener or handler, "com.sun.star.awt.XKeyHandler". The name is case sensitive, everything up to and including the module name is always lowercase, the name of the interface always starts with "X" and the remainder is in TitleCase.  
+
The second parameter is the name of the interface for the listener or handler, "<idl>com.sun.star.awt.XKeyHandler</idl>". The name is case sensitive, everything up to and including the module name is always lowercase, the name of the interface always starts with "X" and the remainder is in TitleCase.  
  
Use the SDK to find out what methods the interface must supply. You must supply routines for all of these methods even if you don't intend to use them. You also need to supply a disposing method. The names of these routines start with the string given in the first parameter to CreateUnoListener , in this example <tt>"KeyHandler_"</tt>.  
+
Use the SDK to find out what methods the interface must supply. You must supply routines for all of these methods even if you don't intend to use them. You also need to supply a disposing method. The names of these routines start with the string given in the first parameter to <tt>CreateUnoListener</tt>, in this example <tt>"KeyHandler_"</tt>.  
  
 
Thus in the example there is <tt>KeyHandler_disposing</tt> and <tt>KeyHandler_keyReleased</tt> which don't actually do anything but are required, and <tt>KeyHandler_keyPressed</tt> which actually does the job.  
 
Thus in the example there is <tt>KeyHandler_disposing</tt> and <tt>KeyHandler_keyReleased</tt> which don't actually do anything but are required, and <tt>KeyHandler_keyPressed</tt> which actually does the job.  
Line 581: Line 629:
 
<tt>sub RemoveKeyHandler</tt> demonstrates how to remove the handler.
 
<tt>sub RemoveKeyHandler</tt> demonstrates how to remove the handler.
  
=OpenOffice.org constants=
+
==OpenOffice.org constants==
 
The above example uses OpenOffice.org constants.  
 
The above example uses OpenOffice.org constants.  
  
E.g. com.sun.star.awt.KeyModifier.MOD2
+
E.g. <tt>com.sun.star.awt.KeyModifier.MOD2</tt>
  
 
OpenOffice.org constants are case sensitive. Everything up to and including the module name is always lowercase. The constant group is in TitleCase. The actual constant name is always UPPERCASE.  
 
OpenOffice.org constants are case sensitive. Everything up to and including the module name is always lowercase. The constant group is in TitleCase. The actual constant name is always UPPERCASE.  
Line 591: Line 639:
  
  
=Using the recorder=
+
==Using the recorder==
 
See [[ The OpenOffice.org recorder and UNO dispatch calls]] section for a discussion on recording UNO Dispatch commands versus writing API calls.
 
See [[ The OpenOffice.org recorder and UNO dispatch calls]] section for a discussion on recording UNO Dispatch commands versus writing API calls.
  
= See also=
+
== See also==
* [[Programming_OooWriter |Programming OOoWriter in C++]] where OOoBascic record are used in C++
+
* [[CookBook|CookBook]]
 +
* [[Text_cursor|Text Cursor in OOoBasic]]
 +
* [[Framework/Article/Easy_To_Use_Message_Boxes|Easy To Use Message Boxes]]
 +
* [[Programming_OooWriter |Programming OOoWriter in C++]] where OOoBasic records are used in C++
 
* A [[XIntrospection_Interface|XIntrospection Interface]] short description. XRay uses this interface.
 
* A [[XIntrospection_Interface|XIntrospection Interface]] short description. XRay uses this interface.
* Download Bernard Marcelly's [http://www.ooomacros.org/dev.php#101416 XRay OOoBasic tool]  
+
* Download Bernard Marcelly's [http://berma.pagesperso-orange.fr/Files_en/XrayTool60_en.odt XRay OOoBasic tool]  
* See [[Object Inspector|The New Object Inspector]]  
+
* See [[Object Inspector|The New Object Inspector]] and [[BASIC/UNO_Object_Browser|The BASIC UNO Object Browser]]
  
 
[[Category:Extensions]]
 
[[Category:Extensions]]
[[Category:Basic:Tutorials]]
+
[[Category:StarBasic]]
 +
[[Category:Documentation/Candidate]]

Latest revision as of 15:34, 27 February 2018

Getting started tutorial

This page provides a getting started guide for writing macros in OpenOffice.org Basic. It assumes some prior programming knowledge. Please edit this page to make it more readable. I hope you will find it useful.

If you already know the basics then you may want the CookBook for wrappers and examples.


Where do I write the code?

OpenOffice.org Basic code is stored in modules within libraries. A library can be:

  • Shared (for a network install - OOo Macros & Dialogs)
  • Just for the current user (My Macros & Dialogs)
  • Within a document or template, so that its code is only available when that document is open.

Libraries stored in a document can easily be copied, transported and distributed with the document.

Libraries not stored in a document or template (that is libraries that are shared or for the current user) are referred to as OpenOffice.org libraries.

To determine the actual folders that contain the OpenOffice.org libraries see:
Tools > Options… > OpenOffice.org > Paths > BASIC.

Note Do not copy or move libraries using operating system commands. Use the macro organizer or package manager instead.


Modules within libraries have a maximum size of 64kb. A library can contain up to 16,000 modules.

For more information see online help for: Modules and Libraries.

Accessing the IDE

To access the IDE for the first time:

OpenOffice.org 1.1.x: Tools > Macros > Macro… >

OpenOffice.org 1.9.x and above: Tools > Macros > Organize macros > OpenOffice.org Basic… >

To get started we will use Module1 in the Standard library which is stored for the current user only:

Type a name for the new macro: HelloWorld

In the macro from listbox select Standard

Click New

You should now see something like:

REM  *****  BASIC  *****
 
Sub Main
 
End Sub
 
Sub HelloWorld
 
End Sub

The cursor will be positioned at the start of the Sub HelloWorld line.

Note Now that while the IDE is running it can be accessed from the OpenOffice.org Window menu, or the task panel provided by the operating system.

Entering the code

Select and delete:

REM  *****  BASIC  *****
 
Sub Main
 
End Sub

Below the line "Sub HelloWorld" type msgbox "Hello World!", so that the editor looks like:

Sub HelloWorld
  msgbox "Hello World!"
End Sub
Notes: The IDE does not provide code completion or help with command syntax as you type, but to get help on a Basic command position the cursor in the command and press F1.
OpenOffice.org Basic commands are not case sensitive. Thus msgbox, MSGBOX and Msgbox will all work.
Strings are enclosed in double quotes.

Running the code

There are several ways of running the Basic code, these include:

  • Directly from the IDE. There is a run button on the macro bar (by default the third control on the second toolbar). This will run the first macro in the current module.
  • From the tools menu:
    • (Version 1.1.x) Tools > Macros > Macro…;
    • (Version 1.9.x and above) Tools > Macro > Run Macro…
  • Assigning the macro to a toolbar button.
  • Assigning the macro to an event.

For now try running the "HelloWorld" subroutine by clicking the Run button. A small dialog titled "soffice" with the text "Hello World!" and an OK button should be displayed.

Saving the code

The code gets automatically saved whenever the container for the code gets saved. Thus, if the code is in an OpenOffice.org library (shared or users) then it gets automatically saved when OpenOffice.org exits. If the code is in a library which is part of a document it gets saved whenever the document is saved.

On the Standard toolbar (by default the top toolbar) in the IDE there is a save button. If the code is in a document or template then clicking this button saves the entire document or template. If the code is in an OpenOffice.org library then just the current library gets saved.


Variables

It is possible to force variable declaration with Option Explicit at the top of the module. For a discussion on whether to declare variables or not, see: [this discussion].

In that discussion the initial author of this wiki thought that variables should always be declared. Since then he has changed to not declaring them. In short it is generally a personal preference. In either case, it is the initial authors preference that variables be named according to the following convention, which is used in the examples in this Wiki:

The first letter of the variable name indicates the type of value that the variable is going to hold, as per the following table (based on a table in Tutorial.pdf by Sun)

Letter Meaning
a Structure
b Boolean (TRUE or FALSE)
e Enumeration. This variable can only have one of a limited set of values.
f Float (3.402823 x 1038 to 1.401298 x 10-45. A single variable can take up to four bytes)
Double (1.79769313486232 x 10308 to 4.94065645841247 x 10-324. A double variable can take up to eight bytes)
Currency (-922337203685477.5808 to +922337203685477.5807 and takes up to eight bytes of memory)
m Array (aka sequence aka matrix)
n Integer (-32768 to 32767.) or
Long (-2147483648 and 2147483647).
o Object, service, or interface
s String (A string variable can store up to 65535 Unicode characters).
x Interface, to indicate that only operations of a particular interface of an object are used
v Variant, Any


Use long descriptive variable names making use of camel case: nMsgBoxReturn

Note: User-defined OpenOffice.org Basic variables are not case sensitive. But UNO-API constants are case sensitive.


Convention exceptions to long descriptive names are for index variables where i, j, and k are commonly used, and for when a string is being built-up, where s is commonly used.

Edit sub HelloWorld so that it looks like the following and run it:

sub HelloWorld
dim i as integer 'This line is optional
  for i = 0 to 2
    'These lines are indented for ease of reading only.
    'All your code should be like this for long-term survival.
    msgbox "Hello World " & i
  next i
end sub

For more information on variables see the online help for "using variables".

Understanding the OpenOffice.org API

This section will start with an example. The remainder of this section aims to give information so that the example can be understood and expanded upon.

Try running the following code with different types of documents being active.

sub main
'BasicLibraries.loadLibrary("XrayTool")
'xray thisComponent
msgbox fnWhichComponent(thisComponent)
end sub
 
 
function fnWhichComponent(oDoc) as string
if HasUnoInterfaces(oDoc, "com.sun.star.lang.XServiceInfo") then 
   if oDoc.supportsService ("com.sun.star.text.GenericTextDocument") then
      fnWhichComponent = "Text"
   elseif oDoc.supportsService("com.sun.star.sheet.SpreadsheetDocument") then
      fnWhichComponent = "Spreadsheet"
   elseif oDoc.supportsService("com.sun.star.presentation.PresentationDocument") then
      fnWhichComponent = "Presentation"
   elseif oDoc.supportsService("com.sun.star.drawing.GenericDrawingDocument") then
      fnWhichComponent = "Drawing"
   else
      fnWhichComponent = "Oops current document something else"
   end if
else
   fnWhichComponent = "Not a document"
end if
End function

Subroutine naming convention

In the above example the user defined function has a name that starts with the letters "fn". This is the initial author's convention so that people know that this is a user defined function. Similarly, the initial author uses the convention that user defined subroutines start with the letters "sub". When learning a language and an API it can be difficult to know what is built-in and what has been defined elsewhere. This document/wiki will use this convention for naming functions and subroutines.

Introducing the OpenOffice.org API

This section introduces the following terms:

  • Interface
  • Module
  • Service
  • Method
  • Property

Understanding the difference between an interface and a service and what a module is, is not essential to being able to write extensions for OpenOffice.org, but it does help interpreting the documentation, and for introspection purposes. You may need to read this section at least twice.

An interface is a definition of a set of methods (and their arguments) that a service which implements that interface must have.

Interfaces are grouped together in modules for naming purposes. All interfaces (and services) start with the name "com.sun.star" then the name of the module then the name of the interface (or service).

For example most services provide the com.sun.star.beans.XPropertySet interface. This interface is stored in the module "beans" and provides access to the properties of a service. A property is a value whereas a method is an action.

An OpenOffice.org object can have many services.

An OpenOffice.org object may have a service, which implements an interface, in which a method description says that another OpenOffice.org object is returned.

Further clarification can be found in Objects, Interfaces and Services

Introspection

HasUnoInterfaces is an OpenOffice.org Basic function for introspection. See this [link] for information on introspection in other languages.

HasUnoInterfaces returns true if all of the specified interfaces are available for the specified object.

Most OpenOffice.org objects provide the method supportsService because they have the interface com.sun.star.lang.XServiceInfo.

In the above example, the OpenOffice.org Basic command HasUnoInterfaces checks that the current document has the interface com.sun.star.lang.XServiceInfo, because if it doesn't have that interface then it doesn't have the method supportsService, and a run time error would occur if such an object tried to access its nonexistent method.

SupportsService is a method which returns true if the specified service is available. The above examples checks for a service to determine the type of document that is currently active.


Xray tool

Using HasUnoInterfaces and supportsService gives information about an object at run time, but checking an object like this would be a nightmare for learning? Thankfully, Bernard Marcelly has come to our rescue with the Xray tool. The Xray tool is available from: [odt installer(en)]. Download the odt file, open the document in OpenOffice.org, follow the instructions for installation and set-up.

Part of the Xray tool set-up is to specify a local copy of the OpenOffice.org SDK. Download the Apache OpenOffice 3.4 SDK and extract it.

In the above example, at the start of the code, there are two commented lines (comments start with an apostrophe):

'BasicLibraries.loadLibrary("XrayTool")
'xray thisComponent

Now that you have the Xray tool installed, uncomment these lines (remove the apostrophes) and rerun the macro.

Xray60en.png

BasicLibraries is an OpenOffice.org Basic command that returns an object for accessing the OpenOffice.org libraries. The loadLibrary method ensures that the routines in that library are available for use.

xray specifies the xray subroutine within the library XrayTool. thisComponent is the object that is being passed to xray for introspection.

To find the object that you want to work with often requires finding or creating it starting from either StarDesktop or thisComponent.

Desktop, documents, and current selection

StarDesktop and thisComponent are OpenOffice.org Basic commands that refer to the application and currently active document respectively.

Unlike Microsoft Office, OpenOffice.org is one application with different components. When running some code it maybe useful to check which component is currently active. The above code demonstrates how that checking process can be done.

The Desktop that StarDesktop refers to is conceptual (historically it actually existed) and can be thought of as the OpenOffice.org application.

Creating new documents

To create a new text document:

oDoc = StarDesktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, Array())

To create a new spreadsheet document:

oDoc = StarDesktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, Array())

An easier approach would be to write a simple function:

function fnNewDoc(sDocType as string)
fnNewDoc = StarDesktop.loadComponentFromURL("private:factory/" & sDocType , "_blank", 0, Array())
end function

Then creating new documents can be achieved with:

oDoc = fnNewDoc("swriter")
oDoc = fnNewDoc("scalc")
oDoc = fnNewDoc("simpress")
oDoc = fnNewDoc("sdraw")
oDoc = fnNewDoc("smath")

See http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XComponentLoader.html .

To open a document

The following example shows how to open a file. For information on URLs in OpenOffice.org see URL Basics.

sFile = "C:\Documents and Settings\danny\Desktop\MyCalc.sxc" ' Windows
sFile = "/home/danny/Desktop/MyCalc.sxc" ' Linux
sURL = ConvertToURL(sFile)
oDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())

Again it may make sense to make this easier by writing a simple function:

function fnOpenDoc(sFile)
sURL = ConvertToURL(sFile)
fnOpenDoc = StarDesktop.loadComponentFromURL(sURL, "_blank", 0, Array())
end function

Examples of calling the function:

oDoc = fnOpenDoc("C:\Documents and Settings\danny\Desktop\MyCalc.sxc") ' Windows
oDoc = fnOpenDoc("/home/danny/Desktop/MyCalc.sxc") ' Linux

Opening a new file from a template

If you want to open a file using an existing template, use the template name instead of the file name. An untitled document will be opened from the template.

Alternatively, set the "AsTemplate" property in the MediaDescriptor for the opened template to TRUE, and a new file will be opened based on the template, even if the template was stored by OO as an ordinary file, rather than a template. Opening a document has some examples.

If the template location is known, simply use it. However, OpenOffice keeps at least two sets of templates: global templates available to all users and "My Templates" available to the current user, and the locations of the directories are not obviously available. They can be found by means of the PathSettings service which allows the user to examine the various directory paths known to OpenOffice. The Template property of this service returns a string which is a semi-colon separated list of URLS, each of which represents a possible location for the desired template. It is necessary to search this string and the resulting directories to find the template. The following code finds a user template and opens a document based on it:

Dim PathService as Object
Dim TemplatePath as String    ' The path list
Dim SCPos as Integer          ' The position of the next semi-colon  
Dim MyTemplatePath as String  ' The URL for the user template
 
' substitute your template name here
const TemplateName = "MyTemplate.ott"
 
' This seems to be the place in which OO stores user templates
const UserTemplateDirectory="/user/template"
 
 
PathService=CreateUNOService("com.sun.star.util.PathSettings")
TemplatePath=PathService.Template
MyTemplatePath=""
 
do while len(TemplatePath) >0
  SCPos=InStr (TemplatePath,";")
  if SCPos>0 then
    MyTemplatePath=Left(TemplatePath, SCPos-1)
  else
    MyTemplatePath=TemplatePath
  end if
 
' NOTE: as well as file URLs there are some others which should be ignored
  if InStr(myTemplatePath,"file:///")=1 And _
      Right(myTemplatePath,14) =  UserTemplateDirectory then
    exit do  
  end if
  TemplatePath=mid(TemplatePath, SCPos+1, len(TemplatePath)-SCPos)
  MyTemplatePath=""        
loop
 
if MyTemplatePath<>"" then
  TemplatePath=ConverttoURL(TemplateName)
  MyTemplatePath=MyTemplatePath & "/" & TemplateName
  InvoiceDoc=StarDesktop.LoadComponentfromURL(MytemplatePath, "_blank", 0, Array())
else
  MsgBox "Cannot find the template directory"
end if

This code does not check that the desired template actually exists; if it doesn't, the LoadComponentfromURL call will fail.

Current Selection

It is common to want to run some code that affects the current selection. ThisComponent has the method getCurrentSelection. Since many different types of objects could possibly be selected it is common to check that the currently selected object has the service that contains the method that we want to apply to the object.

Edit the main subroutine to the following and rerun it on a text document with different selections. (To select more than one block of text hold down the control key).

sub main
BasicLibraries.loadLibrary("XrayTool")
if fnWhichComponent(thisComponent) = "Text" then
        oCurSelection = thisComponent.getCurrentSelection()
        'xray oCurSelection
        if oCurSelection.supportsService("com.sun.star.text.TextRanges") then
                msgbox "There are " & oCurSelection.getCount() & _
                 " selections in the current text document."
        end if
end if
end sub

With nothing selected the number of selections is one - the insertion point, with one block of text selected the count is still one, but with two blocks of text the count is three - the insertion point and the two blocks of selected text.

Exercise 1: Modify the above code so that it works on selected cell ranges in a spreadsheet.

Question1: For two blocks of cells selected, what would be the count for the number of selections?

Properties

Uncomment 'xray oCurSelection so that xray runs, to see that the object that oCurSelection points to has a "property" called Count with a description of "pseudo-prop, read only". It is possible in OpenOffice.org Basic to write oCurSelection.count, but as this is not possible in all other languages accessing the OpenOffice.org API, this Wiki will try to always use the method approach. (I say try because I have not been in the habit of doing this and sometimes I may forget).

This next example demonstrates changing a property value for the current selections.

sub main
BasicLibraries.loadLibrary("XrayTool")
if fnWhichComponent(thisComponent) = "Text" then
   oCurSelection = thisComponent.getCurrentSelection()
   if oCurSelection.supportsService("com.sun.star.text.TextRanges") then
      nCount = oCurSelection.Count
      'xray oCurSelection.getByIndex(0)
      'Warning: The insertion point will have the same action applied twice
      'in this case it doesn't matter, but in others it might.
      for i = 0 to nCount - 1
         oCurSelection.getByIndex(i).setPropertyValue("CharStyleName", "Strong Emphasis")
      next
   end if
end if
end sub

In OpenOffice.org Basic it is possible to shorten the assignment line to:

oCurSelection(i).CharStyleName = "Strong Emphasis"

This wiki will try to use the full methods for both indexing and assigning properties. The rationale is that this makes converting the code to other languages easier and also helps the learner to understand what is happening (again I have not been in the habit of doing this so some examples may slip past me).

Exercise 2: Rewrite the above code so that the warning can be removed.

See Current selection.

Iterative Access to Subordinate Objects (Enumeration access)

Sometimes to access the desired object an enumeration is required. For example paragraphs, within a document or within a selection.

When a Writer document is active and some text is selected, both thisDocument.getText() and thisComponent.getCurrentSelection().getByIndex(i) have the service: com.sun.star.text.TextRange, which has the interface: com.sun.star.container.XContentEnumerationAccess. It is possible to create an enumeration of the paragraphs for the current document or for a particular selection.

' Create enumeration object
oTextElementEnum = thisComponent.getText().createEnumeration()
'or thisComponent.getCurrentSelection().getByIndex(i).createEnumeration()
 
' loop over all text elements
while oTextElementEnum.hasMoreElements()
        oTextElement = oTextElementEnum.nextElement
        if oTextElement.supportsService("com.sun.star.text.TextTable") then
                MsgBox "The current block contains a table."
        end if
        if oTextElement.supportsService("com.sun.star.text.Paragraph") then
                MsgBox "The current block contains a paragraph."
        end if
wend

Exercise 3: Extend the above example to display in a message box all text portions that are bold.

Named access

Some objects provide named access to a particular type of subordinate object, some others indexed access, and some both named and indexed access.

For example if the current document in OpenOffice.org is a spreadsheet then to access a particular sheet can be done by index access:

oSheet = thisComponent.getSheets.getByIndex(0)

or named access:

oSheet = thisComponent.getSheets.getByName("Sheet1")

To check if an object with a particular name already exists use hasByName, for example:

if thisComponent.getSheets.hasByName("Sheet1") then

To loop through all the available object names can be done like:

mNames = thisComponent.getSheets.getElementnames
for i = lbound(mNames) to ubound(mNames)
        msgbox mNames(i)
next


Some named subordinate objects also provide the interface: com.sun.star.container.XNameContainer. This interface defines that such objects should have the following methods: insertByName, insertNewByName, replaceByname and removeByName.

E.g.

thisComponent.getSheets.insertNewByName("NewSheet", 0)

Create new objects

Some objects have services which implement interfaces to provide specific methods for creating a particular type of object.

For example if the current document is a Writer document then thisComponent.getText is an object that provides the service com.sun.star.text.Text, which implements the interface com.sun.star.text.XSimpleText, which defines the methods createTextCursor and createTextCursorByRange. Both of these methods create a text cursor for accessing the text of the document. These cursors are quite independent of the view cursor. The view cursor is visible on the screen and is manipulated by the user (and can be manipulated by program control), whereas a text cursor is not visible on the screen and is solely used by program control. The following code snippet demonstrates creating a new text cursor, such that it starts at the same location as the view cursor and is then moved independent of the view cursor.

oVC = thisComponent.getCurrentController.getViewCursor
oCursor = oVC.getText.createTextCursorByRange(oVC)
oCursor.gotoStartOfSentence(false)
oCursor.gotoEndOfSentence(true)
msgbox oCursor.getString

The string returned by the oCursor.getString method is the text of the sentence in which the view cursor currently resides.

Some objects are context dependent and get created using the method createInstance which is defined in the interface com.sun.star.lang.XMultiServiceFactory. For example to add a rectangle to the first page of a drawing document:

dim aPoint as new com.sun.star.awt.Point
dim aSize as new com.sun.star.awt.Size
 
aPoint.x = 1000
aPoint.y = 1000
 
aSize.Width = 10000
aSize.Height = 10000
 
oRectangleShape = thisComponent.createInstance("com.sun.star.drawing.RectangleShape")
oRectangleShape.Size = aSize
oRectangleShape.Position = aPoint
 
thisComponent.getDrawPages.getByIndex(0).add(oRectangleShape)

This example also uses UNO structs. See below for more information on UNO structs.

Some objects are context independent; to create these objects, use the OpenOffice.org Basic command createUnoService. For example, to create the equivalent to StarDesktop:

oDesktop = createUnoService("com.sun.star.frame.Desktop")

The process that I use to determine how to access or create an object is as follows:

Does the object already exist, if so I should be able to access it from something like thisComponent.

Will the new object belong to another object, if so does the owner have a specific method for creating the object, if so use it.

The new object will belong to another object, but that object doesn't provide a specific method for creating it, but does provide createInstance. If the object doesn't provide createInstance are you sure you are using the correct object, or is it context independent.

I have found working out how to create an object to be quite difficult with existing documentation so I hope that this document/wiki will eventually make this clear.


UNO structs

UNO structures can be declared using the OpenOffice.org Basic command dim as new:

dim aPoint as new com.sun.star.awt.Point

Or by using the OpenOffice.org Basic command createUnoStruct:

aPoint = createUnoStruct("com.sun.star.awt.Point")
Note: When declaring UNO structs, case is important. Note that everything up to the name of the struct is lowercase, and that the name of the struct is in TitleCase.



Creating Listeners and Handlers

Through the user interface it is possible to assign macros to some events:

OpenOffice.org versions 1.1.x: Tools > Configure… > Events.

OpenOffice.org versions 1.9.x and above: Tools > Customize… > Events.

It is also possible to assign macros to a wider range of events using the OpenOffice.org Basic command CreateUnoListener. This same command is used for creating both listeners and handlers. A listener checks for an event and always allows other listeners to respond to the event as well. A handler listens for an event, and can optionally consume the event so that other listeners don't get to act on it.

The following example creates a keyHandler:

global IannzExampleKeyHandler
 
sub SetupKeyHandler
oController = thisComponent.currentController
IannzExampleKeyHandler = CreateUnoListener("KeyHandler_","com.sun.star.awt.XKeyHandler")
oController.addKeyHandler(IannzExampleKeyHandler) ' Register the listener
end sub
 
sub RemoveKeyHandler
thisComponent.currentController.removeKeyHandler(IannzExampleKeyHandler)
end sub
 
sub KeyHandler_disposing
end sub
 
function KeyHandler_keyReleased(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
        KeyHandler_keyReleased = False    
end function
 
function KeyHandler_keyPressed(oKeyEvent as new com.sun.star.awt.KeyHandler) as boolean
KeyHandler_keyPressed = false   'Let other listeners handle the event
if oKeyEvent.modifiers = com.sun.star.awt.KeyModifier.MOD2 then 'Control key was pressed
        if oKeyEvent.keyCode = com.sun.star.awt.Key.Q then
                msgbox "Alt + Q was pressed"
                KeyHandler_keyPressed = true    'Don't let other listeners process this event
        end if
end if
end function

A variable declared as global keeps its value even after the macro exits. In this case we want to be able to use this variable later to remove the handler. As variables declared globally could be used in other libraries, to try and avoid conflict I start all my global variables with Iannz my registered name for the OpenOffice.org web site.

sub SetupKeyHandler sets up the handler. The first parameter to CreateUnoListener is the starting name for the methods that will be called when that type of event occurs: in this example, "KeyHandler_".

The second parameter is the name of the interface for the listener or handler, "com.sun.star.awt.XKeyHandler". The name is case sensitive, everything up to and including the module name is always lowercase, the name of the interface always starts with "X" and the remainder is in TitleCase.

Use the SDK to find out what methods the interface must supply. You must supply routines for all of these methods even if you don't intend to use them. You also need to supply a disposing method. The names of these routines start with the string given in the first parameter to CreateUnoListener, in this example "KeyHandler_".

Thus in the example there is KeyHandler_disposing and KeyHandler_keyReleased which don't actually do anything but are required, and KeyHandler_keyPressed which actually does the job.

sub RemoveKeyHandler demonstrates how to remove the handler.

OpenOffice.org constants

The above example uses OpenOffice.org constants.

E.g. com.sun.star.awt.KeyModifier.MOD2

OpenOffice.org constants are case sensitive. Everything up to and including the module name is always lowercase. The constant group is in TitleCase. The actual constant name is always UPPERCASE.

Programmers not using OpenOffice.org Basic may not have access to these constants.


Using the recorder

See The OpenOffice.org recorder and UNO dispatch calls section for a discussion on recording UNO Dispatch commands versus writing API calls.

See also

Personal tools