# The selection service

It is quite common in GUI applications to have a UI element displaying a collection of items that a user can select (“selection providers”), while other parts of the application must react to changes in the selection (“selection listeners”).

Ideally, the listeners would not have a direct dependency on the UI object. This is especially important in extensible envisage applications, where a plugin might need to react to a selection change, but we do not want to expose the internal organization of the application to external developers.

This package defines a selection service that manages the communication between providers and listener.

## The `SelectionService` object

The `SelectionService` object is the central manager that handles the communication between selection providers and listener.

Selection providers are components that wish to publish information about their current selection for public consumption. They register to a selection service instance when they first have a selection available (e.g., when the UI showing a list of selectable items is initialized), and un-register as soon as the selection is not available anymore (e.g., the UI is destroyed when the windows is closed).

Selection listeners can query the selection service to get the current selection published by a provider, using the provider unique ID.

The service acts as a broker between providers and listeners, making sure that they are notified when the `selection` event is fired.

## Selection providers

Any object can become a selection provider by implementing the `ISelectionProvider` interface, and registering to the selection service.

Selection providers must provide a unique ID `provider_id`, which is used by listeners to request its current selection.

Whenever its selection changes, providers fire a `selection` event. The content of the event is an instance implementing `ISelection` that contains information about the selected items. For example, a `ListSelection` object contains a list of selected items, and their indices.

Selection providers can also be queried directly about their current selection using the `get_selection` method, and can be requested to change their selection to a new one with the `set_selection` method.

### Registration

Selection providers publish their selection by registering to the selection service using the `add_selection_provider` method. When the selection is no longer available, selection providers should un-register through `remove_selection_provider`.

Typically, selection providers are UI objects showing a list or tree of items, they register as soon as the UI component is initialized, and un-register when the UI component disappears (e.g., because their window has been closed). In more complex applications, the registration could be done by a controller object instead.

## Selection listeners

Selection listeners request information regarding the current selection of a selection provider given their provider ID. The `SelectionService` supports two distinct use cases:

> 1. Passively listening to selection changes: listener connect to a specific provider and are notified when the provider’s selection changes.
> 2. Actively querying a provider for its current selection: the selection service can be used to query a provider using its unique ID.

### Passive listening

Listeners connect to the selection events for a given provider using the `connect_selection_listener` method. They need to provide the unique ID of the provider, and a function (or callable) that is called to send the event. This callback function takes one argument, an implementation of the `ISelection` that represents the selection.

It is possible for a listener to connect to a provider ID before it is registered. As soon as the provider is registered, the listener will receive a notification containing the provider’s initial selection.

To disconnect a listener use the methods `disconnect_selection_listener`.

### Active querying

In other instances, an element of the application only needs the current selection at a specific time. For example, a toolbar button could open dialog representing a user action based on what is currently selected in the active editor.

The `get_selection` method calls the corresponding method on the provider with the given ID and returns an `ISelection` instance.

### Setting a selection

Finally, it is possible to request a provider to set its selection to a given set of objects with `set_selection`. The main use case for this method is multiple views of the same list of objects, which need to keep their selection synchronized.

If the items specified in the arguments are not available in the provider, a `ProviderNotRegisteredError` is raised, unless the optional keyword argument `ignore_missing` is set to `True`.

## API Reference

### `apptools.selection` Package

Users of the `apptools.selection` package can access the objects that are part of the public API through the convenience `apptools.selection.api`.

#### `selection_service` Module

class `apptools.selection.selection_service.``SelectionService`

  Bases: `traits.has_traits.HasTraits`

  The selection service connects selection providers and listeners.

  The selection service is a register of selection providers, i.e., objects that publish their current selection.

  Selections can be requested actively, by explicitly requesting the current selection in a provider (`get_selection(id)()`), or passively by connecting selection listeners.

  `add_selection_provider`(provider)

    Add a selection provider.

    The provider is identified by its ID. If a provider with the same ID has been already registered, an `IDConflictError` is raised.

    Arguments:
      provider – ISelectionProvider
        The selection provider added to the internal registry.
  `connect_selection_listener`(provider_id, func)

    Connect a listener to selection events from a specific provider.

    The signature if the listener callback is `func(i_selection)`. The listener is called:

    1. When a provider with the given ID is registered, with its initial selection as argument, or
    2. whenever the provider fires a selection event.

    It is perfectly valid to connect a listener before a provider with the given ID is registered. The listener will remain connected even if the provider is repeatedly connected and disconnected.

    Arguments:
      provider_id – str
        The selection provider ID.
      func – callable(i_selection)
        A callable object that is notified when the selection changes.
  `disconnect_selection_listener`(provider_id, func)

    Disconnect a listener from a specific provider.

    Arguments:
      provider_id – str
        The selection provider ID.
      func – callable(provider_id, i_selection)
        A callable object that is notified when the selection changes.
  `get_selection`(provider_id)

    Return the current selection of the provider with the given ID.

    If a provider with that ID has not been registered, a `ProviderNotRegisteredError` is raised.

    Arguments:
      provider_id – str
        The selection provider ID.
    Returns:
      selection – ISelection
        The current selection of the provider.
  `has_selection_provider`(provider_id)

    Has a provider with the given ID been registered?

  `remove_selection_provider`(provider)

    Remove a selection provider.

    If the provider has not been registered, a `ProviderNotRegisteredError` is raised.

    Arguments:
      provider – ISelectionProvider
        The selection provider added to the internal registry.
  `set_selection`(provider_id, items, ignore_missing=False)

    Set the current selection in a provider to the given items.

    If a provider with the given ID has not been registered, a `ProviderNotRegisteredError` is raised.

    If `ignore_missing` is `True`, items that are not available in the selection provider are silently ignored. If it is `False` (default), a `ValueError` should be raised.

    Arguments:
      provider_id – str
        The selection provider ID.
      items – list
        List of items to be selected.
      ignore_missing – bool
        If `False` (default), the provider raises an exception if any of the items in `items` is not available to be selected. Otherwise, missing elements are silently ignored, and the rest is selected.

#### `i_selection_provider` Module

class `apptools.selection.i_selection_provider.``ISelectionProvider`

  Bases: `traits.has_traits.Interface`

  Source of selections.

  `get_selection`()

    Return the current selection.

    Returns:
      selection – ISelection
        Object representing the current selection.
  `provider_id` = Str()

    Unique ID identifying the provider.

  `selection` = Event

    Event triggered when the selection changes. The content of the event is an `ISelection` instance.

  `set_selection`(items, ignore_missing=False)

    Set the current selection to the given items.

    If `ignore_missing` is `True`, items that are not available in the selection provider are silently ignored. If it is `False` (default), an `ValueError` should be raised.

    Arguments:
      items – list
        List of items to be selected.
      ignore_missing – bool
        If `False` (default), the provider raises an exception if any of the items in `items` is not available to be selected. Otherwise, missing elements are silently ignored, and the rest is selected.

#### `is_selection` Module

class `apptools.selection.i_selection.``IListSelection`

  Bases: `apptools.selection.i_selection.ISelection`

  Selection for ordered sequences of items.

  `indices` = List

    Indices of the selected objects in the selection provider.

  `items` = List

    Selected objects.

class `apptools.selection.i_selection.``ISelection`

  Bases: `traits.has_traits.Interface`

  Collection of selected items.

  `is_empty`()

    Is the selection empty?

  `provider_id` = Str

    ID of the selection provider that created this selection object.

#### `list_selection` Module

class `apptools.selection.list_selection.``ListSelection`

  Bases: `traits.has_traits.HasTraits`

  Selection for ordered sequences of items.

  This is the default implementation of the `IListSelection` interface.

  classmethod `from_available_items`(provider_id, selected, all_items)

    Create a list selection given a list of all available items.

    Fills in the required information (in particular, the indices) based on a list of selected items and a list of all available items.

    Note

    - The list of available items must not contain any duplicate items.
    - It is expected that `selected` is populated by items in `all_items`.
  `indices` = List

    Indices of the selected objects in the selection provider.

  `is_empty`()

    Is the selection empty?

  `items` = List

    Selected objects.

  `provider_id` = Str

    ID of the selection provider that created this selection object.

#### `errors` Module

exception `apptools.selection.errors.``IDConflictError`(provider_id)

  Bases: `Exception`

  Raised when a provider is added and its ID is already registered.

exception `apptools.selection.errors.``ListenerNotConnectedError`(provider_id, listener)

  Bases: `Exception`

  Raised when a listener that was never connected is disconnected.

exception `apptools.selection.errors.``ProviderNotRegisteredError`(provider_id)

  Bases: `Exception`

  Raised when a provider is requested by ID and not found.