class CommandLabelChange(QtWidgets.QUndoCommand):
    """
    This command is used to edit items' labels.
    """
    def __init__(self, diagram, item, undo, redo, refactor=False, name=None):
        """
        Initialize the command.
        :type diagram: Diagram
        :type item: AbstractItem
        :type undo: str
        :type redo: str
        :type refactor: bool
        :type name: str
        """
        super().__init__(name or 'edit {0} label'.format(item.name))
        self.diagram = diagram
        self.project = diagram.project
        self.refactor = refactor
        self.data = {'undo': undo, 'redo': redo}
        self.item = item

    def redo(self):
        """redo the command"""
        meta = None
        # BACKUP METADATA
        if self.item.isNode() and self.refactor:
            meta = self.project.meta(self.item.type(), self.data['undo'])
            if meta:
                self.project.unsetMeta(self.item.type(), self.data['undo'])

        # CHANGE THE CONTENT OF THE LABEL
        if self.item.isNode():
            self.project.doRemoveItem(self.diagram, self.item)
        self.item.setText(self.data['redo'])
        if self.item.isNode():
            self.project.doAddItem(self.diagram, self.item)

        # RESTORE METADATA
        if meta:
            self.project.setMeta(self.item.type(), self.data['redo'], meta)

        # UPDATE PREDICATE NODE STATE TO REFLECT THE CHANGES
        for key in ('undo', 'redo'):
            for node in self.project.predicates(self.item.type(), self.data[key]):
                node.updateNode()

        # IDENTITFY NEIGHBOURS
        if self.item.type() is Item.IndividualNode:
            f1 = lambda x: x.type() is Item.InputEdge
            f2 = lambda x: x.type() in {Item.EnumerationNode, Item.PropertyAssertionNode}
            for node in self.item.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2):
                self.diagram.sgnNodeIdentification.emit(node)
            f3 = lambda x: x.type() is Item.MembershipEdge
            f4 = lambda x: Identity.Neutral in x.identities()
            for node in self.item.outgoingNodes(filter_on_edges=f3, filter_on_nodes=f4):
                self.diagram.sgnNodeIdentification.emit(node)

        # EMIT UPDATED SIGNAL
        self.diagram.sgnUpdated.emit()

    def undo(self):
        """undo the command"""
        meta = None
        # BACKUP METADATA
        if self.item.isNode() and self.refactor:
            meta = self.project.meta(self.item.type(), self.data['redo'])
            if meta:
                self.project.unsetMeta(self.item.type(), self.data['redo'])

        # CHANGE THE CONTENT OF THE LABEL
        if self.item.isNode():
            self.project.doRemoveItem(self.diagram, self.item)
        self.item.setText(self.data['undo'])
        if self.item.isNode():
            self.project.doAddItem(self.diagram, self.item)

        # RESTORE METADATA
        if meta:
            self.project.setMeta(self.item.type(), self.data['undo'], meta)

        # UPDATE PREDICATE NODE STATE TO REFLECT THE CHANGES
        for key in ('undo', 'redo'):
            for node in self.project.predicates(self.item.type(), self.data[key]):
                node.updateNode()

        # IDENTITFY NEIGHBOURS
        if self.item.type() is Item.IndividualNode:
            f1 = lambda x: x.type() is Item.InputEdge
            f2 = lambda x: x.type() in {Item.EnumerationNode, Item.PropertyAssertionNode}
            for node in self.item.outgoingNodes(filter_on_edges=f1, filter_on_nodes=f2):
                self.diagram.sgnNodeIdentification.emit(node)
            f3 = lambda x: x.type() is Item.MembershipEdge
            f4 = lambda x: Identity.Neutral in x.identities()
            for node in self.item.outgoingNodes(filter_on_edges=f3, filter_on_nodes=f4):
                self.diagram.sgnNodeIdentification.emit(node)

        # EMIT UPDATED SIGNAL
        self.diagram.sgnUpdated.emit() class CommandProjectSetIRI(QtWidgets.QUndoCommand):
    """
    This command is used to set the IRI of a project.
    """
    def __init__(self, project, undo, redo):
        """
        Initialize the command.
        :type project: Project
        :type undo: str
        :type redo: str
        """
        super().__init__("set ontology IRI to '{0}'".format(redo))
        self._project = project
        self._undo = undo
        self._redo = redo

    def redo(self):
        """redo the command"""
        self._project.iri = self._redo
        self._project.sgnUpdated.emit()

    def undo(self):
        """undo the command"""
        self._project.iri = self._undo
        self._project.sgnUpdated.emit() class CommandProjectSetPrefix(QtWidgets.QUndoCommand):
    """
    This command is used to set the prefix of a project.
    """
    def __init__(self, project, undo, redo):
        """
        Initialize the command.
        :type project: Project
        :type undo: str
        :type redo: str
        """
        super().__init__("set ontology prefix to '{0}'".format(redo))
        self._project = project
        self._undo = undo
        self._redo = redo

    def redo(self):
        """redo the command"""
        self._project.prefix = self._redo
        self._project.sgnUpdated.emit()

    def undo(self):
        """undo the command"""
        self._project.prefix = self._undo
        self._project.sgnUpdated.emit() class CommandProjectSetProfile(QtWidgets.QUndoCommand):
    """
    This command is used to set the profile of a project.
    """
    def __init__(self, project, undo, redo):
        """
        Initialize the command.
        :type project: Project
        :type undo: OWLProfile
        :type redo: OWLProfile
        """
        super().__init__("set project profile to '{0}'".format(redo))
        self.project = project
        self.data = {'undo': undo, 'redo': redo}

    def redo(self):
        """redo the command"""
        self.project.profile = self.project.session.createProfile(self.data['redo'], self.project)
        # Reshape all the Role and Attribute nodes to show/hide functionality and inverse functionality.
        for node in self.project.nodes():
            if node.type() in {Item.RoleNode, Item.AttributeNode}:
                node.updateNode(selected=node.isSelected())
        # Emit updated signals.
        self.project.session.sgnUpdateState.emit()
        self.project.sgnUpdated.emit()

    def undo(self):
        """undo the command"""
        self.project.profile = self.project.session.createProfile(self.data['undo'], self.project)
        # Reshape all the Role and Attribute nodes to show/hide functionality and inverse functionality.
        for node in self.project.nodes():
            if node.type() in {Item.RoleNode, Item.AttributeNode}:
                node.updateNode(selected=node.isSelected())
                # Emit updated signals.
        self.project.session.sgnUpdateState.emit()
        self.project.sgnUpdated.emit() class CommandProjectSetVersion(QtWidgets.QUndoCommand):
    """
    This command is used to set the version of an ontology.
    """
    def __init__(self, project, undo, redo):
        """
        Initialize the command.
        :type project: Project
        :type undo: str
        :type redo: str
        """
        super().__init__("set ontology version to '{0}'".format(redo))
        self._project = project
        self._undo = undo
        self._redo = redo

    def redo(self):
        """redo the command"""
        self._project.version = self._redo
        self._project.sgnUpdated.emit()

    def undo(self):
        """undo the command"""
        self._project.version = self._undo
        self._project.sgnUpdated.emit() class Item(IntEnum_):
    """
    This class defines all the available elements for graphol diagrams.
    """
    # PREDICATES
    ConceptNode = 65537
    AttributeNode = 65538
    RoleNode = 65539
    ValueDomainNode = 65540
    IndividualNode = 65541

    # CONSTRUCTORS
    DomainRestrictionNode = 65542
    RangeRestrictionNode = 65543
    UnionNode = 65544
    EnumerationNode = 65545
    ComplementNode = 65546
    RoleChainNode = 65547
    IntersectionNode = 65548
    RoleInverseNode = 65549
    DatatypeRestrictionNode = 65550
    DisjointUnionNode = 65551
    PropertyAssertionNode = 65552
    FacetNode = 65553

    # EDGES
    InclusionEdge = 65554
    EquivalenceEdge = 65555
    InputEdge = 65556
    MembershipEdge = 65557

    # EXTRA
    Label = 65558
    Undefined = 65559

    @property
    def realName(self):
        """
        Returns the item readable name, i.e: attribute node, concept node.
        :rtype: str
        """
        return RE_CAMEL_SPACE.sub('\g<1> \g<2>', self.name).lower()

    @property
    def shortName(self):
        """
        Returns the item short name, i.e: attribute, concept.
        :rtype: str
        """
        return rstrip(self.realName, ' node', ' edge') class Facet(Enum_):
    """
    Extends Enum providing all the available Facet restrictions.
    """
    maxExclusive = 'xsd:maxExclusive'
    maxInclusive = 'xsd:maxInclusive'
    minExclusive = 'xsd:minExclusive'
    minInclusive = 'xsd:minInclusive'
    langRange = 'rdf:langRange'
    length = 'xsd:length'
    maxLength = 'xsd:maxLength'
    minLength = 'xsd:minLength'
    pattern = 'xsd:pattern'

    @classmethod
    def forDatatype(cls, value):
        """
        Returns a collection of Facets for the given datatype
        :type value: Datatype
        :rtype: list
        """
        allvalues = [x for x in cls]
        numbers = [Facet.maxExclusive, Facet.maxInclusive, Facet.minExclusive, Facet.minInclusive]
        strings = [Facet.langRange, Facet.length, Facet.maxLength, Facet.minLength, Facet.pattern]
        binary = [Facet.length, Facet.maxLength, Facet.minLength]
        anyuri = [Facet.length, Facet.maxLength, Facet.minLength, Facet.pattern]

        return {
            Datatype.anyURI: anyuri,
            Datatype.base64Binary: binary,
            Datatype.boolean: [],
            Datatype.byte: numbers,
            Datatype.dateTime: numbers,
            Datatype.dateTimeStamp: numbers,
            Datatype.decimal: numbers,
            Datatype.double: numbers,
            Datatype.float: numbers,
            Datatype.hexBinary: binary,
            Datatype.int: numbers,
            Datatype.integer: numbers,
            Datatype.language: strings,
            Datatype.Literal: allvalues,
            Datatype.long: numbers,
            Datatype.Name: strings,
            Datatype.NCName: strings,
            Datatype.negativeInteger: numbers,
            Datatype.NMTOKEN: strings,
            Datatype.nonNegativeInteger: numbers,
            Datatype.nonPositiveInteger: numbers,
            Datatype.normalizedString: strings,
            Datatype.PlainLiteral: strings,
            Datatype.positiveInteger: numbers,
            Datatype.rational: numbers,
            Datatype.real: numbers,
            Datatype.short: numbers,
            Datatype.string: strings,
            Datatype.token: strings,
            Datatype.unsignedByte: numbers,
            Datatype.unsignedInt: numbers,
            Datatype.unsignedLong: numbers,
            Datatype.unsignedShort: numbers,
            Datatype.XMLLiteral: []
        }[value] class Datatype(Enum_):
    """
    Extends Enum providing all the available datatypes.
    """
    rational = 'owl:rational'
    real = 'owl:real'
    PlainLiteral = 'rdf:PlainLiteral'
    XMLLiteral = 'rdf:XMLLiteral'
    Literal = 'rdfs:Literal'
    anyURI = 'xsd:anyURI'
    base64Binary = 'xsd:base64Binary'
    boolean = 'xsd:boolean'
    byte = 'xsd:byte'
    dateTime = 'xsd:dateTime'
    dateTimeStamp = 'xsd:dateTimeStamp'
    decimal = 'xsd:decimal'
    double = 'xsd:double'
    float = 'xsd:float'
    hexBinary = 'xsd:hexBinary'
    int = 'xsd:int'
    integer = 'xsd:integer'
    language = 'xsd:language'
    long = 'xsd:long'
    Name = 'xsd:Name'
    NCName = 'xsd:NCName'
    negativeInteger = 'xsd:negativeInteger'
    NMTOKEN = 'xsd:NMTOKEN'
    nonNegativeInteger = 'xsd:nonNegativeInteger'
    nonPositiveInteger = 'xsd:nonPositiveInteger'
    normalizedString = 'xsd:normalizedString'
    positiveInteger = 'xsd:positiveInteger'
    short = 'xsd:short'
    string = 'xsd:string'
    token = 'xsd:token'
    unsignedByte = 'xsd:unsignedByte'
    unsignedInt = 'xsd:unsignedInt'
    unsignedLong = 'xsd:unsignedLong'
    unsignedShort = 'xsd:unsignedShort'

    @classmethod
    def forProfile(cls, profile):
        """
        Returns the list of supported datatypes for the given OWL 2 profile.
        :type profile: OWLProfile
        :rtype: set
        """
        if profile is OWLProfile.OWL2:
            return {x for x in Datatype}
        elif profile is OWLProfile.OWL2QL:
            return {Datatype.rational, Datatype.real, Datatype.PlainLiteral, Datatype.XMLLiteral,
                Datatype.Literal, Datatype.anyURI, Datatype.base64Binary, Datatype.dateTime,
                Datatype.dateTimeStamp, Datatype.decimal, Datatype.hexBinary, Datatype.integer,
                Datatype.Name, Datatype.NCName, Datatype.NMTOKEN, Datatype.nonNegativeInteger,
                Datatype.normalizedString, Datatype.string, Datatype.token}
        elif profile is OWLProfile.OWL2RL:
            return {Datatype.PlainLiteral, Datatype.XMLLiteral, Datatype.Literal, Datatype.anyURI,
                Datatype.base64Binary, Datatype.boolean, Datatype.byte, Datatype.dateTime,
                Datatype.dateTimeStamp, Datatype.decimal, Datatype.double, Datatype.float,
                Datatype.hexBinary, Datatype.Name, Datatype.NCName, Datatype.negativeInteger,
                Datatype.NMTOKEN, Datatype.nonNegativeInteger, Datatype.nonPositiveInteger,
                Datatype.normalizedString, Datatype.positiveInteger, Datatype.short,
                Datatype.string, Datatype.token, Datatype.unsignedByte, Datatype.unsignedInt,
                Datatype.unsignedLong, Datatype.unsignedShort}
        raise ValueError('unsupported profile: %s' % profile) class OWLProfile(Enum_):
    """
    Extends Enum providing all the available OWL 2 profiles.
    """
    OWL2 = 'OWL 2'
    OWL2EL = 'OWL 2 EL'
    OWL2QL = 'OWL 2 QL'
    OWL2RL = 'OWL 2 RL' class BrushIcon(QtGui.QIcon):
    """
    This class extends QtGui.QIcon and automatically creates an icon filled with the given color.
    """
    def __init__(self, width, height, color, border=None):
        """
        Initialize the icon.
        :type width: T <= int | float
        :type height: T <= int | float
        :type color: str
        :type border: str
        """
        pixmap = QtGui.QPixmap(width, height)
        painter = QtGui.QPainter(pixmap)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        path = QtGui.QPainterPath()
        path.addRect(QtCore.QRectF(QtCore.QPointF(0, 0), QtCore.QPointF(width, height)))
        painter.fillPath(path, QtGui.QBrush(QtGui.QColor(color)))
        if border:
            painter.setPen(QtGui.QPen(QtGui.QColor(border), 0, QtCore.Qt.SolidLine))
            painter.drawPath(path)
        painter.end()
        super().__init__(pixmap) class Font(QtGui.QFont):
    """
    This class extends QtGui.QFont providing better font rendering on different platforms.
    """
    def __init__(self, family, size=12, weight=-1, **kwargs):
        """
        Contruct a new Font instance using the given parameters.
        :type family: str
        :type size: float
        :type weight: float
        """
        if not _MACOS:
            size = int(round(size * 0.75))
        if not self.isHDPI():
            if family == 'Roboto':
                family = 'Arial'
        super().__init__(family, size, weight)
        self.setBold(kwargs.get('bold', False))
        self.setItalic(kwargs.get('italic', False))
        self.setCapitalization(kwargs.get('capitalization', QtGui.QFont.MixedCase))
        self.setStyleHint(kwargs.get('style', QtGui.QFont.AnyStyle))

    @classmethod
    def isHDPI(cls):
        """
        Checks whether HDPI support is enabled on all the connected desktop screens.
        :rtype: bool
        """
        for screen in QtWidgets.QApplication.screens():
            if screen.devicePixelRatio() < 2:
                return False
        return True class AbstractPlugin(QtCore.QObject, HasActionSystem, HasMenuSystem, HasWidgetSystem):
    """
    Extension QtCore.QObject which implements a plugin.
    """
    __metaclass__ = ABCMeta

    def __init__(self, spec, session):
        """
        Initialize the plugin.
        :type spec: PluginSpec
        :type session: session
        """
        super().__init__(session)
        self.spec = spec

    #############################################
    #   PROPERTIES
    #################################

    @property
    def project(self):
        """
        Returns the reference to the active project.
        :rtype: Project
        """
        return self.session.project

    @property
    def session(self):
        """
        Returns the reference to the main session (alias for AbstractPlugin.parent()).
        :rtype: Session
        """
        return self.parent()

    #############################################
    #   INTERFACE
    #################################

    def author(self):
        """
        Returns the author of the plugin.
        :rtype: str
        """
        return self.spec.get('plugin', 'author', fallback='<unknown>')

    def contact(self):
        """
        Returns the contact address for this plugin.
        :rtype: str
        """
        return self.spec.get('plugin', 'contact', fallback='<unknown>')

    def id(self):
        """
        Returns the plugin identifier.
        :rtype: str
        """
        return self.spec.get('plugin', 'id')

    def isBuiltIn(self):
        """
        Returns True if this plugin is a built-in one, False otherwise.
        :rtype: bool
        """
        return isSubPath('@plugins/', inspect.getfile(self.__class__))

    def name(self):
        """
        Returns the name of the plugin.
        :rtype: str
        """
        return self.spec.get('plugin', 'name')

    def objectName(self):
        """
        Returns the system name of the plugin.
        :rtype: str
        """
        return self.spec.get('plugin', 'id')

    def path(self):
        """
        Returns the path to the the plugin (either a directory of a ZIP file).
        :rtype: str
        """
        path = lstrip(inspect.getfile(self.__class__), expandPath('@plugins/'), expandPath('@home/plugins/'))
        home = first(filter(None, path.split(os.path.sep)))
        root = expandPath('@plugins/' if self.isBuiltIn() else '@home/plugins/')
        return os.path.join(root, home)

    @classmethod
    def subclasses(cls):
        """
        Returns the list of subclasses subclassing this very class.
        :rtype: list
        """
        return cls.__subclasses__() + [c for i in cls.__subclasses__() for c in i.subclasses()]

    def version(self):
        """
        Returns the version of the plugin.
        :rtype: NormalizedVersion
        """
        return NormalizedVersion(self.spec.get('plugin', 'version'))

    #############################################
    #   HOOKS
    #################################

    def dispose(self):
        """
        Executed whenever the plugin is going to be destroyed.
        """
        pass

    def start(self):
        """
        Executed whenever the plugin is to be started, after all the plugins have been loaded.
        NOTE: this method is executed before the project is loaded in the main session, so any
        attempt to refer to self.project from within this method will raise an exception.
        To setup project specific signals/slots, it's possible to make use of the sgnReady
        signal emitted by the main session when the startup sequence completes.
        """
        pass

    #############################################
    #   LOGGING UTILITIES
    #################################

    def critical(self, message, *args, **kwargs):
        """
        Log a 'CRITICAL' message.
        To pass exception information, use the keyword argument 'exc_info=True'.
        :type message: str
        """
        LOGGER.critical('{0}: {1}'.format(self.name(), message), *args, **kwargs)

    def debug(self, message, *args, **kwargs):
        """
        Log a 'DEBUG' message.
        To pass exception information, use the keyword argument 'exc_info=True'.
        :type message: str
        """
        LOGGER.debug('{0}: {1}'.format(self.name(), message), *args, **kwargs)

    def error(self, message, *args, **kwargs):
        """
        Log a 'ERROR' message.
        To pass exception information, use the keyword argument 'exc_info=True'.
        :type message: str
        """
        LOGGER.error('{0}: {1}'.format(self.name(), message), *args, **kwargs)

    def info(self, message, *args, **kwargs):
        """
        Log a 'INFO' message.
        To pass exception information, use the keyword argument 'exc_info=True'.
        :type message: str
        """
        LOGGER.info('{0}: {1}'.format(self.name(), message), *args, **kwargs)

    def warning(self, message, *args, **kwargs):
        """
        Log a 'WARNING' message.
        To pass exception information, use the keyword argument 'exc_info=True'.
        :type message: str
        """
        LOGGER.warning('{0}: {1}'.format(self.name(), message), *args, **kwargs) K_FUNCTIONAL = 'functional' K_INVERSE_FUNCTIONAL = 'inverseFunctional' K_ASYMMETRIC = 'asymmetric' K_IRREFLEXIVE = 'irreflexive' K_REFLEXIVE = 'reflexive' K_SYMMETRIC = 'symmetric' K_TRANSITIVE = 'transitive' RE_CAMEL_SPACE = re.compile("""([a-z])([A-Z])""") # space string on camel case token class IntegerField(QtWidgets.QLineEdit):
    """
    This class implements an input field where the user can enter only integer values.
    """
    def __init__(self, *args, **kwargs):
        """
        Initialize the integer input field.
        """
        super().__init__(*args, **kwargs)
        self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
        self.setValidator(QtGui.QIntValidator(self))

    def setValue(self, value):
        """
        Set the value of the field.
        :type value: int.
        """
        self.setText(str(value).strip())

    def value(self):
        """
        Returns the value of the field.
        :rtype: int
        """
        return int(self.text()) class StringField(QtWidgets.QLineEdit):
    """
    This class implements an input field where the user can enter strings.
    """
    def __init__(self, *args, **kwargs):
        """
        Initialize the string input field.
        """
        super().__init__(*args, **kwargs)
        self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)

    def setValue(self, value):
        """
        Set the value of the field.
        :type value: str.
        """
        self.setText(value.strip())

    def value(self):
        """
        Returns the value of the field.
        :rtype: str
        """
        return self.text().strip() class CheckBox(QtWidgets.QCheckBox):
    """
    This class implements a check box.
    """
    def __init__(self, *args, **kwargs):
        """
        Initialize the check box.
        """
        super().__init__(*args, **kwargs)
        self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0) class ComboBox(QtWidgets.QComboBox):
    """
    This class implements a multi selection box.
    """
    def __init__(self, *args, **kwargs):
        """
        Initialize the spin box.
        """
        super().__init__(*args, **kwargs)
        self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
        self._scrollEnabled = True

    def value(self):
        """
        Returns the currently selected value.
        :rtype: QVariant
        """
        return self.itemData(self.currentIndex())

    def setScrollEnabled(self, enabled=True):
        """
        Enable the selection of the option by scrolling the mouse wheel.
        :type enabled: bool
        """
        self._scrollEnabled = enabled

    def scrollEnabled(self):
        """
        Tells whether the selection of the option by scrolling the mouse wheel is enabled.
        :rtype: bool
        """
        return self._scrollEnabled

    def wheelEvent(self, wheelEvent):
        """
        Executed when the mouse wheel is rotated on the item.
        :type wheelEvent: QWheelEvent
        """
        if self.scrollEnabled():
            super().wheelEvent(wheelEvent)
from abc import ABCMeta, abstractmethod
from operator import attrgetter
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from eddy.core.commands.labels import CommandLabelChange
from eddy.core.commands.nodes import CommandNodeSetMeta
from eddy.core.commands.project import CommandProjectSetIRI
from eddy.core.commands.project import CommandProjectSetPrefix
from eddy.core.commands.project import CommandProjectSetProfile
from eddy.core.commands.project import CommandProjectSetVersion
from eddy.core.datatypes.graphol import Item
from eddy.core.datatypes.owl import Facet, Datatype, OWLProfile
from eddy.core.datatypes.qt import BrushIcon, Font
from eddy.core.functions.misc import first, clamp, isEmpty
from eddy.core.functions.signals import connect, disconnect
from eddy.core.plugin import AbstractPlugin
from eddy.core.project import K_FUNCTIONAL, K_INVERSE_FUNCTIONAL
from eddy.core.project import K_ASYMMETRIC, K_IRREFLEXIVE, K_REFLEXIVE
from eddy.core.project import K_SYMMETRIC, K_TRANSITIVE
from eddy.core.regex import RE_CAMEL_SPACE
from eddy.ui.dock import DockWidget
from eddy.ui.fields import IntegerField, StringField
from eddy.ui.fields import CheckBox, ComboBox
        :type node: AbstractNode
        """
        super().updateData(node)

        #############################################
        # BRUSH FIELD
        #################################

        if self.brushMenu.isEmpty():
            self.brushMenu.addActions(self.session.action('brush').actions())
        for action in self.session.action('brush').actions():
            color = action.data()
            brush = QtGui.QBrush(QtGui.QColor(color.value))
            if node.brush() == brush:
                self.brushButton.setIcon(BrushIcon(12, 12, color.value, '#000000'))
                self.brushButton.setText(color.value)
                break

        #############################################
        # NAME / TEXT FIELDS
        #################################

        self.nameField.setValue(node.text())
        self.nameField.home(True)
        self.nameField.deselect()
        self.textField.setValue(node.text())
        self.textField.home(True)
        self.textField.deselect()

        #############################################
        # ENABLE / DISABLE REFACTORING
        #################################

        refactor = True
        if node.type() in {Item.AttributeNode, Item.ConceptNode, Item.RoleNode}:
            if node.special() is not None:
                refactor = False
        self.nameField.setReadOnly(not refactor)


class AttributeNodeInfo(PredicateNodeInfo):
    """
    This class implements the information box for the Attribute node.
    """
    def __init__(self, session, parent=None):
        """
        Initialize the Attribute node information box.
        :type session: Session
        :type parent: QtWidgets.QWidget
        """
        super().__init__(session, parent)

        self.functKey = Key('Funct.', self)
        self.functKey.setFont(Font('Roboto', 12))
        functParent = Parent(self)
        self.functBox = CheckBox(functParent)
        self.functBox.setCheckable(True)
        self.functBox.setFont(Font('Roboto', 12))
        self.functBox.setProperty('key', K_FUNCTIONAL)
        connect(self.functBox.clicked, self.flagChanged)

        self.predPropLayout.addRow(self.functKey, functParent)

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot()
    def flagChanged(self):
        """
        Executed whenever one of the property fields changes.
        """
        sender = self.sender()
        checked = sender.isChecked()
        key = sender.property('key')
        undo = self.project.meta(self.node.type(), self.node.text())
        redo = undo.copy()
        redo[key] = checked
        if redo != undo:
            prop = RE_CAMEL_SPACE.sub('\g<1> \g<2>', key).lower()
            name = "{0}set '{1}' {2} property".format('' if checked else 'un', self.node.text(), prop)
            self.session.undostack.push(
                CommandNodeSetMeta(
                    self.project,
                    self.node.type(),
                    self.node.text(),
                    undo, redo, name))

    #############################################
    #   INTERFACE
    #################################

    def updateData(self, node):
        """
        Fetch new information and fill the widget with data.
        :type node: AbstractNode
        """
        super().updateData(node)
        self.functBox.setChecked(node.isFunctional())
        self.functBox.setEnabled(self.project.profile.type() is not OWLProfile.OWL2QL)
        self.functKey.setEnabled(self.project.profile.type() is not OWLProfile.OWL2QL)


class RoleNodeInfo(PredicateNodeInfo):
    """
    This class implements the information box for the Role node.
    """
    def __init__(self, session, parent=None):
        """
        Initialize the Role node information box.
        :type session: Session
        :type parent: QtWidgets.QWidget
        """
        super().__init__(session, parent)

        self.functKey = Key('Funct.', self)
        self.functKey.setFont(Font('Roboto', 12))
        functParent = Parent(self)
        self.functBox = CheckBox(functParent)
        self.functBox.setCheckable(True)

        self.functBox.setFont(Font('Roboto', 12))