1# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
2# All rights reserved.
3#
4# This software is provided without warranty under the terms of the BSD
5# license included in LICENSE.txt and may be redistributed only under
6# the conditions described in the aforementioned license. The license
7# is also available online at http://www.enthought.com/licenses/BSD.txt
8#
9# Thanks for using Enthought open source!
10
11""" Provides an AbstractValueType ABC for Pyface data models.
12
13This module provides an ABC for data view value types, which are responsible
14for adapting raw data values as used by the data model's ``get_value`` and
15``set_value`` methods to the data channels that the data view expects, such
16as text, color, icons, etc.
17
18It is up to the data view to take this standardized data and determine what
19and how to actually display it.
20"""
21
22from enum import IntEnum
23
24from traits.api import ABCHasStrictTraits, Event, observe
25
26from pyface.color import Color
27from .data_view_errors import DataViewSetError
28
29
30class CheckState(IntEnum):
31    "Possible checkbox states"
32    # XXX in the future this may need a "partial" state, see Pyface #695
33    UNCHECKED = 0
34    CHECKED = 1
35
36
37class AbstractValueType(ABCHasStrictTraits):
38    """ A value type converts raw data into data channels.
39
40    The data channels are editor value, text, color, image, and description.
41    The data channels are used by other parts of the code to produce the actual
42    display.
43
44    Subclasses should mark traits that potentially affect the display of values
45    with ``update_value_type=True`` metdadata, or alternatively fire the
46    ``updated`` event when the state of the value type changes.
47
48    Each data channel is set up to have a method which returns whether there
49    is a value for the channel, a second method which returns the value,
50    and an optional third method which sets the channel value.  These methods
51    should not raise an Exception, eveen when called inappropriately (eg.
52    calling a "get" method after a "has" method has returned False).
53    """
54
55    #: Fired when a change occurs that requires updating values.
56    updated = Event
57
58    def has_editor_value(self, model, row, column):
59        """ Return whether or not the value can be edited.
60
61        The default implementation is that cells that can be set are
62        editable.
63
64        Parameters
65        ----------
66        model : AbstractDataModel
67            The data model holding the data.
68        row : sequence of int
69            The row in the data model being queried.
70        column : sequence of int
71            The column in the data model being queried.
72
73        Returns
74        -------
75        has_editor_value : bool
76            Whether or not the value is editable.
77        """
78        return model.can_set_value(row, column)
79
80    def get_editor_value(self, model, row, column):
81        """ Return a value suitable for editing.
82
83        The default implementation is to return the underlying data value
84        directly from the data model.
85
86        Parameters
87        ----------
88        model : AbstractDataModel
89            The data model holding the data.
90        row : sequence of int
91            The row in the data model being queried.
92        column : sequence of int
93            The column in the data model being queried.
94
95        Returns
96        -------
97        value : any
98            The value to edit.
99        """
100        return model.get_value(row, column)
101
102    def set_editor_value(self, model, row, column, value):
103        """ Set a value that is returned from editing.
104
105        The default implementation is to set the value directly from the
106        data model.  Returns True if successful, False if it fails.
107
108        Parameters
109        ----------
110        model : AbstractDataModel
111            The data model holding the data.
112        row : sequence of int
113            The row in the data model being queried.
114        column : sequence of int
115            The column in the data model being queried.
116        value : any
117            The value to set.
118
119        Raises
120        -------
121        DataViewSetError
122            If the value cannot be set.
123        """
124        model.set_value(row, column, value)
125
126    def has_text(self, model, row, column):
127        """ Whether or not the value has a textual representation.
128
129        The default implementation returns True if ``get_text``
130        returns a non-empty value.
131
132        Parameters
133        ----------
134        model : AbstractDataModel
135            The data model holding the data.
136        row : sequence of int
137            The row in the data model being queried.
138        column : sequence of int
139            The column in the data model being queried.
140
141        Returns
142        -------
143        has_text : bool
144            Whether or not the value has a textual representation.
145        """
146        return self.get_text(model, row, column) != ""
147
148    def get_text(self, model, row, column):
149        """ The textual representation of the underlying value.
150
151        The default implementation calls str() on the underlying value.
152
153        Parameters
154        ----------
155        model : AbstractDataModel
156            The data model holding the data.
157        row : sequence of int
158            The row in the data model being queried.
159        column : sequence of int
160            The column in the data model being queried.
161
162        Returns
163        -------
164        text : str
165            The textual representation of the underlying value.
166        """
167        return str(model.get_value(row, column))
168
169    def set_text(self, model, row, column, text):
170        """ Set the text of the underlying value.
171
172        This is provided primarily for backends which may not permit
173        non-text editing of values, in which case this provides an
174        alternative route to setting the value.  The default implementation
175        does not allow setting the text.
176
177        Parameters
178        ----------
179        model : AbstractDataModel
180            The data model holding the data.
181        row : sequence of int
182            The row in the data model being queried.
183        column : sequence of int
184            The column in the data model being queried.
185        text : str
186            The text to set.
187
188        Raises
189        -------
190        DataViewSetError
191            If the value cannot be set.
192        """
193        raise DataViewSetError("Cannot set value.")
194
195    def has_color(self, model, row, column):
196        """ Whether or not the value has color data.
197
198        has_color : bool
199            Whether or not the value has data-associated color
200            values.
201        """
202        return False
203
204    def get_color(self, model, row, column):
205        """ Get data-associated colour values for the given item.
206
207        The default implementation returns white.
208
209        color : Color instance
210            The color associated with the cell.
211        """
212        return Color(rgba=(1.0, 1.0, 1.0, 1.0))
213
214    def has_image(self, model, row, column):
215        """ Whether or not the value has an image associated with it.
216
217        The default implementation returns True if ``get_image``
218        returns a non-None value.
219
220        Parameters
221        ----------
222        model : AbstractDataModel
223            The data model holding the data.
224        row : sequence of int
225            The row in the data model being queried.
226        column : sequence of int
227            The column in the data model being queried.
228
229        Returns
230        -------
231        has_image : bool
232            Whether or not the value has an image associated with it.
233        """
234        return False
235
236    def get_image(self, model, row, column):
237        """ An image associated with the underlying value.
238
239        The default implementation returns None.
240
241        Parameters
242        ----------
243        model : AbstractDataModel
244            The data model holding the data.
245        row : sequence of int
246            The row in the data model being queried.
247        column : sequence of int
248            The column in the data model being queried.
249
250        Returns
251        -------
252        image : IImageResource
253            The image associated with the underlying value.
254        """
255        from pyface.image_resource import ImageResource
256        return ImageResource("image_not_found")
257
258    def has_check_state(self, model, row, column):
259        """ Whether or not the value has checked state.
260
261        The default implementation returns False.
262
263        Parameters
264        ----------
265        model : AbstractDataModel
266            The data model holding the data.
267        row : sequence of int
268            The row in the data model being queried.
269        column : sequence of int
270            The column in the data model being queried.
271
272        Returns
273        -------
274        has_check_state : bool
275            Whether or not the value has a checked state.
276        """
277        return False
278
279    def get_check_state(self, model, row, column):
280        """ The state of the item check box.
281
282        The default implementation returns "checked" if the value is
283        truthy, or "unchecked" if the value is falsey.
284
285        Parameters
286        ----------
287        model : AbstractDataModel
288            The data model holding the data.
289        row : sequence of int
290            The row in the data model being queried.
291        column : sequence of int
292            The column in the data model being queried.
293
294        Returns
295        -------
296        check_state : CheckState
297            The current checked state.
298        """
299        return (
300            CheckState.CHECKED
301            if model.get_value(row, column)
302            else CheckState.UNCHECKED
303        )
304
305    def set_check_state(self, model, row, column, check_state):
306        """ Set the checked state of the underlying value.
307
308        The default implementation does not allow setting the checked state.
309
310        Parameters
311        ----------
312        model : AbstractDataModel
313            The data model holding the data.
314        row : sequence of int
315            The row in the data model being queried.
316        column : sequence of int
317            The column in the data model being queried.
318        check_state : CheckState
319            The check state value to set.
320
321        Raises
322        -------
323        DataViewSetError
324            If the value cannot be set.
325        """
326        raise DataViewSetError("Cannot set check state.")
327
328    def has_tooltip(self, model, row, column):
329        """ Whether or not the value has a tooltip.
330
331        The default implementation returns True if ``get_tooltip``
332        returns a non-empty value.
333
334        Parameters
335        ----------
336        model : AbstractDataModel
337            The data model holding the data.
338        row : sequence of int
339            The row in the data model being queried.
340        column : sequence of int
341            The column in the data model being queried.
342
343        Returns
344        -------
345        has_tooltip : bool
346            Whether or not the value has a textual representation.
347        """
348        return self.get_tooltip(model, row, column) != ""
349
350    def get_tooltip(self, model, row, column):
351        """ The tooltip for the underlying value.
352
353        The default implementation returns an empty string.
354
355        tooltip : str
356            The textual representation of the underlying value.
357        """
358        return ""
359
360    @observe('+update_value_type')
361    def update_value_type(self, event=None):
362        """ Fire update event when marked traits change. """
363        self.updated = True
364