90 lines
4.7 KiB
Python
90 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
from copy import deepcopy
|
|
from typing import Any
|
|
|
|
from bedit_core.models import Component, ComponentID, ConnectionID, Document, GraphImplementation, ID, ParameterID, PortID
|
|
from bedit_core.serialization.schema import document_from_data, document_to_data
|
|
from bedit_gui.documents import Document as GuiDocument
|
|
from bedit_gui.models import Icon, ShapeID
|
|
|
|
FORMAT_VERSION = 1
|
|
COMPONENTS_MIME = "application/x-bedit-components+json"
|
|
|
|
|
|
def export_components(document: GuiDocument, components: list[Component]) -> dict[str, Any]:
|
|
roots = {document.component_id(component): component for component in components}
|
|
component_ids = _all_component_ids(roots)
|
|
icons = {}
|
|
for component_id in component_ids:
|
|
icon = document.stored_component_icon(component_id)
|
|
if icon is not None:
|
|
icons[component_id] = icon
|
|
return export_component_data(roots, icons)
|
|
|
|
|
|
def export_component_data(components: dict[ComponentID, Component], icons: dict[ComponentID, Icon]) -> dict[str, Any]:
|
|
serialized = document_to_data(Document(format_version=1, id=ID(), name="Clipboard", root=components))
|
|
component_ids = set(_all_component_ids(components))
|
|
icon_data = {str(component_id): icon.to_data() for component_id, icon in icons.items() if component_id in component_ids}
|
|
return {"format_version": FORMAT_VERSION, "type": "components", "components": serialized["root"], "icons": icon_data}
|
|
|
|
|
|
def import_components(payload: dict[str, Any]) -> tuple[dict[ComponentID, Component], dict[ComponentID, Icon]]:
|
|
if payload.get("format_version") != FORMAT_VERSION or payload.get("type") != "components":
|
|
raise ValueError("unsupported component clipboard format")
|
|
components = payload.get("components")
|
|
if not isinstance(components, dict):
|
|
raise TypeError("component clipboard payload must contain a components object")
|
|
clipboard_document = document_from_data({"format_version": 1, "id": str(ID()), "name": "Clipboard", "root": components, "metadata": None})
|
|
component_map: dict[ComponentID, ComponentID] = {}
|
|
port_map: dict[PortID, PortID] = {}
|
|
remapped = _remap_components(clipboard_document.root, component_map, port_map)
|
|
icon_data = payload.get("icons", {})
|
|
if not isinstance(icon_data, dict):
|
|
raise TypeError("component clipboard icons must be an object")
|
|
icons: dict[ComponentID, Icon] = {}
|
|
for old_id, data in icon_data.items():
|
|
new_component_id = component_map.get(ComponentID(old_id))
|
|
if new_component_id is None or not isinstance(data, dict):
|
|
continue
|
|
icon = Icon.from_data(data)
|
|
icon.shapes = {ShapeID(): shape for shape in icon.shapes.values()}
|
|
icon.port_positions = {port_map[port_id]: position for port_id, position in icon.port_positions.items() if port_id in port_map}
|
|
icons[new_component_id] = icon
|
|
return remapped, icons
|
|
|
|
|
|
def _remap_components(components: dict[ComponentID, Component], component_map: dict[ComponentID, ComponentID], port_map: dict[PortID, PortID]) -> dict[ComponentID, Component]:
|
|
remapped: dict[ComponentID, Component] = {}
|
|
for old_component_id, original in components.items():
|
|
component = deepcopy(original)
|
|
new_component_id = ComponentID()
|
|
component_map[old_component_id] = new_component_id
|
|
component.interface.ports = {_new_port_id(old_id, port_map): port for old_id, port in component.interface.ports.items()}
|
|
component.parameters = {ParameterID(): parameter for parameter in component.parameters.values()}
|
|
if isinstance(component.implementation, GraphImplementation):
|
|
graph = component.implementation.graph
|
|
graph.components = _remap_components(graph.components, component_map, port_map)
|
|
graph.connections = {ConnectionID(): connection for connection in graph.connections.values()}
|
|
for connection in graph.connections.values():
|
|
connection.source = port_map.get(connection.source, connection.source)
|
|
connection.target = port_map.get(connection.target, connection.target)
|
|
remapped[new_component_id] = component
|
|
return remapped
|
|
|
|
|
|
def _new_port_id(old_id: PortID, port_map: dict[PortID, PortID]) -> PortID:
|
|
new_id = PortID()
|
|
port_map[old_id] = new_id
|
|
return new_id
|
|
|
|
|
|
def _all_component_ids(components: dict[ComponentID, Component]) -> list[ComponentID]:
|
|
component_ids: list[ComponentID] = []
|
|
for component_id, component in components.items():
|
|
component_ids.append(component_id)
|
|
if isinstance(component.implementation, GraphImplementation):
|
|
component_ids.extend(_all_component_ids(component.implementation.graph.components))
|
|
return component_ids
|