Статьи

Как быстро можно расширить OpenOffice.org?

OpenOffice.org , бесплатный офисный пакет с открытым исходным кодом, может быть расширен на удивление легко. Типичные сценарии в контексте примеров и документации, как правило, предпочитают C ++. Всесторонняя документация для расширений Java растет, но для новичка может быть трудно понять, с чего начать. Шаг за шагом, эта статья покажет, как создать расширение, которое предоставляет PopupMenuController для отображения доступных расширений в каталоге расширений OpenOffice.org.

Вот что мы будем иметь к тому времени, когда закончим процедуру, которая следует ниже. Другими словами, когда вы выбираете всплывающее окно «Wizards», будут перечислены расширения, доступные в каталоге расширений OpenOffice.org:

Из сказанного выше следует, что мы собираемся переопределить пункт меню Wizards, который в контексте выше называется «всплывающим окном» в терминах OpenOffice.org. Это обеспечивается сервисом PopupMenuController. Для этого конкретного сценария нам нужны, помимо нашего Java-кода, два XML-файла вместе с критически важным файлом «Controller.xcu». Этот файл используется для регистрации службы PopupMenuController. Вот:

Что говорит нам вышеуказанный файл? Есть три свойства, которые мы определили. Первый, «Command», указывает, какой из существующих PopupMenuController мы собираемся переопределить. PopupMenuController называется «AutoPilotMenu». Это, однако, не его отображаемое имя. Его отображаемое имя «Волшебники». Для отображения между командами и их отображаемыми именами см. Framework / Article OpenOffice.org 2.x Commands . Второе свойство — «Модуль». Это свойство указывает приложение (т. Е. Writer или Calc или одно или несколько других приложений OpenOffice.org), в котором появится PopupMenuController. Здесь оно пустое и поэтому будет отображаться во всех приложениях. Третье свойство, «Контроллер», определяет класс, который будет реализовывать наш PopupMenuController.

Кроме того, обратите внимание на иерархию элементов выше. Запускается Registered / PopupMenu. Что еще можно зарегистрировать в этом файле? ToolbarControllers и StatusBarControllers. Чтобы увидеть все контроллеры по умолчанию, перейдите в папку установки OpenOffice.org, а затем перейдите в раздел / registry / data / org / openoffice / Office / UI. Там, среди многих других файлов, вы найдете Controller.xcu. Структура этого файла — это то, что мы пытаемся имитировать выше. Поместите указанный выше файл в корневой пакет (т. Е. Безымянный пакет по умолчанию) вашего Java-приложения.

Теперь вам нужно зарегистрировать файл Controller.xcu. Вы делаете это в файле XML с именем uno-extension-manifest.xml. Он должен находиться в том же пакете, что и файл Controller.xcu. Для этого сценария нам нужно зарегистрировать файл Controller.xcu, а также имя JAR, который будет создан. Вот все содержимое файла uno-extension-manifest.xml:

И теперь нам нужен небольшой XML-файл с именем Description.xml, расположенный в том же месте, что и два выше, с содержимым следующим образом:


Далее, давайте посмотрим на некоторый код Java.
Общий процесс на уровне кода в этом случае начинается с регистрации. После того, как вы предоставили код, который регистрирует ваше расширение в сервисах, предоставляемых OpenOffice.org, вы можете реализовать PopupMenuController. Есть также несколько методов, требуемых суперклассами, которые мы оставим нереализованными.

Однако давайте покажем, что в IDE NetBeans есть очень удобный плагин, который делает все намного проще. Плагин OpenOffice.org API существует с 5.5 и ранее. Сейчас он находится в центре обновлений 6.1 Beta, и я использовал его без проблем. Плагин предоставляет мастера и сценарии сборки, так что основная работа, от создания до развертывания, выполняется для вас. Например, шаблоны проектов предоставлены, чтобы вы могли двигаться в нескольких разных направлениях:

Затем, на следующих шагах, как показано ниже, вы можете определить сервис, с которым хотите работать. В этом случае мы хотим работать с com.sun.star.frame.PopupMenuController. Итак, мы выбираем это в шаблоне:

И теперь, в результате этого выбора, когда мастер завершит работу, весь код ниже будет создан для вас, чтобы дать вам точку входа в API OpenOffice.org:

package com.example;

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.ComponentBase;


public final class SearchExtensions extends ComponentBase
implements com.sun.star.lang.XServiceInfo,
com.sun.star.frame.XDispatchProvider,
com.sun.star.frame.XStatusListener,
com.sun.star.lang.XInitialization,
com.sun.star.frame.XPopupMenuController
{
private final XComponentContext m_xContext;
private static final String m_implementationName = SearchExtensions.class.getName();
private static final String[] m_serviceNames = {
"com.sun.star.frame.PopupMenuController" };


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

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

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

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

// 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;
}

// com.sun.star.frame.XDispatchProvider:
public com.sun.star.frame.XDispatch queryDispatch(com.sun.star.util.URL URL, String TargetFrameName, int SearchFlags)
{
// TODO: Exchange the default return implementation for "queryDispatch" !!!
// 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 null;
}

public com.sun.star.frame.XDispatch[] queryDispatches(com.sun.star.frame.DispatchDescriptor[] Requests)
{
// TODO: Exchange the default return implementation for "queryDispatches" !!!
// 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 null;
}

// com.sun.star.lang.XEventListener:
public void disposing(com.sun.star.lang.EventObject Source)
{
// TODO: Insert your implementation for "disposing" here.
}

// com.sun.star.frame.XStatusListener:
public void statusChanged(com.sun.star.frame.FeatureStateEvent State)
{
// TODO: Insert your implementation for "statusChanged" here.
}

// 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.frame.XPopupMenuController:
public void setPopupMenu(com.sun.star.awt.XPopupMenu PopupMenu)
{
// TODO: Insert your implementation for "setPopupMenu" here.
}

public void updatePopupMenu()
{
// TODO: Insert your implementation for "updatePopupMenu" here.
}

}

В дополнение к вышеупомянутому, другой класс также создан. Этот класс централизует регистрацию для всех услуг, предоставляемых вашим добавочным номером. Следовательно это называется «CentralRegistrationClass.java», поскольку это то, что он делает. Я не буду воспроизводить это здесь, потому что все это стандартный код и не очень интересный.

И теперь наша очередь заняться кодированием. Давайте начнем с объявления com.sun.star.frame.XFrame и com.sun.star.awt.XPopupMenu. Последний предоставит всплывающее меню, которое мы создадим позже. Итак, вот мои две дополнительные декларации, ниже тех, которые уже были объявлены:

XFrame m_xFrame;
XPopupMenu m_xPopupMenu;

Next, notice that there are a few very handy methods that we are required to do something with. Two of them are setPopupMenu and updatePopupMenu. For both of them, let’s create a utility method that parses one of the feeds that lists OpenOffice.org extensions:

void fillPopupMenu(XPopupMenu xPopupMenu) {

if (xPopupMenu != null) {

// prepare to read our extensions feed:
BufferedReader in = null;
try {
java.net.URL url = new java.net.URL("http://extensions.services.openoffice.org/taxonomy/term/2/0/feed");
in = new BufferedReader(new InputStreamReader(url.openStream()));
InputSource source = new InputSource(in);

// parse it somehow, in this case using
// org.openide.xml.XMLUtil from the NetBeans APIs:
Document doc = XMLUtil.parse(source, false, false, null, null);
NodeList localList = doc.getElementsByTagName("*");
int length = localList.getLength();
for (int i = 0; i < length; i++) {
org.w3c.dom.Node mainNode = localList.item(i);
String nodeName = mainNode.getNodeName();

// if an element in the XML is "item":
if (nodeName.equals("item")) {
NodeList kids = mainNode.getChildNodes();
for (int k = 0; k < kids.getLength(); k++) {
org.w3c.dom.Node kidNode = kids.item(k);
String kidNodeName = kids.item(k).getNodeName();
String kidValue = kidNode.getTextContent();

//...find its child element called "title":
if (kidNodeName.equals("title")) {

// add the result to the popup menu:
xPopupMenu.insertItem((short)(0), "--> " + kidValue, (short) 0, (short) 0);

}
}
}
}
} catch (SAXException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}

Now that we have that utility method, we can implement the aforementioned required methods:

@Override
public void setPopupMenu(XPopupMenu xPopupMenu) {
// We must have a valid frame reference to fill our popup menu
if (m_xFrame != null) {
m_xPopupMenu = xPopupMenu;
}
// Add a listener to our popup menu
xPopupMenu.addMenuListener((XMenuListener) UnoRuntime.queryInterface(XMenuListener.class, this));

fillPopupMenu(xPopupMenu);
}

@Override
public void updatePopupMenu() {
fillPopupMenu(m_xPopupMenu);
}

Finally, set all the other required methods to null, or make them empty, or let them return true. Those can be filled in later. For example, the select method can be overridden to perform the action that is invoked when an item in the popup menu is selected.

Now, assuming you used NetBeans IDE to create your OpenOffice extension project, you are already good to go. That is because NetBeans IDE provides all the necessary Ant scripts required for the building and packing of your OpenOffice.org extension. Your extension should look something like this:

And, in fact, when you choose «Deploy Extension in Target OpenOffice.org», OpenOffice.org will start up and install your extension, right from inside NetBeans IDE.

Best of all, it is clear that we have only had to deal with the business logic of our OpenOffice.org extension. All the boilerplate code and the building/packaging of our extension have been done under the hood for us. That’s pretty cool and a significant step forward in the viability of OpenOffice.org extensions.

Resources

  • Open Office.org
  • OpenOffice.org SDK
  • Framework/Tutorial/Popup Menu Controller
  • OO Snippets: Adding a Combo-box to a Toolbar
  • Framework/Article OpenOffice.org 2.x Commands