AnotherRecentFileList 
AnotherRecentFileList を題材に PopupMenu コントローラの実装について詳細に説明。
概要 
OpenOffice.org のメニューのうちサブメニューの項目が変化する内容のものは XPopupMenuController インターフェースとして実装されます。たとえば、「ファイル」-「新規作成」や「ツール」-「マクロ」-「マクロの管理」などがあります。
「最近利用したドキュメント」も同じように実装されています。このメニューにはドキュメントの履歴が表示されますが、OOo で利用できる様々なドキュメントの種類が全てごちゃ混ぜに表示されるため分かりにくいと感じる人もいるようです。
そこで、現在のフレームに表示されているドキュメントと同じ種類のドキュメントのみを表示する履歴メニューを作成してみます。
このメニューの作成に必要なものは次のものです。
- 履歴リスト
- フィルターリスト
- XPopupMenuController を備えた UNO コンポーネント
- Controller の登録
- メニューへの登録
履歴リストにはファイル名とそのファイルを開く時に利用されたフィルタ名が保存されています。フィルタ名から度のドキュメントとして開かれたのか判定するためにフィルターリストが必要です。
履歴リスト 
履歴のリストは OOo の構成ファイル中に保存されています。保存されている nodepath は /org.openoffice.Office.Common/History です。各要素は次のような項目を保持しています。ここではヘルプのブックマークのものは無視しました。
要素名 | 説明 |
Size | 全履歴保存サイズ |
List | 全履歴 |
PickListSize | メニューでの履歴表示項目数 |
PickList | メニューでの履歴表示項目 |
全ての履歴と「最近使用したドキュメント」メニューに表示される項目で分けられています。Size および PickListSize は変更でき、どちらも 0-100 の値を取ります (最大値規制されています schema 参照)。
List および PickList 要素はサブノードにそれぞれの履歴についてファイルに関する情報を保存しています。
要素 | 説明 |
URL | ファイルの URL |
Filter | 開くときのフィルター名 (内部形式) |
Title | ドキュメントタイトル |
Password | パスワード |
フィルターリスト 
フィルターのリストは com.sun.star.document.FilterFactory サービスから取得できます。決まった種類のフィルターのリストを取得する場合には com.sun.star.container.XContainerQuery インターフェースを利用すると便利です。
取得されるフィルター情報は FilerDescriptor で指定されます。ここで必要なのはフィルター名 FilterName および DocumentServiceName です。
モジュール名 
ドキュメントのモジュール名はドキュメントを代表するサービス名と同じ名称です。
ポップアップメニュー概要 
ポップアップメニューは com.sun.star.frame.XPopupMenuController インターフェースを実装した UNO コンポーネントが XPopupMenuFactory からインスタンス化されて利用されます。このとき、そのインスタンスはメニューを項目で埋めたり、項目を更新したりといった要求を処理します。
ポップアップメニューを利用するには XPopupMenuController を実装した UNO コンポーネントをコンフィグレーションに登録しておく必要があります。nodepath は /org.openoffice.Office.UI.Controller/Registered/PopupMenu です。
ポップアップメニューコントローラ 
ポップアップメニューコントローラを実装していきます。
ポップアップメニューはメニューの一つの項目として挿入されるためそのメニューを特定するコマンド URL が必要です。ここでは独自のプロトコル mytools.frame: にパス ContextSpecificRecentFileList をつなげて mytools.frame:ContextSpecificRecentFileList とします。
また、ポップアップメニューコントローラの実装名は mytools.frame.ContextSpecificRecentFileList としました。
これらはファイルの最初で定義してあります。
必要なインターフェース 
ポップアップメニューコントローラには一般的な UNO コンポーネントのインターフェース以外に次のものがあります。
- com.sun.star.frame.XPopupMenuController
- com.sun.star.lang.XInitialization
- com.sun.star.frame.XDispatchProvider
- com.sun.star.frame.XStatusListener
実装を特定するために XServiceInfo インターフェースが利用されます。登録した実装名を返すようにします。
- com.sun.star.lang.XServiceInfo
メニュー項目の選択を知るために XMenuListener インターフェースを利用します。
- com.sun.star.awt.XMenuListener
履歴リストが更新されたときにメニューのリストを更新するために変更の通知を受けます。
- com.sun.star.container.XContainerListener
メニューに表示するファイルパスが長いときに省略するために次のインターフェースを利用します。
- com.sun.star.util.XStringAbbreviation
- com.sun.star.util.XStringWidth
__init__ 
ここで作成するポップアップメニューコントローラは簡単な構造なため全てのインターフェースを継承したクラスを一つだけ作成します。XMenuListener や XContainerListener インターフェースなどは別にクラスを用意するのも手です。
上記のように Py-UNO で実装したポップアップメニューコントローラは XInitialize インターフェースを介さずに __init__ に引数が渡されて初期化されます。XInitialization インターフェースの initialize メソッドに渡して引数を処理しています。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | class pmc(unohelper.Base,
XPopupMenuController,
XInitialization,XStatusListener,
XDispatchProvider,
XMenuListener,XContainerListener,XServiceInfo):
""" PopupMenuController implemenation """
def __init__(self,ctx,*args):
"""Initialization of the popupmenu controller.
PopumuMenuController implemented by Py-UNO is instantiated with __init__ function
not initialize method of .lang.XInitialization interface.
"""
#args are .beans.PropertyValue|s. Frame, CommandURL and ModuleName
self.ctx = ctx
self.frame = None # frame of the document
self.modname = "" # module name
self.command = ""
self.list_changed = False
self.file_list = []
self.menu = None
self.history_list = None
if args:
self.initialize(args)
if self.frame:
self.frame.addEventListener(self)
|
XInitialization 
ここで、フレーム Frame やモジュール名 ModuleName を取得します。複数の種類のコマンドを一つのポップアップメニューコントローラで処理するのであれば CommandURL を取得しておきます。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| | # XInitialization
def initialize(self,args):
"""Frame is passed as an argument at the initialization process."""
for arg in args:
if arg.Name == u'Frame':
self.frame = arg.Value
elif arg.Name == u'ModuleName':
#self.modname = arg.Value
if arg.Value.startswith(Mod_sdb_prefix):
# all database modules as OfficeDatabaseDocument
self.modname = Mod_Database
elif arg.Value == Mod_Chart2:
self.modname = Mod_Spreadsheet
else:
self.modname = arg.Value
elif arg.Name == u'CommandURL':
self.command = arg.Value
|
XServiceInfo 
UNO コンポーネントを PopupMenuController サービスに実装名 IMPL_NAME として登録するため、XServiceInfo インターフェースのメソッドでは次のような値を返します。このインターフェースを実装していない場合はポップアップメニューコントローラを見つけられなくなります。
0
1
2
3
4
5
6
7
8
9
| | IMPL_NAME = u'mytools.frame.ContextSpecificRecentFileList'
SERVICE_NAME = u'com.sun.star.frame.PopupMenuController'
# XServiceInfo interface is used to identify the component by the ImplementationName.
def supportsService(self,name):
return (name == SERVICE_NAME)
def getImplementationName(self):
return IMPL_NAME
def getSupportedServiceNames(self):
return (SERVICE_NAME,)
|
XDispatchProvider 
メニューからコマンドで呼ばれるためこのインターフェースが必要です。コマンドが不正であれば無効化することもできます。詳細は DispatchProvider サービスの実装を参考にしてください。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
| | # XDispatchProvider
def queryDispatch(self,url,name,flag):
#print "pmc: querydispatch...",url.Complete
if url.Protocol == Protocol:
if url.Path == Menu_Path:
return self
return None
def queryDispatches(self,args):
disps = []
for arg in args:
disps.append(
self.queryDispatch(arg.FeatureURL,arg.FrameName,arg.SearchFlags))
return tuple(disps)
|
XStatusListener 
ステータスの変更を受け取ります。これも DispatchPovider やメニュー、ツールバーコントローラと同じように利用します。ここでは特にステータスの変更は行いません。
0
1
2
3
| | # XStatusListener
def statusChanged(self,state):
"""This menu is always enabled."""
pass
|
XPopupMenuController 
このインターフェースはメニュー項目のエントリーの作成およびメニュー項目の更新時に呼ばれるメソッドを提供します。
初期化後、メニューが表示される前に setPopupMenu メソッドが呼ばれます。引数として表示するポップアップメニューが渡されます。このメニューは項目更新時に必要であれば変数に保存しておきます。
渡されるのは com.sun.star.awt.XPopupMenu です。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| | # XPopupMenuController
def setPopupMenu(self,menu):
"""Set content of the popup menu passed by the factory."""
if not self.frame: return
if not menu: return
self.menu = menu # keep the menu
try:
self.fill_menu()
except Exception,e:
print e
return
# add menu listener
self.menu.addMenuListener(self)
# adds container listener
self.register_listener()
def updatePopupMenu(self):
"""updatePopupMenu call."""
#print "update pm"
try:
if self.list_changed:
self.clear_menu()
self.fill_menu()
self.register_listener()
self.list_changed = False
except Exception,e:
print e
|
メニューを埋める 
独自のコンテキスト依存の履歴を作成してメニューに挿入します。
まず、履歴のリストとフィルタのリストから現在のドキュメントと同じ種類のドキュメントから成る履歴リストを作成します。そのままメニューに表示すると文字列が長すぎることがあるため省略して表示します。
省略表示は URL を短縮表示に変換 -> システムパスに変換の順に行いますが、win 環境ではこの変換に失敗するため別の方法で行います。また、pyuno の fileUrlToSystemPath による URL からシステムパスへの変換時にマルチバイト文字が含まれていると失敗するバグがあるため変換は urllib を利用することにします (このバグは OOo 2.4.1 で修正されたことになっていますが、日本語が含まれると失敗してしまいます)。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| | def fill_menu(self):
"""Fill menu with entries."""
self.file_list = []
reader = self.__get_history_reader()
# create history list according to the module name
if self.modname in (Mod_StartModule,Mod_BasicIDE,Mod_Database):
self.create_general_history(reader)
else:
self.file_list = create_context_spacific_history(
self.ctx, reader, self.modname)
if not self.file_list:
self.menu.insertItem(1,u'~No Documents.',0,1)
self.menu.enableItem(1,False)
return
ua = self.ctx.ServiceManager.createInstanceWithContext(
u'com.sun.star.util.UriAbbreviation', self.ctx)
sw = string_width()
urlStr = u'URL'
entries = []
if sep == chr(0x5C):
for i,v in enumerate(self.file_list):
if v[urlStr].startswith(u'file:///'):
syspath = self.abbreviation(
unicode(
unquote(
v[urlStr].encode('ascii')),'utf8'
)[8:].replace('/','\\'), 46, '\\')
else:
syspath = ua.abbreviateString(sw,46,v[urlStr])
label = u'~%s: %s' % (i+1, syspath)
self.menu.insertItem(i+1,label,0,i)
else:
for i,v in enumerate(self.file_list):
if v[urlStr].startswith(u'file:///'):
syspath = uno.fileUrlToSystemPath(
ua.abbreviateString(sw,46,v[urlStr]))
else:
syspath = ua.abbreviateString(sw,46,v[urlStr])
label = u'~%s: %s' % (i+1, syspath)
self.menu.insertItem(i+1,label,0,i)
|
履歴の作成 
履歴を作成するには履歴リストの取得およびフィルタリストが必要です。履歴には PickListSize と同じ数だけ項目を表示します。
履歴の List には保存情報が含まれないため、PickList から一部を取得します。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| | def create_context_spacific_history(ctx,reader,modname):
"""Context specific history."""
# make a module specific list
iFlag = 0x1
eFlag = 0x8 + 0x1000 + 0x40000
filter_list = get_filter_list(ctx,modname,iFlag,eFlag)
if not filter_list.has_key(modname):
# not found
return create_general_history(reader)
file_list = []
# size of the list
pk_size = reader.getPropertyValue(u'PickListSize')
# module specific filter names
if modname == Mod_Global:
# GlobalDocument: Global + Text
Global_filters = Set(filter_list.get(Mod_Global,[]))
Text_filters = Set(filter_list.get(Mod_Text,[]))
mod_filters = Global_filters | Text_filters
else:
mod_filters = Set(filter_list[modname])
filterName = u'Filter'
urlStr = u'URL'
# "List" does not list stored file. So, get files form "PickList" and
# complement remains from "List".
# PickList keeps Recent Files entries.
pk_list = reader.getPropertyValue(u'PickList')
if pk_list.hasElements():
for name in pk_list.getElementNames():
pk = pk_list.getByName(name)
fl = pk.getPropertyValue(filterName)
# check the filter
if fl in mod_filters:
element = {}
element[urlStr] = pk.getPropertyValue(urlStr)
element[filterName] = fl
file_list.append(element)
# List keeps all file histories.
hist_list = reader.getPropertyValue(u'List')
if hist_list.hasElements():
for name in hist_list.getElementNames():
hist = hist_list.getByName(name)
fl = hist.getPropertyValue(filterName)
if fl in mod_filters:
element = {}
element[urlStr] = hist.getPropertyValue(urlStr)
element[filterName] = fl
if not element in file_list:
file_list.append(element)
if len(file_list) >= pk_size: break
return file_list
|
フィルタリストの取得 
フィルタリストの取得は FilterFactory からモジュール名およびフラッグを指定して行います。特定のドキュメントの種類で利用されるフィルタのみが取得できます。また、フラッグで内部フィルタなどを排除しています。指定するフィルタは OOo のソースコードを参照。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| | def get_filter_info(descs):
docService = u'DocumentService'
filterName = u'Name'
service = ""
name = ""
for desc in descs:
if desc.Name == docService:
service = desc.Value
elif desc.Name == filterName:
name = desc.Value
return service,name
def get_filter_list(ctx,mod="",iFlag=0,eFlag=0):
"""Get filter list from FilterFactory.
Returned filters are categorized in their module.
filer_list["com.sun.star.sheet.SpreadsheetDocument": ["calc8", "..."], "": ... ]
"""
ff = ctx.ServiceManager.createInstanceWithContext(
u'com.sun.star.document.FilterFactory' ,ctx)
que = u'getSortedFilterList():module=%s:iflags=%s:eflags=%s' % (mod,iFlag,eFlag)
filters = ff.createSubSetEnumerationByQuery(que)
filter_list = {}
get_info = get_filter_info
while filters.hasMoreElements():
fl = filters.nextElement()
service,name = get_info(fl)
if filter_list.has_key(service):
filter_list[service].append(name)
else:
filter_list[service] = [name]
return filter_list
|
XContainerListener 
履歴リストが更新されたときに通知を受けます。何度も受けても仕方ないのでリスナーを削除しておきます。メニュー内容の更新は updatePopupMenu メソッドが呼ばれたときに行うことにします。また、そのときに再度リスナーを設定します。
0
1
2
3
4
5
6
7
8
9
| | # XContainerListener
def elementInserted(self,ev):
self.list_changed = True
self.unregister_listener()
def elementRemoved(self,ev):
self.list_changed = True
self.unregister_listener()
def elementReplaced(self,ev):
self.list_changed = True
self.unregister_listener()
|
XMenuListener 
メニューの項目がクリックされるとファイルを開きます。ポップアップメニューの MenuId が 0 はキャンセルに予約されているので利用してはいけません。
0
1
2
3
4
5
6
7
8
9
10
11
12
| | # XMenuListener
def hihglight(self,ev):
pass
def activate(self,ev):
pass
def deactivate(self,ev):
pass
def select(self,ev):
menu_id = ev.MenuId
if menu_id <= 0: return
if self.file_list and len(self.file_list) < menu_id -1:
self.open_file( self.file_list[menu_id -1] )
|
ファイルを開く 
履歴リストで項目が選択されたときそのファイルを開きます。ファイルを開くのは dispatch で行うと普通にメニューなどから開いたときと同じ動作になります。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| | def open_file(self,entry):
"""Open file with dispatch."""
if not self.frame: return
url = URL()
url.Complete = u'.uno:Open'
#print entry["URL"],entry["Filter"]
transformer = self.ctx.ServiceManager.createInstanceWithContext(
u'com.sun.star.util.URLTransformer', self.ctx)
d,url = transformer.parseStrict(url)
arg1 = create_PropertyValue(u'Referer', u'private:user')
arg2 = create_PropertyValue(u'AsTemplate',False)
arg3 = create_PropertyValue(u'FilterName',entry[u'Filter'])
arg4 = create_PropertyValue(u'SynchronMode',False)
arg5 = create_PropertyValue(u'URL',entry[u'URL'])
arg6 = create_PropertyValue(u'FrameName',u'_default')
args = (arg1,arg2,arg3,arg4,arg5,arg6)
desktop = self.ctx.ServiceManager.createInstanceWithContext(
u'com.sun.star.frame.Desktop', self.ctx)
disp = desktop.queryDispatch(url,u'_self',0)
if disp:
disp.dispatch(url,args)
|
コントローラの登録 
作成したポップアップメニューコントローラは OOo に認識されるようにコンフィグレーションに登録しなければいけません。登録は /org.openoffice.Office.UI.Controller/Registered/PopupMenu に新しく独自名のノードを追加して行います。テンプレート名は org.openoffice.Office.UI.Controller/ControllerType です。
ノード名は他の人のものと同じにならないように適当に独自の名前を付けます。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| |
<oor:component-data
xmlns:oor="http://openoffice.org/2001/registry"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
oor:name="Controller"
oor:package="org.openoffice.Office.UI">
<node oor:name="Registered">
<node oor:name="PopupMenu">
<node oor:name="mytools.frame.ContextSpecificRecentFileList" oor:op="replace">
<prop oor:name="Command">
<value>mytools.frame:ContextSpecificRecentFileList</value>
</prop>
<prop oor:name="Module">
<value></value>
</prop>
<prop oor:name="Controller">
<value>mytools.frame.ContextSpecificRecentFileList</value>
</prop>
</node>
</node>
</node>
</oor:component-data>
|
項目 | 説明 |
Command | コマンド名 |
Module | 利用するモジュール |
Controller | ポップアップメニューコントローラを実装するコンポーネントの実装名 |
メニューへの組み込み 
既存メニューへの組み込みは Addons コンフィグレーションにより行われます。詳細はアドオンツールバーとメニューを参照してください。
「ファイル」メニューの「最近利用したファイル」メニューの一つ下に追加することにします。
位置は「ファイル」メニューのコマンド .uno:PickList にある「最近利用したファイル」 .uno:RecentFileList の下のため .uno:PickList\.uno:RecentFileList となります。追加するためのコマンド AddAfter を指定、追加目標メニューがないときには無視するために AddPath を指定しています。
追加されるメニューの URL には mytools.frame:ContextSpecificRecentFileList を指定します。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| |
<oor:component-data
xmlns:oor="http://openoffice.org/2001/registry"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
oor:name="Addons"
oor:package="org.openoffice.Office">
<node oor:name="AddonUI">
<node oor:name="OfficeMenuBarMerging">
<node oor:name="mytools.frame" oor:op="replace">
<node oor:name="ContextSpecificRecentFileList" oor:op="replace">
<prop oor:name="MergePoint">
<value>.uno:PickList\.uno:RecentFileList</value>
</prop>
<prop oor:name="MergeCommand">
<value>AddAfter</value>
</prop>
<prop oor:name="MergeFallback">
<value>AddPath</value>
</prop>
<prop oor:name="MergeContext">
<value>com.sun.star.text.TextDocument,
com.sun.star.sheet.SpreadsheetDocument,
com.sun.star.presentation.PresentationDocument,
com.sun.star.drawing.DrawingDocument,
com.sun.star.text.GlobalDocument,
com.sun.star.text.WebDocument,
com.sun.star.formula.FormulaProperties</value>
</prop>
<node oor:name="MenuItems">
<node oor:name="a11_show1" oor:op="replace">
<prop oor:name="Title">
<value xml:lang="en-US">~Recent Documents 2</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>mytools.frame:ContextSpecificRecentFileList</value>
</prop>
<prop oor:name="Target">
<value>_self</value>
</prop>
<prop oor:name="Context">
<value>com.sun.star.text.TextDocument,
com.sun.star.sheet.SpreadsheetDocument,
com.sun.star.presentation.PresentationDocument,
com.sun.star.drawing.DrawingDocument,
com.sun.star.text.GlobalDocument,
com.sun.star.text.WebDocument,
com.sun.star.formula.FormulaProperties</value>
</prop>
</node>
</node>
</node>
</node>
</node>
</node>
</oor:component-data>
|
パッケージ作成 
ZIP アーカイブにひとまとめにしたパッケージを作成するまえに次のようなファイルを作成します。
META-INF 
拡張機能に処理させる必要のあるファイルを記述します。
登録する必要があるのは ポップアップメニューコントローラを実装した UNO コンポーネントの pmc.py ファイル、メニューを追加するための Addons.xcu、コントローラを登録するための Controller.xcu ファイルです。これらのファイルは任意の名称に変更します。ファイルの種類は以下のようになります。
0
1
2
3
4
5
6
7
| <?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest>
<manifest:file-entry manifest:full-path="pmc.py"
manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
<manifest:file-entry manifest:full-path="Controller.xcu"
manifest:media-type="application/vnd.sun.star.configuration-data"/>
<manifest:file-entry manifest:full-path="Addons.xcu"
manifest:media-type="application/vnd.sun.star.configuration-data"/>
</manifest:manifest>
|
description.xml 
拡張機能の情報などを記述します。このファイル名は変更してはいけません。
他の拡張機能と区別されるように identifier を指定します。また、バージョンや依存性、ライセンス表示を指定します。
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| <?xml version="1.0" encoding="UTF-8"?>
<description xmlns="http://openoffice.org/extensions/description/2006"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:d="http://openoffice.org/extensions/description/2006">
<identifier value="mytools.frame.ContextSpecificRecentFileList" />
<version value="0.0.4" />
<dependencies>
<OpenOffice.org-minimal-version value="2.3" d:name="OpenOffice.org 2.3" />
</dependencies>
<registration>
<simple-license accept-by="user" default-license-id="this" suppress-on-update="true">
<license-text xlink:href="LICENSE.txt" license-id="this" />
</simple-license>
</registration>
<display-name>
<name lang="en">Context specific recent file list.</name>
</display-name>
</description>
|
パッケージ化 
パッケージの作成は次のようなファイル配置で行います。description.xml および META-INF/manifest.xml ファイル以外のファイルは上記のファイルに指定した位置に配置します。指定によって自由に変更できます (あまりに複雑にネストしたディレクトリに入れるとパス名長問題が発生する場合があります)。
- 適当なディレクトリ内
- META-INF
- description.xml
- pmc.py
- Addons.xcu
- Controller.xcu
- LICENSE.txt
これらのファイルを選択して ZIP アーカイブを作成します。このとき、ファイルがアーカイブの最上層にくるようにしてください。そうでなければ拡張機能マネージャが読み込みに失敗します。
ファイル名を適当に .oxt などに変更して完成です。
インストール 
- OpenOffice.org を起動します
- メニュー - ツール - 拡張機能マネージャを選択します
- 「マイ拡張機能」を選択、追加ボタンを押します
- インストールする拡張機能のファイルを選択します
コメント 