Sunday, June 20, 2010

Creating a Smart Tag

So far I have only been able to get the recognize portion of the smart tag to work. The code for the working portion is here:
package com.example;

import com.sun.star.container.XStringKeyMap;
import com.sun.star.i18n.Boundary;
import com.sun.star.i18n.WordType;
import com.sun.star.uno.XComponentContext;
import com.sun.star.lib.uno.helper.Factory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.registry.XRegistryKey;
import com.sun.star.lib.uno.helper.WeakBase;
import com.sun.star.text.TextMarkupType;


public final class NewSmartTag extends WeakBase
implements com.sun.star.smarttags.XSmartTagRecognizer,
com.sun.star.lang.XServiceInfo
{
private final XComponentContext m_xContext;
private static final String m_SmartTagType = "com.example#newsmarttag";
private static final String m_implementationName = NewSmartTag.class.getName();
private static final String[] m_serviceNames = {
"com.sun.star.smarttags.SmartTagRecognizer" };


public NewSmartTag( XComponentContext context )
{
m_xContext = context;
};

public static XSingleComponentFactory __getComponentFactory( String sImplementationName ) {
XSingleComponentFactory xFactory = null;

if ( sImplementationName.equals( m_implementationName ) )
xFactory = Factory.createComponentFactory(NewSmartTag.class, m_serviceNames);
return xFactory;
}

public static boolean __writeRegistryServiceInfo( XRegistryKey xRegistryKey ) {
return Factory.writeRegistryServiceInfo(m_implementationName,
m_serviceNames,
xRegistryKey);
}

// com.sun.star.lang.XInitialization:
public void initialize(Object[] aArguments) throws com.sun.star.uno.Exception
{
// TODO: Insert your implementation for "initialize" here.
}

// com.sun.star.smarttags.XSmartTagRecognizer:
public int getSmartTagCount()
{
return 1;
}

public String getName(com.sun.star.lang.Locale aLocale)
{
// TODO: Exchange the default return implementation for "getName" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return "Test Smart Tag";
}

public String getDescription(com.sun.star.lang.Locale aLocale)
{
// TODO: Exchange the default return implementation for "getDescription" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return "Testing Smart Tags";
}

public String getSmartTagName(int nSmartTagIndex) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "getSmartTagName" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return m_SmartTagType;
}

public String getSmartTagDownloadURL(int nSmartTagIndex) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "getSmartTagDownloadURL" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return new String();
}

public void recognize(String aText, int nStart, int nLength, com.sun.star.smarttags.SmartTagRecognizerMode eDataType, com.sun.star.lang.Locale aLocale, com.sun.star.text.XTextMarkup xTextMarkup, String aApplicationName, com.sun.star.frame.XController xController, com.sun.star.i18n.XBreakIterator xTokenizer)
{
final int nEndPos = nStart + nLength;
Boundary aWordBounds = xTokenizer.getWordBoundary( aText, nStart, aLocale, WordType.DICTIONARY_WORD, true );
while (aWordBounds.startPos < aWordBounds.endPos && aWordBounds.endPos <=nEndPos)
{
final String aWord = aText.substring(aWordBounds.startPos, aWordBounds.endPos);
if(aWord.equalsIgnoreCase("David"))
{
XStringKeyMap xProps = null;
xTextMarkup.commitTextMarkup(TextMarkupType.SMARTTAG,
m_SmartTagType,
aWordBounds.startPos,
aWordBounds.endPos = aWordBounds.startPos,
xProps);

}
aWordBounds = xTokenizer.nextWord( aText, aWordBounds.startPos, aLocale, WordType.DICTIONARY_WORD );
}
}

public boolean hasPropertyPage(int nSmartTagIndex, com.sun.star.lang.Locale aLocale) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Exchange the default return implementation for "hasPropertyPage" !!!
// NOTE: Default initialized polymorphic structs can cause problems
// because of missing default initialization of primitive types of
// some C++ compilers or different Any initialization in Java and C++
// polymorphic structs.
return false;
}

public void displayPropertyPage(int nSmartTagIndex, com.sun.star.lang.Locale aLocale) throws com.sun.star.lang.IndexOutOfBoundsException
{
// TODO: Insert your implementation for "displayPropertyPage" here.
}

// com.sun.star.lang.XServiceInfo:
public String getImplementationName() {
return m_implementationName;
}

public boolean supportsService( String sService ) {
int len = m_serviceNames.length;

for( int i=0; i < len; i++) {
if (sService.equals(m_serviceNames[i]))
return true;
}
return false;
}

public String[] getSupportedServiceNames() {
return m_serviceNames;
}

}

The recognize function is pretty much the only part that is coded.
First we define the length of the document, then tokenize that string dividing each word. We then set each word as our test and compare it to our string (in this case it is "David" but we could easily replace this with a method with regular expressions. We then set that text to the markup if it matches and then we get the next word. Getting the next word is important because it will give you an infinite loop if you do not.

The other methods are mostly definition methods such as getName, or getDescription and no coding is required to create them.

Sunday, June 13, 2010

Automating OO From .NET

This was an interesting experience, I am a lot more comfortable developing in visual studio but for OO Netbeans has been much easier.

Here is the source:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.uno;
using Microsoft.Win32;
using System.IO;
using System.Runtime.InteropServices;
using unoidl.com.sun.star.frame;


namespace ConsoleApplication1
{
class TestOO
{
public int run()
{
InitOpenOfficeEnvironment();
unoidl.com.sun.star.uno.XComponentContext localContent = uno.util.Bootstrap.bootstrap();
unoidl.com.sun.star.lang.XMultiServiceFactory multiServiceFactory = (unoidl.com.sun.star.lang.XMultiServiceFactory)localContent.getServiceManager();
XComponentLoader componentLoader = (XComponentLoader)multiServiceFactory.createInstance("com.sun.star.frame.Desktop");
XComponent xComponent = componentLoader.loadComponentFromURL("private:factory/swriter", "_blank", 0, new unoidl.com.sun.star.beans.PropertyValue[0]);

((unoidl.com.sun.star.text.XTextDocument)xComponent).getText().setString("Automating OO");


return 0;
}
private void InitOpenOfficeEnvironment()
{
string baseKey;
// OpenOffice being a 32 bit app, its registry location is different in a 64 bit OS
if (Marshal.SizeOf(typeof(IntPtr)) == 8)
baseKey = @"SOFTWARE\Wow6432Node\OpenOffice.org\";
else
baseKey = @"SOFTWARE\OpenOffice.org\";

// Get the URE directory
string key = baseKey + @"LayerDev\URE\1";
RegistryKey reg = Registry.CurrentUser.OpenSubKey(key);
if (reg == null) reg = Registry.LocalMachine.OpenSubKey(key);
string urePath = @"C:\Program Files\OOo-dev 3\URE\bin";
reg.Close();
urePath = Path.Combine(urePath, "bin");

// Get the UNO Path
key = baseKey + @"UNO\InstallPath";
reg = Registry.CurrentUser.OpenSubKey(key);
if (reg == null) reg = Registry.LocalMachine.OpenSubKey(key);
string unoPath = @"C:\Program Files\OOo-dev 3\program";
//reg.Close();

string path;
path = string.Format("{0};{1}", System.Environment.GetEnvironmentVariable("PATH"), urePath);
System.Environment.SetEnvironmentVariable("PATH", path);
System.Environment.SetEnvironmentVariable("UNO_PATH", unoPath);
}

}

This is a console app that when runs opens an instance of open office writer and prints "Automating OO".

The InitOpenOfficeEnvironment() method was code that I found here: http://blog.nkadesign.com/2008/net-working-with-openoffice-3/ and it sets up the path and uno_path environment variables so that open office can run. You could also set your paths manually but this allows for distribution. I had a lot of trouble getting the path from the registry as the code was originally for the openoffice 3 and not oo3 dev.

The next part of the code uses the factory style of programming to allow for easier development. It instantiates an openoffice writer and sets the text.

Sunday, May 30, 2010

Building Open Office on Windows 7

Ok, this was a long and painful process. I encourage you to be patient when building as there are little things that will go wrong and force you to rebuild or recompile.

The instructions for building Open Office can be found here.

To start you first need to download Cygwin. When installing Cygwin make sure you install all the packages found in the instructions (section entitled "required additional packages")

Next download the source code to your local drive. The instructions can be found here. Go to the section titled :

SCM-Access for the current development line (named DEV300) using Mercurial as SCM

You should perform this in Cygwin. To get to your local drive type cd :
From there find where you downloaded the package to and do the command line instructions.

Next go to the requirements section in the first page. There are a number of files and programs you will need to install. For the things that are files make sure that you copy them in the correct directory. Where it says /external/, the files will reside in the OpenOffice directory structure that you created (the src root). I learned the hard way that having some of these files missing can add significant time to both your configure and compile times.

The next thing that I would recommend that you do is read and follow the section titled:

breaking links to executables

Initially I did not do this but if you skip this step there is a chance you will not be able to run the ./configure found later. I followed the instructions exactly when dealing with the awk.exe/gawk.exe example but was able to get away with simply renaming gunzip.exe to gzip.exe.

Next was the installation of the perl modules. This is another step I skipped initially however it was extremely costly as it won't appear as an error in the ./configure step but at the end of the compilation. Make sure you install all the modules (although some may already be installed and up to date)

After doing the setup it is time to run ./configure in the src_root.
My configure command looked like this:
$ ./configure --disable-build-mozilla --with-midl-path="/cygdrive/c/Program Fil
es/Microsoft SDKs/Windows/v6.1/Bin" --with-jdk-home="/cygdrive/c/Program Files/
Java/jdk1.6.0_20" --with-mozilla-build="/cygdrive/c/mozilla-build" -disable-dir
ectx --with-ant-home=/cygdrive/c/apache-ant-1.8.1 --without-junit --with-frame-
home="/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.1" --with-psdk-home="
/cygdrive/c/Program Files/Microsoft SDKs/Windows/v6.1"

Make sure that you change your paths accordingly. The "--disable-build-mozilla" and "--with-mozilla-build" seem redundant but only after I put both in would the configure actually complete. I am not sure why that is but I tried all other combinations and none worked.

If your ./configure gives you errors, it will generally tell you what exactly is wrong so it is fairly easy to correct however it may take a while to get it running perfectly on your machine.

The bootstrap section gave me no trouble and at that point I thought it would be smooth sailing until I got to the dmake command.

Dmake ran (initially) without problems and I had some pretty random errors. One of them was saying it could not access c:/temp, and that was because I didn't have a c:/temp. I created the folder and it continued working.

The compilation took a very very long time on my machine (17+hrs) so be very patient and keep an eye on it in case it stops but you don't have to constantly watch it.

Another good thing is if it stops at a certain point you don't have to recompile everything, just the things that haven't already been compiled.

When it was finished and creating the installer I ran into a few errors, it kept looking for a msi_template/codes.txt file. I simply copied the file from another directory into the directory it was looking for it in.

After a long time fixing and recompiling it finally was able to create the installer file. While running the installer file I recommend some sort of treat for yourself because you deserve it if you made it this far.


Sunday, May 23, 2010

Creating a simple Calc Function Extension

The actual process was very simple, the majority of the work came with installing the correct components on my fresh Windows 7 OS.

The first part was getting and installing Netbeans (6.8 IDE) and the Open Office API.

I selected a new project-> and the Chose OpenOffice.org Calc Add-in.

The next screen will set up your Java class name and Package name as well as the directories to store.

The next screen sets up the function definition. You need to set the function name and return type (as well as the display name in the calc app, the description for the auto generated comments and accessibility name for the blind users). Next you can add and edit the parameters of your function and change their type and names.

After clicking finish you should get some auto generated code under your new project in the source package.

Next open up your package and class that you defined and look for the function in the class.
There is a lot of extra code generated but comments will appear in the code to add your functionality, I did something real simple:

public int addFive(int parmValue)
{
return parmValue + 5;
}

I then saved and made sure there were no compile errors and right clicked on the package->Deploy and Run Extension in OpenOffice.org.

It opens OOo and asks to install the extension. After I accept the installation it opens up calc with a new spreadsheet.

I am able to write in the address bar = ADDFIVE(2) and it will display 7.

Friday, May 14, 2010

First Blog

This is where I plan to detail any interesting things I come across when doing my OSD600 Class