text: """
  `ToolManager`
      Class that makes the bridge between user interaction (key press,
      toolbar clicks, ..) and the actions in response to the user inputs.
  """
  
  from __future__ import (absolute_import, division, print_function,
                          unicode_literals)
  import six
  import warnings
  
  import matplotlib.cbook as cbook
  import matplotlib.widgets as widgets
  from matplotlib.rcsetup import validate_stringlist
  import matplotlib.backend_tools as tools
  
  
  class ToolEvent(object):
      """Event for tool manipulation (add/remove)"""
      def __init__(self, name, sender, tool, data=None):
          self.name = name
          self.sender = sender
          self.tool = tool
          self.data = data
  
  
  class ToolTriggerEvent(ToolEvent):
      """Event to inform  that a tool has been triggered"""
      def __init__(self, name, sender, tool, canvasevent=None, data=None):
          ToolEvent.__init__(self, name, sender, tool, data)
          self.canvasevent = canvasevent
  
  
  class ToolManagerMessageEvent(object):
      """
      Event carrying messages from toolmanager
  
      Messages usually get displayed to the user by the toolbar
      """
      def __init__(self, name, sender, message):
          self.name = name
          self.sender = sender
          self.message = message
  
  
  class ToolManager(object):
      """
      Helper class that groups all the user interactions for a FigureManager
  
      Attributes
      ----------
      manager: `FigureManager`
      keypresslock: `widgets.LockDraw`
          `LockDraw` object to know if the `canvas` key_press_event is locked
      messagelock: `widgets.LockDraw`
          `LockDraw` object to know if the message is available to write
      """
  
      def __init__(self, canvas):
          warnings.warn('Treat the new Tool classes introduced in v1.5 as ' +
                         'experimental for now, the API will likely change in ' +
                         'version 2.1 and perhaps the rcParam as well')
          self.canvas = canvas
  
          self._key_press_handler_id = self.canvas.mpl_connect(
              'key_press_event', self._key_press)
  
          self._tools = {}
          self._keys = {}
          self._toggled = {}
          self._callbacks = cbook.CallbackRegistry()
  
          # to process keypress event
          self.keypresslock = widgets.LockDraw()
          self.messagelock = widgets.LockDraw()
  
      def toolmanager_connect(self, s, func):
          """
          Connect event with string *s* to *func*.
  
          Parameters
          ----------
          s : String
              Name of the event
  
              The following events are recognized
  
              - 'tool_message_event'
              - 'tool_removed_event'
              - 'tool_added_event'
  
              For every tool added a new event is created
  
              - 'tool_trigger_TOOLNAME`
                Where TOOLNAME is the id of the tool.
  
          func : function
              Function to be called with signature
              def func(event)
          """
          return self._callbacks.connect(s, func)
  
      def toolmanager_disconnect(self, cid):
          """
          Disconnect callback id *cid*
  
          Example usage::
  
              cid = toolmanager.toolmanager_connect('tool_trigger_zoom',
                                                    on_press)
              #...later
              toolmanager.toolmanager_disconnect(cid)
          """
          return self._callbacks.disconnect(cid)
  
      def message_event(self, message, sender=None):
          """ Emit a `ToolManagerMessageEvent`"""
          if sender is None:
              sender = self
  
          s = 'tool_message_event'
          event = ToolManagerMessageEvent(s, sender, message)
          self._callbacks.process(s, event)
  
      @property
      def active_toggle(self):
          """Currently toggled tools"""
  
          return self._toggled
  
      def get_tool_keymap(self, name):
          """
          Get the keymap associated with the specified tool
  
          Parameters
          ----------
          name : string
              Name of the Tool
  
          Returns
          -------
          list : list of keys associated with the Tool
          """
  
          keys = [k for k, i in six.iteritems(self._keys) if i == name]
          return keys
  
      def _remove_keys(self, name):
          for k in self.get_tool_keymap(name):
              del self._keys[k]
  
      def update_keymap(self, name, *keys):
          """
          Set the keymap to associate with the specified tool
  
          Parameters
          ----------
          name : string
              Name of the Tool
          keys : keys to associate with the Tool
          """
  
          if name not in self._tools:
              raise KeyError('%s not in Tools' % name)
  
          self._remove_keys(name)
  
          for key in keys:
              for k in validate_stringlist(key):
                  if k in self._keys:
                      warnings.warn('Key %s changed from %s to %s' %
                                    (k, self._keys[k], name))
                  self._keys[k] = name
  
      def remove_tool(self, name):
          """
          Remove tool from `ToolManager`
  
          Parameters
          ----------
          name : string
              Name of the Tool
          """
  
          tool = self.get_tool(name)
          tool.destroy()
  
          # If is a toggle tool and toggled, untoggle
          if getattr(tool, 'toggled', False):
              self.trigger_tool(tool, 'toolmanager')
  
          self._remove_keys(name)
  
          s = 'tool_removed_event'
          event = ToolEvent(s, self, tool)
          self._callbacks.process(s, event)
  
          del self._tools[name]
  
      def add_tool(self, name, tool, *args, **kwargs):
          """
          Add *tool* to `ToolManager`
  
          If successful adds a new event `tool_trigger_name` where **name** is
          the **name** of the tool, this event is fired everytime
          the tool is triggered.
  
          Parameters
          ----------
          name : str
              Name of the tool, treated as the ID, has to be unique
          tool : class_like, i.e. str or type
              Reference to find the class of the Tool to added.
  
          Notes
          -----
          args and kwargs get passed directly to the tools constructor.
  
          See Also
          --------
          matplotlib.backend_tools.ToolBase : The base class for tools.
          """
  
          tool_cls = self._get_cls_to_instantiate(tool)
          if not tool_cls:
              raise ValueError('Impossible to find class for %s' % str(tool))
  
          if name in self._tools:
              warnings.warn('A "Tool class" with the same name already exists, '
                            'not added')
              return self._tools[name]
  
          tool_obj = tool_cls(self, name, *args, **kwargs)
          self._tools[name] = tool_obj
  
          if tool_cls.default_keymap is not None:
              self.update_keymap(name, tool_cls.default_keymap)
  
          # For toggle tools init the radio_group in self._toggled
          if isinstance(tool_obj, tools.ToolToggleBase):
              # None group is not mutually exclusive, a set is used to keep track
              # of all toggled tools in this group
              if tool_obj.radio_group is None:
                  self._toggled.setdefault(None, set())
              else:
                  self._toggled.setdefault(tool_obj.radio_group, None)
  
          self._tool_added_event(tool_obj)
          return tool_obj
  
      def _tool_added_event(self, tool):
          s = 'tool_added_event'
          event = ToolEvent(s, self, tool)
          self._callbacks.process(s, event)
  
      def _handle_toggle(self, tool, sender, canvasevent, data):
          """
          Toggle tools, need to untoggle prior to using other Toggle tool
          Called from trigger_tool
  
          Parameters
          ----------
          tool: Tool object
          sender: object
              Object that wishes to trigger the tool
          canvasevent : Event
              Original Canvas event or None
          data : Object
              Extra data to pass to the tool when triggering
          """
  
          radio_group = tool.radio_group
          # radio_group None is not mutually exclusive
          # just keep track of toggled tools in this group
          if radio_group is None:
              if tool.toggled:
                  self._toggled[None].remove(tool.name)
              else:
                  self._toggled[None].add(tool.name)
              return
  
          # If the tool already has a toggled state, untoggle it
          if self._toggled[radio_group] == tool.name:
              toggled = None
          # If no tool was toggled in the radio_group
          # toggle it
          elif self._toggled[radio_group] is None:
              toggled = tool.name
          # Other tool in the radio_group is toggled
          else:
              # Untoggle previously toggled tool
              self.trigger_tool(self._toggled[radio_group],
                                self,
                                canvasevent,
                                data)
              toggled = tool.name
  
          # Keep track of the toggled tool in the radio_group
          self._toggled[radio_group] = toggled
  
      def _get_cls_to_instantiate(self, callback_class):
          # Find the class that corresponds to the tool
          if isinstance(callback_class, six.string_types):
              # FIXME: make more complete searching structure
              if callback_class in globals():
                  callback_class = globals()[callback_class]
              else:
                  mod = 'backend_tools'
                  current_module = __import__(mod,
                                              globals(), locals(), [mod], 1)
  
                  callback_class = getattr(current_module, callback_class, False)
          if callable(callback_class):
              return callback_class
          else:
              return None
  
      def trigger_tool(self, name, sender=None, canvasevent=None,
                       data=None):
          """
          Trigger a tool and emit the tool_trigger_[name] event
  
          Parameters
          ----------
          name : string
              Name of the tool
          sender: object
              Object that wishes to trigger the tool
          canvasevent : Event
              Original Canvas event or None
          data : Object
              Extra data to pass to the tool when triggering
          """
          tool = self.get_tool(name)
          if tool is None:
              return
  
          if sender is None:
              sender = self
  
          self._trigger_tool(name, sender, canvasevent, data)
  
          s = 'tool_trigger_%s' % name
          event = ToolTriggerEvent(s, sender, tool, canvasevent, data)
          self._callbacks.process(s, event)
  
      def _trigger_tool(self, name, sender=None, canvasevent=None, data=None):
          """
          Trigger on a tool
  
          Method to actually trigger the tool
          """
          tool = self.get_tool(name)
  
          if isinstance(tool, tools.ToolToggleBase):
              self._handle_toggle(tool, sender, canvasevent, data)
  
          # Important!!!
          # This is where the Tool object gets triggered
          tool.trigger(sender, canvasevent, data)
  
      def _key_press(self, event):
          if event.key is None or self.keypresslock.locked():
              return
  
          name = self._keys.get(event.key, None)
          if name is None:
              return
          self.trigger_tool(name, canvasevent=event)
  
      @property
      def tools(self):
          """Return the tools controlled by `ToolManager`"""
  
          return self._tools
  
      def get_tool(self, name, warn=True):
          """
          Return the tool object, also accepts the actual tool for convenience
  
          Parameters
          ----------
          name : str, ToolBase
              Name of the tool, or the tool itself
          warn : bool, optional
              If this method should give warnings.
          """
          if isinstance(name, tools.ToolBase) and name.name in self._tools:
              return name
          if name not in self._tools:
              if warn:
                  warnings.warn("ToolManager does not control tool %s" % name)
              return None
          return self._tools[name]
  