1# -----------------------------------------------------------------------------
2#
3#  Copyright (c) 2006, 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: Dave Peterson <dpeterson@enthought.com>
14#
15# -----------------------------------------------------------------------------
16
17"""
18A handler that delegates the handling of events to a set of sub-handlers.
19
20This is typically used as the handler for dynamic views.  See the
21**traits.has_dynamic_view** module.
22"""
23
24# Enthought library imports
25from traits.api import HasTraits, List
26from .ui import Dispatcher
27
28# Local imports.
29from .handler import Handler
30
31# Set up a logger:
32import logging
33
34logger = logging.getLogger(__name__)
35
36
37class DelegatingHandler(Handler):
38    """ A handler that delegates the handling of events to a set of
39        sub-handlers.
40    """
41
42    # -- Public 'DelegatingHandler' Interface ---------------------------------
43
44    #: The list of sub-handlers this object delegates to:
45    sub_handlers = List(HasTraits)
46
47    # -- Protected 'DelegatingHandler' Interface ------------------------------
48
49    #: A list of dispatchable handler methods:
50    _dispatchers = List()
51
52    # -------------------------------------------------------------------------
53    #  'Handler' interface:
54    # -------------------------------------------------------------------------
55
56    # -- Public Methods -------------------------------------------------------
57
58    def closed(self, info, is_ok):
59        """ Handles the user interface being closed by the user.
60
61            This method is overridden here to unregister any dispatchers that
62            were set up in the *init()* method.
63        """
64        for d in self._dispatchers:
65            d.remove()
66
67    def init(self, info):
68        """ Initializes the controls of a user interface.
69
70        This method is called after all user interface elements have been
71        created, but before the user interface is displayed. Use this method to
72        further customize the user interface before it is displayed.
73
74        This method is overridden here to delegate to sub-handlers.
75
76        Parameters
77        ----------
78        info : *UIInfo* object
79            The UIInfo object associated with the view
80
81        Returns
82        -------
83        initialized : bool
84            A boolean, indicating whether the user interface was successfully
85            initialized. A True value indicates that the UI can be displayed;
86            a False value indicates that the display operation should be
87            cancelled.
88        """
89
90        # Iterate through our sub-handlers, and for each method whose name is
91        # of the form 'object_name_changed', where 'object' is the name of an
92        # object in the UI's context, create a trait notification handler that
93        # will call the method whenever object's 'name' trait changes.
94        logger.debug("Initializing delegation in DelegatingHandler [%s]", self)
95        context = info.ui.context
96        for h in self.sub_handlers:
97            # fixme: I don't know why this wasn't here before... I'm not
98            # sure this is right!
99            h.init(info)
100
101            for name in self._each_trait_method(h):
102                if name[-8:] == "_changed":
103                    prefix = name[:-8]
104                    col = prefix.find("_", 1)
105                    if col >= 0:
106                        object = context.get(prefix[:col])
107                        if object is not None:
108                            logger.debug(
109                                "\tto method [%s] on handler[%s]", name, h
110                            )
111                            method = getattr(h, name)
112                            trait_name = prefix[col + 1:]
113                            self._dispatchers.append(
114                                Dispatcher(method, info, object, trait_name)
115                            )
116
117                            # Also invoke the method immediately so initial
118                            # user interface state can be correctly set.
119                            if object.base_trait(trait_name).type != "event":
120                                method(info)
121
122                # fixme: These are explicit workarounds for problems with:-
123                #
124                # 'GeometryHierarchyViewHandler'
125                #
126                # which is used in the :-
127                #
128                # 'GeometryHierarchyTreeEditor'
129                #
130                # which are in the 'encode.cad.ui.geometry' package.
131                #
132                # The tree editor has dynamic views, and hence the handler gets
133                # wrapped by a 'DelegatingHandler'. Unfortunately the handler
134                # has a couple of methods that aren't picked up by the usual
135                # wrapping strategy:-
136                #
137                # 1) 'tree_item_selected'
138                #
139                # - which is obviously called when a tree item is selected.
140                #
141                # 2) 'inspect_object'
142                #
143                # - which is called directly as as action from the context menu
144                #   defined in the tree editor.
145                #
146                elif name in ["tree_item_selected", "inspect_object"]:
147                    self.__dict__[name] = self._create_delegate(h, name)
148
149        return True
150
151    def _create_delegate(self, h, name):
152        """ Quick fix for handler methods that are currently left out!
153        """
154
155        def delegate(*args, **kw):
156            method = getattr(h, name)
157            return method(*args, **kw)
158
159        return delegate
160