Added the ability to store and retrieve macros in the document macro storage.

This commit is contained in:
依瑪貓 2017-04-28 17:29:08 +08:00
parent 763703b96a
commit a47e65a50c
3 changed files with 212 additions and 47 deletions

View File

@ -256,6 +256,13 @@ LIBRARY The name of the Basic library. Default to the same
Run he specific macro after synchronization, for
convenience.
--user Store the macros in the user macro storage. (default)
--doc Store the macros in the document macro storage.
--target TARGET The target storage document if there are more than one
opened documents. A partial path is OK.
-h, --help Show the help message and exit
-v, --version Show programs version number and exit
@ -264,7 +271,7 @@ LIBRARY The name of the Basic library. Default to the same
COPYRIGHT
---------
Copyright (c) 2016 imacat.
Copyright (c) 2016-2017 imacat.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -3,7 +3,7 @@
# Office Basic macro source synchronizer.
# by imacat <imacat@mail.imacat.idv.tw>, 2016-08-31
# Copyright (c) 2016 imacat.
# Copyright (c) 2016-2017 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -86,6 +86,13 @@ LIBRARY The name of the Basic library. Default to the same
Run he specific macro after synchronization, for
convenience.
--user Store the macros in the user macro storage. (default)
--doc Store the macros in the document macro storage.
--target TARGET The target storage document if there are more than one
opened documents. A partial path is OK.
-h, --help Show the help message and exit
-v, --version Show programs version number and exit
@ -133,9 +140,8 @@ def main():
# Downloads the macros from OpenOffice/LibreOffice Basic
if args.get:
oo = Office(args.port)
libraries = oo.service_manager.createInstance(
"com.sun.star.script.ApplicationScriptLibraryContainer")
modules = read_basic_modules(libraries, args.library)
storage = find_storage(oo, args.storage_type, args.target)
modules = read_basic_modules(storage, args.library)
if len(modules) == 0:
print("ERROR: Library %s does not exist" % args.library,
file=sys.stderr)
@ -153,19 +159,10 @@ def main():
file=sys.stderr)
return
oo = Office(args.port)
libraries = oo.service_manager.createInstance(
"com.sun.star.script.ApplicationScriptLibraryContainer")
update_basic_modules(libraries, args.library, modules, oo)
storage = find_storage(oo, args.storage_type, args.target)
update_basic_modules(storage, args.library, modules)
if args.run is not None:
factory = oo.service_manager.DefaultContext.getByName(
"/singletons/com.sun.star.script.provider."
"theMasterScriptProviderFactory")
provider = factory.createScriptProvider("")
script = provider.getScript(
"vnd.sun.star.script:%s.%s"
"?language=Basic&location=application" %
(args.library, args.run))
script.invoke((), (), ())
run_macro(storage, args.library, args.run)
print("Done. %02d:%02d elapsed." %
(int((time.time() - t_start) / 60),
@ -206,6 +203,18 @@ def parse_args():
parser.add_argument(
"-r", "--run", metavar="MODULE.MACRO",
help="The macro to run after the upload, if any.")
parser.add_argument(
"--user", dest="storage_type",
action="store_const", const="user",
help="Store the macros in the user macro storage. (default)")
parser.add_argument(
"--doc", dest="storage_type",
action="store_const", const="doc",
help="Store the macros in the document macro storage.")
parser.add_argument(
"--target", metavar="TARGET",
help=("The target storage document if there are more than one"
" opened documents. A partial path is OK."))
parser.add_argument(
"-v", "--version", action="version", version="%(prog)s 0.4")
args = parser.parse_args()
@ -219,9 +228,117 @@ def parse_args():
if args.ext[0] != ".":
args.ext = "." + args.ext
if args.storage_type is None:
args.storage_type = "user"
# Paths are understood locally, despite of the content encoding.
if args.target is not None:
args.target = args.target.decode(locale.getpreferredencoding())
return
def find_storage(oo, type, target):
"""Finds the macro storage to store the macros.
Arguments:
type: The storage type, either "user" or "doc".
target: The file path to locate the storing document if there
are more than one opened documents. A partial path
is OK.
Returns:
The storage to save the macros, as a Storage object.
"""
if type == "user":
storage = Storage()
storage.type = type
storage.oo = oo
storage.doc = None
storage.libs = oo.service_manager.createInstance(
"com.sun.star.script.ApplicationScriptLibraryContainer")
return storage
elif type == "doc":
storage = Storage()
storage.type = type
storage.oo = oo
storage.doc = find_doc(oo, target)
storage.libs = storage.doc.getPropertyValue("BasicLibraries")
return storage
else:
return None
def find_doc(oo, target):
"""Find the target opened document by a partial path.
Arguments:
target: A partial path of the document.
Returns:
If there is only one opened document, it is returned. If
there are more than one opened document, the document whose
file path matches the "target" is returned. Otherwise, if
no matching document or more than one matching document are
found, the program exists with an error.
"""
# Checks the opened documents
enum = oo.desktop.getComponents().createEnumeration()
opened = []
while enum.hasMoreElements():
component = enum.nextElement()
if component.supportsService(
"com.sun.star.document.OfficeDocument"):
opened.append(component)
if len(opened) == 0:
print("ERROR: Found no opened document to store the macros",
file=sys.stderr)
sys.exit(1)
# There are opened documents.
if target is None:
if len(opened) == 1:
return opened[0]
print("ERROR: There are more than one opened documens."
" Please specify the file path.",
file=sys.stderr)
sys.exit(1)
file_content_provider = oo.service_manager.createInstance(
"com.sun.star.ucb.FileContentProvider")
matched = []
for doc in opened:
if doc.hasLocation():
path = file_content_provider.getSystemPathFromFileURL(
doc.getLocation())
else:
path = doc.getTitle()
if path.find(target) >= 0:
matched.append(doc)
if len(matched) == 1:
return matched[0]
elif len(matched) == 0:
print("ERROR: Found no matching document to store the macros.",
file=sys.stderr)
print("Opened documents:", file=sys.stderr)
for doc in opened:
if doc.hasLocation():
path = file_content_provider.getSystemPathFromFileURL(
doc.getLocation())
else:
path = doc.getTitle()
print("* %s" % path, file=sys.stderr)
sys.exit(1)
else:
print("ERROR: There are more than one matching documents.",
file=sys.stderr)
print("Matching documents:", file=sys.stderr)
for doc in matched:
if doc.hasLocation():
path = file_content_provider.getSystemPathFromFileURL(
doc.getLocation())
else:
path = doc.getTitle()
print("* %s" % path, file=sys.stderr)
sys.exit(1)
def read_in_source_dir(projdir, ext, encoding):
"""Read-in the source files.
@ -289,15 +406,13 @@ def update_source_dir(projdir, modules, ext, encoding):
return
def read_basic_modules(libraries, libname):
def read_basic_modules(storage, libname):
"""
Read the OpenOffice/LibreOffice Basic macros from the macro
storage.
Arguments:
libraries: The Basic library storage, as a
com.sun.star.script.ApplicationScriptLibraryContainer
service object.
storage: The Basic macro storage, as a Storage object.
libname: The name of the Basic library.
Returns:
@ -306,41 +421,44 @@ def read_basic_modules(libraries, libname):
dictionary are the module contents.
"""
modules = {}
if not libraries.hasByName(libname):
if not storage.libs.hasByName(libname):
return modules
libraries.loadLibrary(libname)
library = libraries.getByName(libname)
storage.libs.loadLibrary(libname)
library = storage.libs.getByName(libname)
for modname in library.getElementNames():
modules[modname] = library.getByName(modname)
return modules
def update_basic_modules(libraries, libname, modules, oo):
def update_basic_modules(storage, libname, modules):
"""Update the OpenOffice/LibreOffice Basic macro storage.
Arguments:
libraries: The Basic library storage, as a
com.sun.star.script.ApplicationScriptLibraryContainer
service object.
storage: The Basic macro storage, as a Storage object.
libname: The name of the Basic library.
modules: The Basic modules, as a dictionary. The keys of
the dictionary are the module names, and the values of
the dictionary are the module contents.
oo: The OpenOffice/LibreOffice connection, as an Office
object.
"""
if not libraries.hasByName(libname):
libraries.createLibrary(libname)
if not storage.libs.hasByName(libname):
storage.libs.createLibrary(libname)
print("Script library %s created." % libname, file=sys.stderr)
create_dialog_library(oo, libname)
library = libraries.getByName(libname)
create_dialog_library(storage, libname)
library = storage.libs.getByName(libname)
for modname in sorted(modules.keys()):
library.insertByName(modname, modules[modname])
print("Module %s added." % modname, file=sys.stderr)
else:
libraries.loadLibrary(libname)
library = libraries.getByName(libname)
curmods = sorted(library.getElementNames())
storage.libs.loadLibrary(libname)
library = storage.libs.getByName(libname)
# As of OpenOffice 4.1.3, when there is no modules in the
# document storage, it returns a zero-length byte sequence
# instead of an empty string list.
# The byte sequence cannot be sorted directly.
curmods = library.getElementNames()
if len(curmods) == 0:
curmods = []
curmods = sorted(curmods)
for modname in sorted(modules.keys()):
if modname not in curmods:
library.insertByName(modname, modules[modname])
@ -352,28 +470,57 @@ def update_basic_modules(libraries, libname, modules, oo):
if modname not in modules:
library.removeByName(modname)
print("Module %s removed." % modname, file=sys.stderr)
if libraries.isModified():
libraries.storeLibraries()
if storage.libs.isModified():
storage.libs.storeLibraries()
else:
print("Everything is in sync.", file=sys.stderr)
return
def create_dialog_library(oo, libname):
def create_dialog_library(storage, libname):
"""Create the dialog library.
Arguments:
oo: The OpenOffice/LibreOffice connection, as an Office
object.
storage: The Basic macro storage, as a Storage object.
libname: The name of the dialog library.
"""
libraries = oo.service_manager.createInstance(
if storage.type is "user":
libraries = storage.oo.service_manager.createInstance(
"com.sun.star.script.ApplicationDialogLibraryContainer")
else:
libraries = storage.doc.getPropertyValue("DialogLibraries")
libraries.createLibrary(libname)
print("Dialog library %s created." % libname, file=sys.stderr)
libraries.storeLibraries()
def run_macro(storage, libname, macro):
"""Run a Basic macro.
Arguments:
storage: The Basic macro storage, as a Storage object.
libname: The name of the dialog library.
macro: The The macro to run.
"""
if storage.type is "user":
factory = storage.oo.service_manager.DefaultContext.getByName(
"/singletons/com.sun.star.script.provider."
"theMasterScriptProviderFactory")
provider = factory.createScriptProvider("")
script = provider.getScript(
"vnd.sun.star.script:%s.%s"
"?language=Basic&location=application" %
(args.library, args.run))
script.invoke((), (), ())
else:
provider = storage.doc.getScriptProvider()
script = provider.getScript(
"vnd.sun.star.script:%s.%s"
"?language=Basic&location=document" %
(args.library, args.run))
script.invoke((), (), ())
def read_file(path, encoding):
"""Read a file, and deals with Python 3 / 2 compatibility.
@ -448,6 +595,17 @@ def update_file(path, content, encoding):
return is_updated
class Storage:
"""A Basic macro storage.
Attributes:
oo: The office connection, as an Office() object.
doc: The office document component to store the macros.
libs: The Basic macro storage of the document.
"""
pass
class Office:
"""The OpenOffice/LibreOffice connection.

View File

@ -3,7 +3,7 @@
# Python setuptools installer for the obasync project.
# by imacat <imacat@mail.imacat.idv.tw>, 2016-12-20
# Copyright (c) 2016 imacat.
# Copyright (c) 2016-2017 imacat.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.