1# ------------------------------------------------------------------------------
2#
3#  Copyright (c) 2005, Enthought, Inc.
4#  All rights reserved.
5#
6#  This software is provided without warranty under the terms of the BSD
7#  license included in LICENSE.txt and may be redistributed only
8#  under the conditions described in the aforementioned license.  The license
9#  is also available online at http://www.enthought.com/licenses/BSD.txt
10#
11#  Thanks for using Enthought open source!
12#
13#  Author: David C. Morrill
14#  Date:   10/18/2004
15#
16# ------------------------------------------------------------------------------
17
18""" Defines the abstract ViewElement class that all trait view template items
19    (i.e., View, Group, Item, Include) derive from.
20"""
21
22import re
23
24from traits.api import AbstractViewElement, Bool, HasPrivateTraits, Instance
25
26from .ui_traits import (
27    AnObject,
28    DockStyle,
29    EditorStyle,
30    ExportType,
31    HelpId,
32    Image,
33)
34
35
36label_pat = re.compile(r"^(.*)\[(.*)\](.*)$", re.MULTILINE | re.DOTALL)
37label_pat2 = re.compile(r"^(.*){(.*)}(.*)$", re.MULTILINE | re.DOTALL)
38
39# -------------------------------------------------------------------------
40#  'ViewElement' class (abstract):
41# -------------------------------------------------------------------------
42
43
44class ViewElement(HasPrivateTraits):
45    """ An element of a view.
46    """
47
48    def replace_include(self, view_elements):
49        """ Searches the current object's **content** attribute for objects that
50        have an **id** attribute, and replaces each one with an Include object
51        with the same **id** value, and puts the replaced object into the
52        specified ViewElements object.
53
54        Parameters
55        ----------
56        view_elements : ViewElements object
57            Object containing Group, Item, and Include objects
58        """
59        pass  # Normally overridden in a subclass
60
61    def is_includable(self):
62        """ Returns whether the object is replacable by an Include object.
63        """
64        return False  # Normally overridden in a subclass
65
66
67class DefaultViewElement(ViewElement):
68    """ A view element that can be used as a default value for traits whose
69        value is a view element.
70    """
71
72    # -------------------------------------------------------------------------
73    #  Trait definitions:
74    # -------------------------------------------------------------------------
75
76    #: The default context object to edit:
77    object = AnObject
78
79    #: The default editor style to use:
80    style = EditorStyle
81
82    #: The default dock style to use:
83    dock = DockStyle
84
85    #: The default notebook tab image to use:
86    image = Image
87
88    #: The category of elements dragged out of the view:
89    export = ExportType
90
91    #: Should labels be added to items in a group?
92    show_labels = Bool(True)
93
94
95# -------------------------------------------------------------------------
96#  Trait definitions:
97# -------------------------------------------------------------------------
98
99# The container trait used by ViewSubElements:
100Container = Instance(ViewElement, factory=DefaultViewElement)
101
102# -------------------------------------------------------------------------
103#  'ViewSubElement' class (abstract):
104# -------------------------------------------------------------------------
105
106
107class ViewSubElement(ViewElement):
108    """ Abstract class representing elements that can be contained in a view.
109    """
110
111    # -------------------------------------------------------------------------
112    #  Trait definitions:
113    # -------------------------------------------------------------------------
114
115    #: The object this ViewSubElement is contained in; must be a ViewElement.
116    container = Container
117
118    #: External help context identifier:
119    help_id = HelpId
120
121    def _split(self, name, value, char, finder, assign, result):
122        """ Splits a string at a specified character.
123        """
124        col = finder(value, char)
125        if col < 0:
126            return value
127
128        items = (value[:col].strip(), value[col + 1:].strip())
129        if items[assign] != "":
130            setattr(self, name, items[assign])
131
132        return items[result]
133
134    def _option(self, string, option, name, value):
135        """ Sets a object trait if a specified option string is found.
136        """
137        col = string.find(option)
138        if col >= 0:
139            string = string[:col] + string[col + len(option):]
140            setattr(self, name, value)
141
142        return string
143
144    def _parse_style(self, value):
145        """ Parses any of the one-character forms of the **style** trait.
146        """
147        value = self._option(value, "$", "style", "simple")
148        value = self._option(value, "@", "style", "custom")
149        value = self._option(value, "*", "style", "text")
150        value = self._option(value, "~", "style", "readonly")
151        value = self._split("style", value, ";", str.rfind, 1, 0)
152
153        return value
154
155    def _parse_label(self, value):
156        """ Parses a '[label]' value from the string definition.
157        """
158        match = label_pat.match(value)
159        if match is not None:
160            self._parsed_label()
161        else:
162            match = label_pat2.match(value)
163
164        empty = False
165        if match is not None:
166            self.label = match.group(2).strip()
167            empty = self.label == ""
168            value = match.group(1) + match.group(3)
169
170        return (value, empty)
171
172    def _parsed_label(self):
173        """ Handles a label being found in the string definition.
174        """
175        pass
176
177    def _repr_value(self, value, prefix="", suffix="", ignore=""):
178        """ Returns a "pretty print" version of a specified Item trait value.
179        """
180        if value == ignore:
181            return ""
182
183        return "%s%s%s" % (prefix, value, suffix)
184
185    def _repr_options(self, *names):
186        """ Returns a 'pretty print' version of a list of traits.
187        """
188        result = []
189        for name in names:
190            value = getattr(self, name)
191            if value != self.trait(name).default_value_for(self, name):
192                result.append((name, repr(value)))
193
194        if len(result) > 0:
195            n = max([len(name) for name, value in result])
196            return ",\n".join(
197                ["%s = %s" % (name.ljust(n), value) for name, value in result]
198            )
199
200        return None
201
202    def _indent(self, string, indent="    "):
203        """ Indents each line in a specified string by 4 spaces.
204        """
205        return "\n".join([indent + s for s in string.split("\n")])
206
207
208# Register ViewElement as implementing AbstractViewElement
209# TODO: eventually have ViewElement inherit directly
210AbstractViewElement.register(ViewElement)
211