# property_binding.py # # Copyright 2023 James Westman # # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 3 of the # License, or (at your option) any later version. # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program. If not, see . # # SPDX-License-Identifier: LGPL-3.0-or-later from .common import * from .contexts import ScopeCtx from .gobject_object import Object from .gtkbuilder_template import Template class PropertyBindingFlag(AstNode): grammar = [ AnyOf( UseExact("flag", "inverted"), UseExact("flag", "bidirectional"), UseExact("flag", "no-sync-create"), UseExact("flag", "sync-create"), ) ] @property def flag(self) -> str: return self.tokens["flag"] @validate() def sync_create(self): if self.flag == "sync-create": raise UpgradeWarning( "'sync-create' is now the default. Use 'no-sync-create' if this is not wanted.", actions=[CodeAction("remove 'sync-create'", "")], ) class PropertyBinding(AstNode): grammar = AnyOf( [ Keyword("bind-property"), UseIdent("source"), ".", UseIdent("property"), ZeroOrMore(PropertyBindingFlag), ], [ Keyword("bind"), UseIdent("source"), ".", UseIdent("property"), PropertyBindingFlag, ZeroOrMore(PropertyBindingFlag), ], ) @property def source(self) -> str: return self.tokens["source"] @property def source_obj(self) -> T.Optional[Object]: return self.context[ScopeCtx].objects.get(self.source) @property def property_name(self) -> str: return self.tokens["property"] @property def flags(self) -> T.List[PropertyBindingFlag]: return self.children[PropertyBindingFlag] @property def inverted(self) -> bool: return any([f.flag == "inverted" for f in self.flags]) @property def bidirectional(self) -> bool: return any([f.flag == "bidirectional" for f in self.flags]) @property def no_sync_create(self) -> bool: return any([f.flag == "no-sync-create" for f in self.flags]) @validate("source") def source_object_exists(self) -> None: if self.source_obj is None: raise CompileError( f"Could not find object with ID {self.source}", did_you_mean=(self.source, self.context[ScopeCtx].objects.keys()), ) @validate("property") def property_exists(self) -> None: if self.source_obj is None: return gir_class = self.source_obj.gir_class if gir_class is None or gir_class.incomplete: # Objects that we have no gir data on should not be validated # This happens for classes defined by the app itself return if ( isinstance(gir_class, gir.Class) and gir_class.properties.get(self.property_name) is None ): raise CompileError( f"{gir_class.full_name} does not have a property called {self.property_name}" ) @validate("bind") def old_bind(self): if self.tokens["bind"]: raise UpgradeWarning( "Use 'bind-property', introduced in blueprint 0.8.0, to use binding flags", actions=[CodeAction("Use 'bind-property'", "bind-property")], )