#!/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: