#!/usr/bin/env python3
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
import os
from prompt_toolkit import prompt
import xml.dom.minidom
#-------------------------- generating the xml functions --------------------------
def generate_menu_fragment(addon_name, merge_name, merge_point, merge_command, merge_fallback, merge_context, menu_items):
return ''.join(x for x in [
'',
f'',
f'',
f'{merge_point}' if merge_point else '',
f'{merge_command}' if merge_command else '',
f'{merge_fallback}' if merge_fallback else '',
f'{merge_context}' if merge_context else '',
'',
menu_items,
'',
'',
'',
''
] if x)
def generate_menu_item_fragment(item_name, title, url, image_path, target, submenus=None):
submenu_fragment = None
if submenus:
submenu_items = ''.join(submenus)
submenu_fragment = f'{submenu_items}'
return ''.join(x for x in [
f'',
f'{title}' if title else '',
f'{url}' if url else '',
f'{image_path}' if image_path else '',
f'{target}' if target else '',
submenu_fragment or '',
''
] if x)
def generate_submenu_item_fragment(submenu_name, title, url, image_path, target):
return ''.join(x for x in [
f'',
f'{title}' if title else '',
f'{url}' if url else '',
f'{image_path}' if image_path else '',
f'{target}' if target else '',
''
] if x)
def generate_toolbar_fragment(addon_name, merge_name, merge_toolbar, merge_point, merge_command, merge_fallback, merge_context, toolbar_items):
return ''.join(x for x in [
'',
f'',
f'',
f'{merge_toolbar}' if merge_toolbar else '',
f'{merge_point}' if merge_point else '',
f'{merge_command}' if merge_command else '',
f'{merge_fallback}' if merge_fallback else '',
f'{merge_context}' if merge_context else '',
'',
toolbar_items,
'',
'',
'',
''
] if x)
def generate_toolbar_item_fragment(item_name, title, url, image_path, target, separator_position=None):
return ''.join(x for x in [
f'',
f'{title}' if title else '',
f'{url}' if url else '',
f'{image_path}' if image_path else '',
f'{target}' if target else '',
f'{separator_position}' if separator_position else '',
''
] if x)
def generate_images_fragment(image_small, image_big, image_small_hc, image_big_hc):
return ''.join(x for x in [
'',
f'{image_small}' if image_small else '',
f'{image_big}' if image_big else '',
f'{image_small_hc}' if image_small_hc else '',
f'{image_big_hc}' if image_big_hc else '',
''
] if x)
def generate_addon_menu_fragment(addon_menu_name, title, url, image_path, target):
return ''.join(x for x in [
f'',
f'{title}' if title else '',
f'{url}' if url else '',
f'{image_path}' if image_path else '',
f'{target}' if target else '',
''
] if x)
def generate_help_menu_fragment(help_menu_name, title, url, image_path, target):
return ''.join(x for x in [
f'',
f'{title}' if title else '',
f'{url}' if url else '',
f'{image_path}' if image_path else '',
f'{target}' if target else '',
''
] if x)
def stitching_xcu(addon_name, merge_type, merge_name, merge_point, merge_command, merge_fallback, items, merge_toolbar, merge_context):
if merge_type == 'menu':
menu_items = ''.join(items)
merge_fragment = generate_menu_fragment(addon_name, merge_name, merge_point, merge_command, merge_fallback, merge_context, menu_items=menu_items)
elif merge_type == 'toolbar':
toolbar_items = ''.join(items)
merge_fragment = generate_toolbar_fragment(addon_name, merge_name, merge_toolbar, merge_point, merge_command, merge_fallback, merge_context, toolbar_items=toolbar_items)
else:
merge_fragment = ''
return merge_fragment
def generate_xcu(merge_fragment, output_dir, addon_menu_fragment, help_menu_fragment, images_fragment):
# Determine the output directory
if not output_dir:
while True:
choice = input("\n\nSave Addons.xcu file to desktop or specify a path to custom directory? (d/c): ").lower()
if choice in ("d", "desktop"):
output_dir = os.path.join(os.path.expanduser('~'), 'Desktop')
break
elif choice == "c":
custom_dir = input("\nEnter custom directory path: ")
output_dir = validate_directory(custom_dir)
if output_dir:
break
else:
print("\nInvalid choice. Please choose 'd' or 'c'.")
final_fragment = f"""
{addon_menu_fragment}
{merge_fragment}
{help_menu_fragment}
{images_fragment}
"""
file_path = os.path.join(output_dir, 'Addons.xcu')
with open(file_path, 'w', encoding='utf-8') as file:
dom = xml.dom.minidom.parseString(final_fragment)
pretty_xml_as_string = dom.toprettyxml()
# Post-process to remove unnecessary newlines
pretty_xml_as_string = os.linesep.join([s for s in pretty_xml_as_string.splitlines() if s.strip()])
file.write(pretty_xml_as_string)
#-------------------------- prompt message functions --------------------------
def prompt_merge_context():
print("\nSelect the application module context for the merge instruction:")
print("1. Writer")
print("2. Spreadsheet")
print("3. Presentation")
print("4. Draw")
print("5. Formula")
print("6. Chart")
print("7. Bibliography")
choice = input("Enter your choice (1-7): ").strip()
if choice == '1':
return "com.sun.star.text.TextDocument"
elif choice == '2':
return "com.sun.star.sheet.SpreadsheetDocument"
elif choice == '3':
return "com.sun.star.presentation.PresentationDocument"
elif choice == '4':
return "com.sun.star.drawing.DrawingDocument"
elif choice == '5':
return "com.sun.star.formula.FormulaProperties"
elif choice == '6':
return "com.sun.star.chart.ChartDocument"
elif choice == '7':
return "com.sun.star.frame.Bibliography"
else:
print("Invalid choice. Please select again.")
return prompt_merge_context()
def prompt_images():
print("\nPlease provide paths to the [ .bmp ] images (type 'skip' to skip providing an image):")
image_small = input("Path to small image: ").strip()
if image_small.lower() == 'skip':
image_small = None
else:
image_small = validate_image_path(image_small)
image_big = input("Path to big image: ").strip()
if image_big.lower() == 'skip':
image_big = None
else:
image_big = validate_image_path(image_big)
image_small_hc = input("Path to small high-contrast image: ").strip()
if image_small_hc.lower() == 'skip':
image_small_hc = None
else:
image_small_hc = validate_image_path(image_small_hc)
image_big_hc = input("Path to big high-contrast image: ").strip()
if image_big_hc.lower() == 'skip':
image_big_hc = None
else:
image_big_hc = validate_image_path(image_big_hc)
return image_small, image_big, image_small_hc, image_big_hc
def prompt_addon_name():
addon_name = prompt("\nEnter addon name (extID name, e.g., com...): ").strip()
return addon_name
def prompt_menu_title(menu_type):
title = prompt(f"\nEnter {menu_type} item title (use '~' before the accelerator key, e.g., '~File'): ").strip()
return title
def prompt_button_separator():
print("\nDo you want to add a button separator?")
print("1. Before the first button")
print("2. Between the buttons")
print("3. No separator")
choice = input("Enter your choice (1-3): ").strip()
if choice == '1':
return "before_first"
elif choice == '2':
return "between_buttons"
elif choice == '3':
return "none"
else:
print("Invalid choice. Please select again.")
return prompt_button_separator()
def prompt_image_path(merge_type):
while True:
image_path = prompt(f"\nEnter path to {merge_type} item image (optional, enter 'skip' to skip): ").strip().lower()
if image_path == 'skip':
return None
image_path = validate_image_path(image_path)
if image_path:
return image_path
print("Invalid file type. Please try again or enter 'skip' to skip.")
#-------------------------- validating provided path --------------------------
def validate_directory(directory_path):
if not directory_path:
return None # No output directory provided
try:
if not os.path.isdir(directory_path):
raise ValueError("\nInvalid directory: Path is not a directory")
# Additional validations can be added here if needed
except ValueError as e:
print(f"Error: {e}")
return None
return directory_path
def validate_image_path(image_path):
if not image_path:
return None # No image path provided
try:
if not os.path.exists(image_path):
raise ValueError("\nInvalid path: Path does not exist")
_, ext = os.path.splitext(image_path)
if ext.lower() != '.bmp':
raise ValueError("\nInvalid file type: Only .bmp files are allowed")
# Additional validations can be added here if needed
except ValueError as e:
print(f"Error: {e}")
return None
return image_path
#-------------------------- MAIN --------------------------
def main():
print("\nWelcome to the LibreOffice Addons Configuration Tool!\n")
merge_fragment = ''
addon_name = prompt_addon_name()
addon_menu_fragment = ''
help_menu_fragment = ''
images_fragment = ''
# Prompt for addon menu item
choice = prompt("\nDo you want to create the 'AddonMenu' item (Tools > Add-ons)? (y/n): ").strip().lower()
if choice == 'y':
addon_menu_name = prompt("\nEnter addon menu item name (Tools > Add-ons): ").strip()
addon_menu_title = prompt_menu_title("addon menu")
addon_menu_url = prompt("\nEnter addon menu item URL: ").strip()
addon_menu_target = prompt("\nEnter addon menu item target (_self, _default, _top, _parent, or _blank): ").strip()
addon_menu_image_path = prompt("\nEnter path to addon menu item image (optional): ").strip()
addon_menu_image_path = validate_image_path(addon_menu_image_path)
addon_menu_fragment = generate_addon_menu_fragment(addon_menu_name, addon_menu_title, addon_menu_url, addon_menu_image_path, addon_menu_target)
# Prompt for help menu item
choice = prompt("\nDo you want to create the 'OfficeHelp' item (Help menu)? (y/n): ").strip().lower()
if choice == 'y':
help_menu_name = prompt("\nEnter help menu item name (Help menu): ").strip()
help_menu_title = prompt_menu_title("help menu")
help_menu_url = prompt("\nEnter help menu item URL: ").strip()
help_menu_target = prompt("\nEnter help menu item target (_self, _default, _top, _parent, or _blank): ").strip()
help_menu_image_path = prompt("\nEnter path to help menu item image (optional): ").strip()
help_menu_image_path = validate_image_path(help_menu_image_path)
help_menu_fragment = generate_help_menu_fragment(help_menu_name, help_menu_title, help_menu_url, help_menu_image_path, help_menu_target)
# Prompt for images
choice = prompt("\nImages attribute defines the icons that appear next to the text in the menu and toolbar items.\nDo you want to provide images? (y/n): ").strip().lower()
if choice == 'y':
image_small, image_big, image_small_hc, image_big_hc = prompt_images()
images_fragment = generate_images_fragment(image_small, image_big, image_small_hc, image_big_hc)
while True: # Outer loop for gathering input for each merge
items = [] # Initialize items list for each merge type
merge_type = prompt("\n\nEnter merge type (menu or toolbar): ").strip()
while merge_type not in ['menu', 'toolbar']:
merge_type = prompt("\nInvalid input. \nEnter merge type (menu or toolbar): ").strip()
# Gather input for merge
if merge_type == 'menu':
merge_name = prompt("\nEnter merge name (m name for menu): \nFor example:\nFor a menu merge operation, you could enter something like 'm1' or 'm_print_options': ").strip()
else:
merge_name = prompt("\nEnter merge name (label[] for toolbar): \nFor a toolbar merge operation, you could enter something like [PrintButton] or [FormatToolbar]: ").strip()
merge_point = prompt("\nEnter merge point: ").strip()
merge_command = prompt("\nEnter merge command (possible merge commands are: AddAfter, AddBefore, Replace, Remove): ").strip()
merge_fallback = prompt("\nEnter merge fallback (possible merge fallbacks are: AddAfter, AddBefore, Replace, Remove): ").strip()
merge_toolbar = None
output_dir = None
merge_context = prompt_merge_context()
while True: # Inner loop for gathering input for items
item_name = prompt(f"\nEnter {merge_type} item name (or 'done' to finish): ").strip().lower()
if item_name.lower() == 'done':
break
title = prompt_menu_title(f"{merge_type} item")
url = prompt(f"\nEnter {merge_type} item URL (can be a macro path or dispatch command): ").strip()
target = prompt(f"\nEnter {merge_type} item target (_self, _default, _top, _parent, or _blank): ").strip()
image_path = prompt_image_path(merge_type)
# Prompt for submenus
submenus = []
add_submenu = prompt(f"\nAdd submenu for this {merge_type} item? (y/n): ").strip().lower()
if add_submenu == 'y':
while True:
submenu_name = prompt("\nEnter submenu name (or 'done' to finish): ").strip().lower()
if submenu_name.lower() == 'done':
break
submenu_title = prompt_menu_title("submenu")
submenu_url = prompt("\nEnter submenu URL: ").strip()
submenu_target = prompt("\nEnter submenu target (_self, _default, _top, _parent, or _blank): ").strip()
submenu_image_path = prompt("\nEnter path to submenu image (optional): ").strip()
submenu_image_path = validate_image_path(submenu_image_path)
submenus.append(generate_submenu_item_fragment(submenu_name, submenu_title, submenu_url, submenu_image_path, submenu_target))
# Generate item fragment based on merge type
if merge_type == 'menu':
items.append(generate_menu_item_fragment(item_name, title, url, image_path, target, submenus))
elif merge_type == 'toolbar':
merge_toolbar = prompt("\nEnter the Merge ToolBar name\n(specifies the appearance of the floating toolbar reached via View > Toolbars > Add-on) : ").strip()
separator_position = prompt_button_separator()
items.append(generate_toolbar_item_fragment(item_name, title, url, image_path, target, separator_position))
merge_fragment += stitching_xcu(addon_name, merge_type, merge_name, merge_point, merge_command, merge_fallback, items, merge_toolbar, merge_context)
keep_merging = prompt("\n\n\nContinue to modify Menu or Toolbar? ( y or n ): ").strip().lower()
if keep_merging == 'n':
# Generate .xcu file
generate_xcu(merge_fragment, output_dir, addon_menu_fragment, help_menu_fragment, images_fragment)
break
print("Thank you for using the Addons.xcu file generation. Have a Nice Day!")
if __name__ == "__main__":
main()
# vim: set shiftwidth=4 softtabstop=4 expandtab: