def init(self, parent):
        """ 
        Finishes initializing the editor by creating the underlying toolkit widget.
        """

        factory = self.factory
        self.notebook = VerticalNotebook(**factory.get(
            'multiple_open', 'delete', 'scrollable', 'double_click')).set(
                editor=self)
        self.control = self.notebook.create_control(parent)

        # Set up the additional 'list items changed' event handler needed for
        # a list based trait:
        self.context_object.on_trait_change(self.update_editor_item,
                                            self.extended_name + '_items?',
                                            dispatch='ui')

        # Synchronize the editor selection with the user selection:
        if factory.multiple_open:
            self.sync_value(factory.selected,
                            'selected_list',
                            'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected_item', 'both')

        self.set_tooltip()
    def init(self, parent):
        """ 
        Finishes initializing the editor by creating the underlying toolkit widget.
        """
        
        factory = self.factory
        self.notebook = VerticalNotebook(**factory.get('multiple_open',
                                                       'delete',
                                                       'scrollable', 
                                                       'double_click')).set(editor=self)
        self.control = self.notebook.create_control(parent)

        # Set up the additional 'list items changed' event handler needed for
        # a list based trait:
        self.context_object.on_trait_change(self.update_editor_item,
                                            self.extended_name + '_items?', dispatch='ui')

        # Synchronize the editor selection with the user selection:
        if factory.multiple_open:
            self.sync_value(factory.selected, 'selected_list', 'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected_item', 'both')

        self.set_tooltip()
class _VerticalNotebookEditor(Editor):
    """ 
    Traits UI vertical notebook editor for editing lists of objects with traits.
    """

    #-- Trait Definitions ----------------------------------------------------

    # Is the notebook editor scrollable? This values overrides the default:
    scrollable = True

    #-- Private Traits -------------------------------------------------------

    # The currently selected notebook page object (or objects):
    selected_item = Any
    selected_list = List

    # The ThemedVerticalNotebook we use to manage the notebook:
    notebook = Instance(VerticalNotebook)

    # Dictionary of page counts for all unique names:
    pages = Any({})

    #-- Editor Methods -------------------------------------------------------

    def init(self, parent):
        """ 
        Finishes initializing the editor by creating the underlying toolkit widget.
        """

        factory = self.factory
        self.notebook = VerticalNotebook(**factory.get(
            'multiple_open', 'delete', 'scrollable', 'double_click')).set(
                editor=self)
        self.control = self.notebook.create_control(parent)

        # Set up the additional 'list items changed' event handler needed for
        # a list based trait:
        self.context_object.on_trait_change(self.update_editor_item,
                                            self.extended_name + '_items?',
                                            dispatch='ui')

        # Synchronize the editor selection with the user selection:
        if factory.multiple_open:
            self.sync_value(factory.selected,
                            'selected_list',
                            'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected_item', 'both')

        self.set_tooltip()

    def update_editor(self):
        """ 
        Updates the editor when the object trait changes externally to the editor.
        """

        # Replace all of the current notebook pages:
        self.notebook.pages = [self._create_page(obj) for obj in self.value]

    def update_editor_item(self, event):
        """ 
        Handles an update to some subset of the trait's list.
        """

        # Replace the updated notebook pages:
        self.notebook.pages[event.index: event.index + len(event.removed)] \
            = [self._create_page(obj) for obj in event.added]

    def dispose(self):
        """ 
        Disposes of the contents of an editor. 
        """

        self.context_object.on_trait_change(self.update_editor_item,
                                            self.name + '_items?',
                                            remove=True)
        del self.notebook.pages[:]

        super(_VerticalNotebookEditor, self).dispose()

    #-- Trait Event Handlers -------------------------------------------------

    def _selected_item_changed(self, old, new):
        """ 
        Handles the selected item being changed.
        """

        if new is not None:
            self.notebook.open(self._find_page(new))
        elif old is not None:
            self.notebook.close(self._find_page(old))

    def _selected_list_changed(self, old, new):
        """ 
        Handles the selected list being changed.
        """

        notebook = self.notebook
        for obj in old:
            notebook.close(self._find_page(obj))

        for obj in new:
            notebook.open(self._find_page(obj))

    def _selected_list_items_changed(self, event):
        self._selected_list_changed(event.removed, event.added)

    @on_trait_change('notebook:pages:is_open')
    def _page_state_modified(self, page, name, old, is_open):
        if self.factory.multiple_open:
            obj = page.data
            if is_open:
                if obj not in self.selected_list:
                    self.selected_list.append(obj)
            elif obj in self.selected_list:
                self.selected_list.remove(obj)
        elif is_open:
            self.selected_item = page.data
        else:
            self.selected_item = None

    #-- Private Methods ------------------------------------------------------

    def _create_page(self, obj):
        """ 
        Creates and returns a notebook page for a specified object with traits.
        """
        # Create a new notebook page:
        page = self.notebook.create_page().set(data=obj)

        # Create the Traits UI for the object to put in the notebook page:
        ui = obj.edit_traits(parent=page.parent,
                             view=self.factory.view,
                             kind='subpanel').set(parent=self.ui)

        # Get the name of the page being added to the notebook:
        name = ''
        page_name = self.factory.page_name
        if page_name[0:1] == '.':
            if getattr(obj, page_name[1:], Undefined) is not Undefined:
                page.register_name_listener(obj, page_name[1:])
        else:
            name = page_name

        if name == '':
            name = user_name_for(obj.__class__.__name__)

        # Make sure the name is not a duplicate, then save it in the page:
        if page.name == '':
            self.pages[name] = count = self.pages.get(name, 0) + 1
            if count > 1:
                name += (' %d' % count)
            page.name = name

        # Get the page description
        page_desc = self.factory.page_description
        if page_desc[0:1] == '.':
            if getattr(obj, page_desc[1:], Undefined) is not Undefined:
                page.register_description_listener(obj, page_desc[1:])

        # Get the page icon
        page_icon = self.factory.page_icon
        if page_icon[0:1] == '.':
            if getattr(obj, page_icon[1:], Undefined) is not Undefined:
                page.register_icon_listener(obj, page_icon[1:])

        # Is the page deletable?
        page_deletable = self.factory.page_deletable
        if page_deletable[0:1] == '.':
            if getattr(obj, page_deletable[1:], Undefined) is not Undefined:
                page.register_deletable_listener(obj, page_deletable[1:])

        # Save the Traits UI in the page so it can dispose of it later:
        page.ui = ui

        # Return the new notebook page
        return page

    def _find_page(self, obj):
        """ 
        Find the notebook page corresponding to a specified object. Returns
        the page if found, and **None** otherwise.
        """
        for page in self.notebook.pages:
            if obj is page.data:
                return page

        return None
class _VerticalNotebookEditor(Editor):
    """ 
    Traits UI vertical notebook editor for editing lists of objects with traits.
    """

    #-- Trait Definitions ----------------------------------------------------

    # Is the notebook editor scrollable? This values overrides the default:
    scrollable = True

    #-- Private Traits -------------------------------------------------------

    # The currently selected notebook page object (or objects):
    selected_item = Any
    selected_list = List

    # The ThemedVerticalNotebook we use to manage the notebook:
    notebook = Instance(VerticalNotebook)

    # Dictionary of page counts for all unique names:
    pages = Any({})

    #-- Editor Methods -------------------------------------------------------

    def init(self, parent):
        """ 
        Finishes initializing the editor by creating the underlying toolkit widget.
        """
        
        factory = self.factory
        self.notebook = VerticalNotebook(**factory.get('multiple_open',
                                                       'delete',
                                                       'scrollable', 
                                                       'double_click')).set(editor=self)
        self.control = self.notebook.create_control(parent)

        # Set up the additional 'list items changed' event handler needed for
        # a list based trait:
        self.context_object.on_trait_change(self.update_editor_item,
                                            self.extended_name + '_items?', dispatch='ui')

        # Synchronize the editor selection with the user selection:
        if factory.multiple_open:
            self.sync_value(factory.selected, 'selected_list', 'both',
                            is_list=True)
        else:
            self.sync_value(factory.selected, 'selected_item', 'both')

        self.set_tooltip()

    def update_editor(self):
        """ 
        Updates the editor when the object trait changes externally to the editor.
        """
        
        # Replace all of the current notebook pages:
        self.notebook.pages = [self._create_page(obj) for obj in self.value]

    def update_editor_item(self, event):
        """ 
        Handles an update to some subset of the trait's list.
        """
        
        # Replace the updated notebook pages:
        self.notebook.pages[event.index: event.index + len(event.removed)] \
            = [self._create_page(obj) for obj in event.added]

    def dispose(self):
        """ 
        Disposes of the contents of an editor. 
        """
        
        self.context_object.on_trait_change(self.update_editor_item,
                                            self.name + '_items?', remove=True)
        del self.notebook.pages[:]

        super(_VerticalNotebookEditor, self).dispose()

    #-- Trait Event Handlers -------------------------------------------------

    def _selected_item_changed(self, old, new):
        """ 
        Handles the selected item being changed.
        """
        
        if new is not None:
            self.notebook.open(self._find_page(new))
        elif old is not None:
            self.notebook.close(self._find_page(old))

    def _selected_list_changed(self, old, new):
        """ 
        Handles the selected list being changed.
        """
        
        notebook = self.notebook
        for obj in old:
            notebook.close(self._find_page(obj))

        for obj in new:
            notebook.open(self._find_page(obj))

    def _selected_list_items_changed(self, event):
        self._selected_list_changed(event.removed, event.added)

    @on_trait_change('notebook:pages:is_open')
    def _page_state_modified(self, page, name, old, is_open):
        if self.factory.multiple_open:
            obj = page.data
            if is_open:
                if obj not in self.selected_list:
                    self.selected_list.append(obj)
            elif obj in self.selected_list:
                self.selected_list.remove(obj)
        elif is_open:
            self.selected_item = page.data
        else:
            self.selected_item = None

    #-- Private Methods ------------------------------------------------------

    def _create_page(self, obj):
        """ 
        Creates and returns a notebook page for a specified object with traits.
        """
        # Create a new notebook page:
        page = self.notebook.create_page().set(data = obj)

        # Create the Traits UI for the object to put in the notebook page:
        ui = obj.edit_traits(parent=page.parent,
                             view=self.factory.view,
                             kind='subpanel').set(parent=self.ui)

        # Get the name of the page being added to the notebook:
        name = ''
        page_name = self.factory.page_name
        if page_name[0:1] == '.':
            if getattr(obj, page_name[1:], Undefined) is not Undefined:
                page.register_name_listener(obj, page_name[1:])
        else:
            name = page_name

        if name == '':
            name = user_name_for(obj.__class__.__name__)

        # Make sure the name is not a duplicate, then save it in the page:
        if page.name == '':
            self.pages[name] = count = self.pages.get(name, 0) + 1
            if count > 1:
                name += (' %d' % count)
            page.name = name

        # Get the page description
        page_desc = self.factory.page_description
        if page_desc[0:1] == '.':
            if getattr(obj, page_desc[1:], Undefined) is not Undefined:
                page.register_description_listener(obj, page_desc[1:])
            
        # Get the page icon
        page_icon = self.factory.page_icon
        if page_icon[0:1] == '.':
            if getattr(obj, page_icon[1:], Undefined) is not Undefined:
                page.register_icon_listener(obj, page_icon[1:])
                
        # Is the page deletable?
        page_deletable = self.factory.page_deletable
        if page_deletable[0:1] == '.':
            if getattr(obj, page_deletable[1:], Undefined) is not Undefined:
                page.register_deletable_listener(obj, page_deletable[1:])

        # Save the Traits UI in the page so it can dispose of it later:
        page.ui = ui

        # Return the new notebook page
        return page

    def _find_page(self, obj):
        """ 
        Find the notebook page corresponding to a specified object. Returns
        the page if found, and **None** otherwise.
        """
        for page in self.notebook.pages:
            if obj is page.data:
                return page

        return None