Example #1
0
    def testAdd(self):
        mapper = KeyMapper()
        o1 = object()
        o2 = object()
        o3 = object()

        # Create new ids
        key1 = mapper.key(o1)
        key2 = mapper.key(o2)
        key3 = mapper.key(o3)

        self.assertEquals(mapper.get(key1), o1)
        self.assertEquals(mapper.get(key2), o2)
        self.assertEquals(mapper.get(key3), o3)
        self.assertNotEquals(key1, key2)
        self.assertNotEquals(key1, key3)
        self.assertNotEquals(key2, key3)

        self.assertSize(mapper, 3)

        # Key should not add if there already is a mapping
        self.assertEquals(mapper.key(o3), key3)
        self.assertSize(mapper, 3)

        # Remove -> add should return a new key
        mapper.remove(o1)
        newkey1 = mapper.key(o1)
        self.assertNotEqual(key1, newkey1)
Example #2
0
    def testAdd(self):
        mapper = KeyMapper()
        o1 = object()
        o2 = object()
        o3 = object()

        # Create new ids
        key1 = mapper.key(o1)
        key2 = mapper.key(o2)
        key3 = mapper.key(o3)

        self.assertEquals(mapper.get(key1), o1)
        self.assertEquals(mapper.get(key2), o2)
        self.assertEquals(mapper.get(key3), o3)
        self.assertNotEquals(key1, key2)
        self.assertNotEquals(key1, key3)
        self.assertNotEquals(key2, key3)

        self.assertSize(mapper, 3)

        # Key should not add if there already is a mapping
        self.assertEquals(mapper.key(o3), key3)
        self.assertSize(mapper, 3)

        # Remove -> add should return a new key
        mapper.remove(o1)
        newkey1 = mapper.key(o1)
        self.assertNotEqual(key1, newkey1)
Example #3
0
    def paintActions(self, actionTarget, paintTarget):

        actionMapper = None

        actions = set()
        if self.actionHandlers is not None:
            for handler in self.actionHandlers:
                ac = handler.getActions(actionTarget, self.viewer)
                if ac is not None:
                    for action in ac:
                        actions.add(action)

        if self.ownActions is not None:
            actions = actions.union(self.ownActions)


        # Must repaint whenever there are actions OR if all actions have
        # been removed but still exist on client side
        if len(actions) > 0 or self._clientHasActions:
            actionMapper = KeyMapper()

            paintTarget.addVariable(self.viewer, "action", "")
            paintTarget.startTag("actions")

            for a in actions:
                paintTarget.startTag("action")
                akey = actionMapper.key(a)
                paintTarget.addAttribute("key", akey);
                if a.getCaption() is not None:
                    paintTarget.addAttribute("caption", a.getCaption())

                if a.getIcon() is not None:
                    paintTarget.addAttribute("icon", a.getIcon())

                if isinstance(a, ShortcutAction):
                    sa = a
                    paintTarget.addAttribute("kc", sa.getKeyCode())
                    modifiers = sa.getModifiers()
                    if modifiers is not None:
                        smodifiers = [None] * len(modifiers)
                        for i in range(len(modifiers)):
                            smodifiers[i] = str(modifiers[i])

                        paintTarget.addAttribute("mk", smodifiers)

                paintTarget.endTag("action")

            paintTarget.endTag("actions")

        # Update flag for next repaint so we know if we need to paint empty
        # actions or not (must send actions is client had actions before and
        # all actions were removed).
        self._clientHasActions = len(actions) > 0
Example #4
0
    def paintActions(self, actionTarget, paintTarget):

        self.actionMapper = None

        actions = set()
        if self.actionHandlers is not None:
            for handler in self.actionHandlers:
                ac = handler.getActions(actionTarget, self.viewer)
                if ac is not None:
                    for a in ac:
                        actions.add(a)

        if self.ownActions is not None:
            actions = actions.union(self.ownActions)

        # Must repaint whenever there are actions OR if all actions have
        # been removed but still exist on client side
        if (len(actions) > 0) or self._clientHasActions:
            self.actionMapper = KeyMapper()

            paintTarget.addVariable(self.viewer, "action", "")
            paintTarget.startTag("actions")

            for a in actions:
                paintTarget.startTag("action")
                akey = self.actionMapper.key(a)
                paintTarget.addAttribute("key", akey)
                if a.getCaption() is not None:
                    paintTarget.addAttribute("caption", a.getCaption())

                if a.getIcon() is not None:
                    paintTarget.addAttribute("icon", a.getIcon())

                if isinstance(a, ShortcutAction):
                    sa = a
                    paintTarget.addAttribute("kc", sa.getKeyCode())
                    modifiers = sa.getModifiers()
                    if modifiers is not None:
                        smodifiers = [None] * len(modifiers)
                        for i in range(len(modifiers)):
                            smodifiers[i] = str(modifiers[i])

                        paintTarget.addAttribute("mk", smodifiers)

                paintTarget.endTag("action")

            paintTarget.endTag("actions")

        # Update flag for next repaint so we know if we need to paint empty
        # actions or not (must send actions is client had actions before and
        # all actions were removed).
        self._clientHasActions = len(actions) > 0
Example #5
0
    def testRemoveAll(self):
        mapper = KeyMapper()
        o1 = object()
        o2 = object()
        o3 = object()

        # Create new ids
        mapper.key(o1)
        mapper.key(o2)
        mapper.key(o3)

        self.assertSize(mapper, 3)
        mapper.removeAll()
        self.assertSize(mapper, 0)
Example #6
0
    def addActionHandler(self, actionHandler):
        """Adds an action handler.

        @see: L{IContainer.addActionHandler}
        """
        if actionHandler is not None:
            if self._actionHandlers is None:
                self._actionHandlers = list()
                self._actionMapper = KeyMapper()

            if actionHandler not in self._actionHandlers:
                self._actionHandlers.append(actionHandler)
                self.requestRepaint()
Example #7
0
    def __init__(self):
        """Constructs a new Tabsheet. Tabsheet is immediate by default, and
        the default close handler removes the tab being closed.
        """
        super(TabSheet, self).__init__()

        #: List of component tabs (tab contents). In addition to being on this
        #  list, there is a L{ITab} object in tabs for each tab with
        #  meta-data about the tab.
        self._components = list()

        #: Map containing information related to the tabs (caption, icon etc).
        self._tabs = dict()

        #: Selected tab content component.
        self._selected = None

        #: Mapper between server-side component instances (tab contents) and
        #  keys given to the client that identify tabs.
        self._keyMapper = KeyMapper()

        #: When true, the tab selection area is not displayed to the user.
        self._tabsHidden = None

        #: Tabs that have been shown to the user (have been painted as
        #  selected).
        self._paintedTabs = set()

        #: Handler to be called when a tab is closed.
        self._closeHandler = None

        #: expand horizontally by default
        self.setWidth(100.0, self.UNITS_PERCENTAGE)
        self.setImmediate(True)

        self.setCloseHandler(InnerHandler())
Example #8
0
    def testRemoveAll(self):
        mapper = KeyMapper()
        o1 = object()
        o2 = object()
        o3 = object()

        # Create new ids
        mapper.key(o1)
        mapper.key(o2)
        mapper.key(o3)

        self.assertSize(mapper, 3)
        mapper.removeAll()
        self.assertSize(mapper, 0)
Example #9
0
    def __init__(self):
        """Constructs a new Tabsheet. Tabsheet is immediate by default, and
        the default close handler removes the tab being closed.
        """
        super(TabSheet, self).__init__()

        #: List of component tabs (tab contents). In addition to being on this
        #  list, there is a L{ITab} object in tabs for each tab with
        #  meta-data about the tab.
        self._components = list()

        #: Map containing information related to the tabs (caption, icon etc).
        self._tabs = dict()

        #: Selected tab content component.
        self._selected = None

        #: Mapper between server-side component instances (tab contents) and
        #  keys given to the client that identify tabs.
        self._keyMapper = KeyMapper()

        #: When true, the tab selection area is not displayed to the user.
        self._tabsHidden = None

        #: Tabs that have been shown to the user (have been painted as
        #  selected).
        self._paintedTabs = set()

        #: Handler to be called when a tab is closed.
        self._closeHandler = None

        #: expand horizontally by default
        self.setWidth(100.0, self.UNITS_PERCENTAGE)
        self.setImmediate(True)

        self.setCloseHandler(InnerHandler())
Example #10
0
class ActionManager(action.IHandler, action.INotifier):  #action.IContainer
    """Notes:

    Empties the keymapper for each repaint to avoid leaks; can cause problems
    in the future if the client assumes key don't change. (if lazyloading, one
    must not cache results)
    """
    def __init__(self, viewer=None):
        #: List of action handlers
        self.ownActions = None

        #: List of action handlers
        self.actionHandlers = None

        #: Action mapper
        self.actionMapper = None

        self._clientHasActions = False

        self.viewer = viewer

    def requestRepaint(self):
        if self.viewer is not None:
            self.viewer.requestRepaint()

    def setViewer(self, viewer):
        if viewer == self.viewer:
            return

        if self.viewer is not None:
            self.viewer.removeActionHandler(self)

        self.requestRepaint()  # this goes to the old viewer
        if viewer is not None:
            viewer.addActionHandler(self)

        self.viewer = viewer
        self.requestRepaint()  # this goes to the new viewer

    def addAction(self, action):
        if self.ownActions is None:
            self.ownActions = set()

        if self.ownActions.add(action):
            self.requestRepaint()

    def removeAction(self, action):
        if self.ownActions is not None:
            if self.ownActions.remove(action):
                self.requestRepaint()

    def addActionHandler(self, actionHandler):
        if actionHandler == self:
            # don't add the actionHandler to itself
            return

        if actionHandler is not None:
            if self.actionHandlers is None:
                self.actionHandlers = set()

            if actionHandler not in self.actionHandlers:
                self.actionHandlers.add(actionHandler)
                self.requestRepaint()

    def removeActionHandler(self, actionHandler):
        if (self.actionHandlers is not None
                and actionHandler in self.actionHandlers):
            if self.actionHandlers.remove(actionHandler):
                self.requestRepaint()

            if len(self.actionHandlers) == 0:
                self.actionHandlers = None

    def removeAllActionHandlers(self):
        if self.actionHandlers is not None:
            self.actionHandlers = None
            self.requestRepaint()

    def paintActions(self, actionTarget, paintTarget):

        self.actionMapper = None

        actions = set()
        if self.actionHandlers is not None:
            for handler in self.actionHandlers:
                ac = handler.getActions(actionTarget, self.viewer)
                if ac is not None:
                    for a in ac:
                        actions.add(a)

        if self.ownActions is not None:
            actions = actions.union(self.ownActions)

        # Must repaint whenever there are actions OR if all actions have
        # been removed but still exist on client side
        if (len(actions) > 0) or self._clientHasActions:
            self.actionMapper = KeyMapper()

            paintTarget.addVariable(self.viewer, "action", "")
            paintTarget.startTag("actions")

            for a in actions:
                paintTarget.startTag("action")
                akey = self.actionMapper.key(a)
                paintTarget.addAttribute("key", akey)
                if a.getCaption() is not None:
                    paintTarget.addAttribute("caption", a.getCaption())

                if a.getIcon() is not None:
                    paintTarget.addAttribute("icon", a.getIcon())

                if isinstance(a, ShortcutAction):
                    sa = a
                    paintTarget.addAttribute("kc", sa.getKeyCode())
                    modifiers = sa.getModifiers()
                    if modifiers is not None:
                        smodifiers = [None] * len(modifiers)
                        for i in range(len(modifiers)):
                            smodifiers[i] = str(modifiers[i])

                        paintTarget.addAttribute("mk", smodifiers)

                paintTarget.endTag("action")

            paintTarget.endTag("actions")

        # Update flag for next repaint so we know if we need to paint empty
        # actions or not (must send actions is client had actions before and
        # all actions were removed).
        self._clientHasActions = len(actions) > 0

    def handleActions(self, variables, sender):
        if 'action' in variables and self.actionMapper is not None:
            key = variables.get('action')
            a = self.actionMapper.get(key)
            target = variables.get('actiontarget')
            if a is not None:
                self.handleAction(a, sender, target)

    def getActions(self, target, sender):
        actions = set()
        if self.ownActions is not None:
            for a in self.ownActions:
                actions.add(a)

        if self.actionHandlers is not None:
            for h in self.actionHandlers:
                as_ = h.getActions(target, sender)
                if as_ is not None:
                    for a in as_:
                        actions.add(a)

        return list(actions)

    def handleAction(self, a, sender, target):
        if self.actionHandlers is not None:
            arry = list(self.actionHandlers)
            for handler in arry:
                handler.handleAction(a, sender, target)

        if ((self.ownActions is not None) and (a in self.ownActions)
                and isinstance(a, action.IListener)):
            a.handleAction(sender, target)
Example #11
0
class ActionManager(action.IHandler, action.INotifier):  #action.IContainer
    """Notes:

    Empties the keymapper for each repaint to avoid leaks; can cause problems
    in the future if the client assumes key don't change. (if lazyloading, one
    must not cache results)
    """

    def __init__(self, viewer=None):
        #: List of action handlers
        self.ownActions = None

        #: List of action handlers
        self.actionHandlers = None

        #: Action mapper
        self.actionMapper = None

        self._clientHasActions = False

        self.viewer = viewer


    def requestRepaint(self):
        if self.viewer is not None:
            self.viewer.requestRepaint()


    def setViewer(self, viewer):
        if viewer == self.viewer:
            return

        if self.viewer is not None:
            self.viewer.removeActionHandler(self)

        self.requestRepaint()  # this goes to the old viewer
        if viewer is not None:
            viewer.addActionHandler(self)

        self.viewer = viewer
        self.requestRepaint()  # this goes to the new viewer


    def addAction(self, action):
        if self.ownActions is None:
            self.ownActions = set()

        if self.ownActions.add(action):
            self.requestRepaint()


    def removeAction(self, action):
        if self.ownActions is not None:
            if self.ownActions.remove(action):
                self.requestRepaint()


    def addActionHandler(self, actionHandler):
        if actionHandler == self:
            # don't add the actionHandler to itself
            return

        if actionHandler is not None:
            if self.actionHandlers is None:
                self.actionHandlers = set()

            if actionHandler not in self.actionHandlers:
                self.actionHandlers.add(actionHandler)
                self.requestRepaint()


    def removeActionHandler(self, actionHandler):
        if (self.actionHandlers is not None
                and actionHandler in self.actionHandlers):
            if self.actionHandlers.remove(actionHandler):
                self.requestRepaint()

            if len(self.actionHandlers) == 0:
                self.actionHandlers = None


    def removeAllActionHandlers(self):
        if self.actionHandlers is not None:
            self.actionHandlers = None
            self.requestRepaint()



    def paintActions(self, actionTarget, paintTarget):

        self.actionMapper = None

        actions = set()
        if self.actionHandlers is not None:
            for handler in self.actionHandlers:
                ac = handler.getActions(actionTarget, self.viewer)
                if ac is not None:
                    for a in ac:
                        actions.add(a)

        if self.ownActions is not None:
            actions = actions.union(self.ownActions)


        # Must repaint whenever there are actions OR if all actions have
        # been removed but still exist on client side
        if (len(actions) > 0) or self._clientHasActions:
            self.actionMapper = KeyMapper()

            paintTarget.addVariable(self.viewer, "action", "")
            paintTarget.startTag("actions")

            for a in actions:
                paintTarget.startTag("action")
                akey = self.actionMapper.key(a)
                paintTarget.addAttribute("key", akey);
                if a.getCaption() is not None:
                    paintTarget.addAttribute("caption", a.getCaption())

                if a.getIcon() is not None:
                    paintTarget.addAttribute("icon", a.getIcon())

                if isinstance(a, ShortcutAction):
                    sa = a
                    paintTarget.addAttribute("kc", sa.getKeyCode())
                    modifiers = sa.getModifiers()
                    if modifiers is not None:
                        smodifiers = [None] * len(modifiers)
                        for i in range(len(modifiers)):
                            smodifiers[i] = str(modifiers[i])

                        paintTarget.addAttribute("mk", smodifiers)

                paintTarget.endTag("action")

            paintTarget.endTag("actions")

        # Update flag for next repaint so we know if we need to paint empty
        # actions or not (must send actions is client had actions before and
        # all actions were removed).
        self._clientHasActions = len(actions) > 0


    def handleActions(self, variables, sender):
        if 'action' in variables and self.actionMapper is not None:
            key = variables.get('action')
            a = self.actionMapper.get(key)
            target = variables.get('actiontarget')
            if a is not None:
                self.handleAction(a, sender, target)


    def getActions(self, target, sender):
        actions = set()
        if self.ownActions is not None:
            for a in self.ownActions:
                actions.add(a)

        if self.actionHandlers is not None:
            for h in self.actionHandlers:
                as_ = h.getActions(target, sender)
                if as_ is not None:
                    for a in as_:
                        actions.add(a)

        return list(actions)


    def handleAction(self, a, sender, target):
        if self.actionHandlers is not None:
            arry = list(self.actionHandlers)
            for handler in arry:
                handler.handleAction(a, sender, target)

        if ((self.ownActions is not None)
                and (a in self.ownActions)
                and isinstance(a, action.IListener)):
            a.handleAction(sender, target)
Example #12
0
class TabSheet(AbstractComponentContainer):
    """TabSheet component.

    Tabs are typically identified by the component contained on the tab (see
    L{ComponentContainer}), and tab metadata (including caption, icon,
    visibility, enabledness, closability etc.) is kept in separate {@link
    ITab} instances.

    Tabs added with L{addComponent} get the caption and the
    icon of the component at the time when the component is created, and these
    are not automatically updated after tab creation.

    A tab sheet can have multiple tab selection listeners and one tab close
    handler (L{ICloseHandler}), which by default removes the tab from the
    TabSheet.

    The L{TabSheet} can be styled with the .v-tabsheet, .v-tabsheet-tabs
    and .v-tabsheet-content styles. Themes may also have pre-defined
    variations of the tab sheet presentation, such as
    L{Reindeer.TABSHEET_BORDERLESS}, L{Runo.TABSHEET_SMALL} and
    several other styles in L{Reindeer}.

    The current implementation does not load the tabs to the UI before the
    first time they are shown, but this may change in future releases.

    @author: IT Mill Ltd.
    @author: Richard Lincoln
    @version: @VERSION@
    """

    CLIENT_WIDGET = None #ClientWidget(VTabsheet, LoadStyle.EAGER)

    def __init__(self):
        """Constructs a new Tabsheet. Tabsheet is immediate by default, and
        the default close handler removes the tab being closed.
        """
        super(TabSheet, self).__init__()

        #: List of component tabs (tab contents). In addition to being on this
        #  list, there is a L{ITab} object in tabs for each tab with
        #  meta-data about the tab.
        self._components = list()

        #: Map containing information related to the tabs (caption, icon etc).
        self._tabs = dict()

        #: Selected tab content component.
        self._selected = None

        #: Mapper between server-side component instances (tab contents) and
        #  keys given to the client that identify tabs.
        self._keyMapper = KeyMapper()

        #: When true, the tab selection area is not displayed to the user.
        self._tabsHidden = None

        #: Tabs that have been shown to the user (have been painted as
        #  selected).
        self._paintedTabs = set()

        #: Handler to be called when a tab is closed.
        self._closeHandler = None

        #: expand horizontally by default
        self.setWidth(100.0, self.UNITS_PERCENTAGE)
        self.setImmediate(True)

        self.setCloseHandler(InnerHandler())


    def getComponentIterator(self):
        """Gets the component container iterator for going through all the
        components (tab contents).

        @return: the unmodifiable Iterator of the tab content components
        """
        return iter(self._components)


    def getComponentCount(self):
        """Gets the number of contained components (tabs). Consistent with
        the iterator returned by L{getComponentIterator}.

        @return: the number of contained components
        """
        return len(self._components)


    def removeComponent(self, c):
        """Removes a component and its corresponding tab.

        If the tab was selected, the first eligible (visible and enabled)
        remaining tab is selected.

        @param c: the component to be removed.
        """
        if c is not None and c in self._components:
            super(TabSheet, self).removeComponent(c)
            self._keyMapper.remove(c)
            self._components.remove(c)
            if c in self._tabs:
                del self._tabs[c]
            if c == self._selected:
                if len(self._components) == 0:
                    self._selected = None
                else:
                    # select the first enabled and visible tab, if any
                    self.updateSelection()
                    self.fireSelectedTabChange()

            self.requestRepaint()


    def removeTab(self, tab):
        """Removes a L{ITab} and the component associated with it, as
        previously added with L{addTab}, or L{addComponent}.

        If the tab was selected, the first eligible (visible and enabled)
        remaining tab is selected.

        @see: L{addTab}
        @see: L{addComponent}
        @see: L{removeComponent}
        @param tab: the ITab to remove
        """
        self.removeComponent(tab.getComponent())


    def addComponent(self, c):
        """Adds a new tab into TabSheet. IComponent caption and icon are
        copied to the tab metadata at creation time.

        @see: L{addTab}
        @param c: the component to be added.
        """
        self.addTab(c)


    def addTab(self, *args):
        """Adds a new tab into TabSheet.

        The first tab added to a tab sheet is automatically selected and a
        tab selection event is fired.

        If the component is already present in the tab sheet, changes its
        caption and icon and returns the corresponding (old) tab, preserving
        other tab metadata.

        @param args: tuple of the form
            - (c, caption, icon)
              1. the component to be added onto tab - should not be null.
              2. the caption to be set for the component and used rendered
                 in tab bar
              3. the icon to be set for the component and used rendered in
                 tab bar
            - (c, caption, icon, position)
              1. the component to be added onto tab - should not be null.
              2. the caption to be set for the component and used rendered
                 in tab bar
              3. the icon to be set for the component and used rendered in
                 tab bar
              4. the position at where the the tab should be added
            - (c)
              1. the component to be added onto tab - should not be null.
            - (c, position)
              1. the component to be added onto tab - should not be null.
              2. The position where the tab should be added

        @return: the created L{ITab}
        """
        nargs = len(args)
        if nargs == 1:
            c, = args
            return self.addTab(c, len(self._components))
        elif nargs == 2:
            c, position = args
            if c is None:
                return None
            elif c in self._tabs:
                return self._tabs.get(c)
            else:
                return self.addTab(c, c.getCaption(), c.getIcon(), position)
        elif nargs == 3:
            c, caption, icon = args
            return self.addTab(c, caption, icon, len(self._components))
        elif nargs == 4:
            c, caption, icon, position = args
            if c is None:
                return None
            elif c in self._tabs:
                tab = self._tabs.get(c)
                tab.setCaption(caption)
                tab.setIcon(icon)
                return tab
            else:
                self._components.insert(position, c)
                tab = TabSheetTabImpl(caption, icon, self)
                self._tabs[c] = tab
                if self._selected is None:
                    self._selected = c
                    self.fireSelectedTabChange()
                super(TabSheet, self).addComponent(c)
                self.requestRepaint()
                return tab
        else:
            raise ValueError, 'invalid number of arguments'


    def moveComponentsFrom(self, source):
        """Moves all components from another container to this container. The
        components are removed from the other container.

        If the source container is a L{TabSheet}, component captions and
        icons are copied from it.

        @param source:
                   the container components are removed from.
        """
        i = source.getComponentIterator()
        while True:
            try:
                c = i.next()
                caption = None
                icon = None
                if issubclass(source.__class__, TabSheet):
                    caption = source.getTabCaption(c)
                    icon = source.getTabIcon(c)
                source.removeComponent(c)
                self.addTab(c, caption, icon)
            except StopIteration:
                break


    def paintContent(self, target):
        """Paints the content of this component.

        @param target:
                   the paint target
        @raise PaintException:
                    if the paint operation failed.
        """
        if self.areTabsHidden():
            target.addAttribute('hidetabs', True)

        target.startTag('tabs')

        orphaned = set(self._paintedTabs)

        i = self.getComponentIterator()
        while True:
            try:
                component = i.next()
                if component in orphaned:
                    orphaned.remove(component)
                tab = self._tabs.get(component)
                target.startTag('tab')
                if not tab.isEnabled() and tab.isVisible():
                    target.addAttribute('disabled', True)

                if not tab.isVisible():
                    target.addAttribute('hidden', True)

                if tab.isClosable():
                    target.addAttribute('closable', True)

                icon = tab.getIcon()
                if icon is not None:
                    target.addAttribute('icon', icon)

                caption = tab.getCaption()
                if caption is not None and len(caption) > 0:
                    target.addAttribute('caption', caption)

                description = tab.getDescription()
                if description is not None:
                    target.addAttribute('description', description)

                componentError = tab.getComponentError()
                if componentError is not None:
                    componentError.paint(target)

                target.addAttribute('key', self._keyMapper.key(component))
                if component == self._selected:
                    target.addAttribute('selected', True)
                    component.paint(target)
                    self._paintedTabs.add(component)

                elif component in self._paintedTabs:
                    component.paint(target)
                else:
                    component.requestRepaintRequests()
                target.endTag('tab')
            except StopIteration:
                break

        target.endTag('tabs')

        if self._selected is not None:
            target.addVariable(self, 'selected',
                    self._keyMapper.key(self._selected))

        # clean possibly orphaned entries in paintedTabs
        for component2 in orphaned:
            self._paintedTabs.remove(component2)


    def areTabsHidden(self):
        """Are the tab selection parts ("tabs") hidden.

        @return: true if the tabs are hidden in the UI
        """
        return self._tabsHidden


    def hideTabs(self, tabsHidden):
        """Hides or shows the tab selection parts ("tabs").

        @param tabsHidden:
                   true if the tabs should be hidden
        """
        self._tabsHidden = tabsHidden
        self.requestRepaint()


    def getTabCaption(self, c):
        """Gets tab caption. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @deprecated: Use L{getTab} and L{ITab.getCaption} instead.
        """
        warn('use getTab() and ITab.getCaption() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is None:
            return ''
        else:
            return info.getCaption()


    def setTabCaption(self, c, caption):
        """Sets tab caption. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @param caption: the caption to set.
        @deprecated: Use L{getTab} and L{ITab.setCaption} instead.
        """
        warn('use getTab() and ITab.getCaption() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is not None:
            info.setCaption(caption)
            self.requestRepaint()


    def getTabIcon(self, c):
        """Gets the icon for a tab. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @deprecated: Use L{getTab} and L{ITab.getIcon} instead.
        """
        warn('use getTab() and ITab.getIcon() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is None:
            return None
        else:
            return info.getIcon()


    def setTabIcon(self, c, icon):
        """Sets icon for the given component. The tab is identified by the
        tab content component.

        @param c:
                   the component in the tab
        @param icon:
                   the icon to set
        @deprecated: Use L{getTab} and L{ITab.setIcon} instead.
        """
        warn('use getTab() and ITab.getIcon() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is not None:
            info.setIcon(icon)
            self.requestRepaint()


    def getTab(self, arg):
        """Returns the L{ITab} (metadata) for a component. The
        L{ITab} object can be used for setting caption,icon, etc
        for the tab.

        @param arg:
                   the component or the position of the tab
        """
        if isinstance(arg, IComponent):
            c = arg
            return self._tabs.get(c)
        else:
            position = arg
            c = self._components[position]
            if c is not None:
                return self._tabs.get(c)
            return None


    def setSelectedTab(self, c):
        """Sets the selected tab. The tab is identified by the tab content
        component.
        """
        if (c is not None and c in self._components
                and c != self._selected):
            self._selected = c
            self.updateSelection()
            self.fireSelectedTabChange()
            self.requestRepaint()


    def updateSelection(self):
        """Checks if the current selection is valid, and updates the selection
        if the previously selected component is not visible and enabled. The
        first visible and enabled tab is selected if the current selection is
        empty or invalid.

        This method does not fire tab change events, but the caller should do
        so if appropriate.

        @return: true if selection was changed, false otherwise
        """
        originalSelection = self._selected

        i = self.getComponentIterator()
        while True:
            try:
                component = i.next()
                tab = self._tabs.get(component)
                # If we have no selection, if the current selection is
                # invisible or if the current selection is disabled (but
                # the whole component is not) we select this tab instead
                selectedTabInfo = None
                if self._selected is not None:
                    selectedTabInfo = self._tabs.get(self._selected)

                if (self._selected is None
                        or selectedTabInfo is None
                        or not selectedTabInfo.isVisible()
                        or not selectedTabInfo.isEnabled()):
                    # The current selection is not valid so we need to
                    # change it
                    if tab.isEnabled() and tab.isVisible():
                        self._selected = component
                        break
                    else:
                        # The current selection is not valid but this
                        # tab cannot be selected either.
                        self._selected = None
            except StopIteration:
                break

        return originalSelection != self._selected


    def getSelectedTab(self):
        """Gets the selected tab content component.

        @return: the selected tab contents
        """
        return self._selected


    def changeVariables(self, source, variables):
        if 'selected' in variables:
            select = self._keyMapper.get(variables.get('selected'))
            self.setSelectedTab(select)

        if 'close' in variables:
            tab = self._keyMapper.get(variables.get('close'))

            if tab is not None:
                self._closeHandler.onTabClose(self, tab)


    def replaceComponent(self, oldComponent, newComponent):
        """Replaces a component (tab content) with another. This can be used
        to change tab contents or to rearrange tabs. The tab position and some
        metadata are preserved when moving components within the same
        L{TabSheet}.

        If the oldComponent is not present in the tab sheet, the new one is
        added at the end.

        If the oldComponent is already in the tab sheet but the newComponent
        isn't, the old tab is replaced with a new one, and the caption and
        icon of the old one are copied to the new tab.

        If both old and new components are present, their positions are
        swapped.
        """
        if self._selected == oldComponent:
            # keep selection w/o selectedTabChange event
            self._selected = newComponent

        newTab = self._tabs.get(newComponent)
        oldTab = self._tabs.get(oldComponent)

        # Gets the captions
        oldCaption = None
        oldIcon = None
        newCaption = None
        newIcon = None

        if oldTab is not None:
            oldCaption = oldTab.getCaption()
            oldIcon = oldTab.getIcon()

        if newTab is not None:
            newCaption = newTab.getCaption()
            newIcon = newTab.getIcon()
        else:
            newCaption = newComponent.getCaption()
            newIcon = newComponent.getIcon()

        # Gets the locations
        oldLocation = -1
        newLocation = -1
        location = 0
        for component in self._components:
            if component == oldComponent:
                oldLocation = location
            if component == newComponent:
                newLocation = location
            location += 1

        if oldLocation == -1:
            self.addComponent(newComponent)
        elif newLocation == -1:
            self.removeComponent(oldComponent)
            self._keyMapper.remove(oldComponent)
            newTab = self.addTab(newComponent)
            self._components.remove(newComponent)
            self._components.append(oldLocation, newComponent)
            newTab.setCaption(oldCaption)
            newTab.setIcon(oldIcon)
        else:
            if oldLocation > newLocation:
                self._components.remove(oldComponent)
                self._components.append(newLocation, oldComponent)
                self._components.remove(newComponent)
                self._components.append(oldLocation, newComponent)
            else:
                self._components.remove(newComponent)
                self._components.append(oldLocation, newComponent)
                self._components.remove(oldComponent)
                self._components.append(newLocation, oldComponent)

            if newTab is not None:
                # This should always be true
                newTab.setCaption(oldCaption)
                newTab.setIcon(oldIcon)

            if oldTab is not None:
                # This should always be true
                oldTab.setCaption(newCaption)
                oldTab.setIcon(newIcon)

            self.requestRepaint()


    def addListener(self, listener, iface=None):
        """Adds a tab selection listener

        @param listener:
                   the Listener to be added.
        """
        if (isinstance(listener, ISelectedTabChangeListener) and
                (iface is None or iface == ISelectedTabChangeListener)):
            self.registerListener(SelectedTabChangeEvent,
                    listener, _SELECTED_TAB_CHANGE_METHOD)

        super(TabSheet, self).addListener(listener, iface)


    def removeListener(self, listener, iface=None):
        """Removes a tab selection listener

        @param listener:
                   the Listener to be removed.
        """
        if (isinstance(listener, IRepaintRequestListener) and
                (iface is None or iface == IRepaintRequestListener)):
            super(TabSheet, self).removeListener(listener, iface)
            if isinstance(listener, CommunicationManager):
                # clean the paintedTabs here instead of detach to avoid subtree
                # caching issues when detached-attached without render
                self._paintedTabs.clear()

        if (isinstance(listener, ISelectedTabChangeListener) and
                (iface is None or iface == ISelectedTabChangeListener)):
            self.withdrawListener(SelectedTabChangeEvent,
                    listener, _SELECTED_TAB_CHANGE_METHOD)

        super(TabSheet, self).removeListener(listener, iface)


    def fireSelectedTabChange(self):
        """Sends an event that the currently selected tab has changed."""
        self.fireEvent( SelectedTabChangeEvent(self) )


    def setCloseHandler(self, handler):
        """Provide a custom L{ICloseHandler} for this TabSheet if you
        wish to perform some additional tasks when a user clicks on a tabs
        close button, e.g. show a confirmation dialogue before removing the
        tab.

        To remove the tab, if you provide your own close handler, you must
        call L{removeComponent} yourself.

        The default ICloseHandler for TabSheet will only remove the tab.
        """
        self._closeHandler = handler


    def setTabPosition(self, tab, position):
        """Sets the position of the tab.

        @param tab:
                   The tab
        @param position:
                   The new position of the tab
        """
        oldPosition = self.getTabPosition(tab)
        self._components.remove(oldPosition)
        self._components.append(position, tab.getComponent())
        self.requestRepaint()


    def getTabPosition(self, tab):
        """Gets the position of the tab

        @param tab:
                   The tab
        """
        try:
            return self._components.index( tab.getComponent() )
        except ValueError:
            return -1
Example #13
0
class TabSheet(AbstractComponentContainer):
    """TabSheet component.

    Tabs are typically identified by the component contained on the tab (see
    L{ComponentContainer}), and tab metadata (including caption, icon,
    visibility, enabledness, closability etc.) is kept in separate {@link
    ITab} instances.

    Tabs added with L{addComponent} get the caption and the
    icon of the component at the time when the component is created, and these
    are not automatically updated after tab creation.

    A tab sheet can have multiple tab selection listeners and one tab close
    handler (L{ICloseHandler}), which by default removes the tab from the
    TabSheet.

    The L{TabSheet} can be styled with the .v-tabsheet, .v-tabsheet-tabs
    and .v-tabsheet-content styles. Themes may also have pre-defined
    variations of the tab sheet presentation, such as
    L{Reindeer.TABSHEET_BORDERLESS}, L{Runo.TABSHEET_SMALL} and
    several other styles in L{Reindeer}.

    The current implementation does not load the tabs to the UI before the
    first time they are shown, but this may change in future releases.

    @author: Vaadin Ltd.
    @author: Richard Lincoln
    @version: @VERSION@
    """

    CLIENT_WIDGET = None  #ClientWidget(VTabsheet, LoadStyle.EAGER)

    def __init__(self):
        """Constructs a new Tabsheet. Tabsheet is immediate by default, and
        the default close handler removes the tab being closed.
        """
        super(TabSheet, self).__init__()

        #: List of component tabs (tab contents). In addition to being on this
        #  list, there is a L{ITab} object in tabs for each tab with
        #  meta-data about the tab.
        self._components = list()

        #: Map containing information related to the tabs (caption, icon etc).
        self._tabs = dict()

        #: Selected tab content component.
        self._selected = None

        #: Mapper between server-side component instances (tab contents) and
        #  keys given to the client that identify tabs.
        self._keyMapper = KeyMapper()

        #: When true, the tab selection area is not displayed to the user.
        self._tabsHidden = None

        #: Tabs that have been shown to the user (have been painted as
        #  selected).
        self._paintedTabs = set()

        #: Handler to be called when a tab is closed.
        self._closeHandler = None

        #: expand horizontally by default
        self.setWidth(100.0, self.UNITS_PERCENTAGE)
        self.setImmediate(True)

        self.setCloseHandler(InnerHandler())

    def getComponentIterator(self):
        """Gets the component container iterator for going through all the
        components (tab contents).

        @return: the unmodifiable Iterator of the tab content components
        """
        return iter(self._components)

    def getComponentCount(self):
        """Gets the number of contained components (tabs). Consistent with
        the iterator returned by L{getComponentIterator}.

        @return: the number of contained components
        """
        return len(self._components)

    def removeComponent(self, c):
        """Removes a component and its corresponding tab.

        If the tab was selected, the first eligible (visible and enabled)
        remaining tab is selected.

        @param c: the component to be removed.
        """
        if c is not None and c in self._components:
            super(TabSheet, self).removeComponent(c)
            self._keyMapper.remove(c)
            self._components.remove(c)
            if c in self._tabs:
                del self._tabs[c]
            if c == self._selected:
                if len(self._components) == 0:
                    self._selected = None
                else:
                    # select the first enabled and visible tab, if any
                    self.updateSelection()
                    self.fireSelectedTabChange()

            self.requestRepaint()

    def removeTab(self, tab):
        """Removes a L{ITab} and the component associated with it, as
        previously added with L{addTab}, or L{addComponent}.

        If the tab was selected, the first eligible (visible and enabled)
        remaining tab is selected.

        @see: L{addTab}
        @see: L{addComponent}
        @see: L{removeComponent}
        @param tab: the ITab to remove
        """
        self.removeComponent(tab.getComponent())

    def addComponent(self, c):
        """Adds a new tab into TabSheet. IComponent caption and icon are
        copied to the tab metadata at creation time.

        @see: L{addTab}
        @param c: the component to be added.
        """
        self.addTab(c)

    def addTab(self, *args):
        """Adds a new tab into TabSheet.

        The first tab added to a tab sheet is automatically selected and a
        tab selection event is fired.

        If the component is already present in the tab sheet, changes its
        caption and icon and returns the corresponding (old) tab, preserving
        other tab metadata.

        @param args: tuple of the form
            - (c, caption, icon)
              1. the component to be added onto tab - should not be null.
              2. the caption to be set for the component and used rendered
                 in tab bar
              3. the icon to be set for the component and used rendered in
                 tab bar
            - (c, caption, icon, position)
              1. the component to be added onto tab - should not be null.
              2. the caption to be set for the component and used rendered
                 in tab bar
              3. the icon to be set for the component and used rendered in
                 tab bar
              4. the position at where the the tab should be added
            - (c)
              1. the component to be added onto tab - should not be null.
            - (c, position)
              1. the component to be added onto tab - should not be null.
              2. The position where the tab should be added
            - (c, caption)
              1. the component to be added onto tab - should not be null.
              2. the caption to be set for the component and used rendered
                 in tab bar

        @return: the created L{ITab}
        """
        nargs = len(args)
        if nargs == 1:
            c, = args
            return self.addTab(c, len(self._components))
        elif nargs == 2:
            if isinstance(args[1], basestring):
                c, caption = args
                self.addTab(c, caption, None)
            else:
                c, position = args
                if c is None:
                    return None
                elif c in self._tabs:
                    return self._tabs.get(c)
                else:
                    return self.addTab(c, c.getCaption(), c.getIcon(),
                                       position)
        elif nargs == 3:
            c, caption, icon = args
            return self.addTab(c, caption, icon, len(self._components))
        elif nargs == 4:
            c, caption, icon, position = args
            if c is None:
                return None
            elif c in self._tabs:
                tab = self._tabs.get(c)
                tab.setCaption(caption)
                tab.setIcon(icon)
                return tab
            else:
                self._components.insert(position, c)
                tab = TabSheetTabImpl(caption, icon, self)
                self._tabs[c] = tab
                if self._selected is None:
                    self._selected = c
                    self.fireSelectedTabChange()
                super(TabSheet, self).addComponent(c)
                self.requestRepaint()
                return tab
        else:
            raise ValueError, 'invalid number of arguments'

    def moveComponentsFrom(self, source):
        """Moves all components from another container to this container. The
        components are removed from the other container.

        If the source container is a L{TabSheet}, component captions and
        icons are copied from it.

        @param source:
                   the container components are removed from.
        """
        i = source.getComponentIterator()
        while True:
            try:
                c = i.next()
                caption = None
                icon = None
                if issubclass(source.__class__, TabSheet):
                    caption = source.getTabCaption(c)
                    icon = source.getTabIcon(c)
                source.removeComponent(c)
                self.addTab(c, caption, icon)
            except StopIteration:
                break

    def paintContent(self, target):
        """Paints the content of this component.

        @param target:
                   the paint target
        @raise PaintException:
                    if the paint operation failed.
        """
        if self.areTabsHidden():
            target.addAttribute('hidetabs', True)

        target.startTag('tabs')

        orphaned = set(self._paintedTabs)

        i = self.getComponentIterator()
        while True:
            try:
                component = i.next()
                if component in orphaned:
                    orphaned.remove(component)
                tab = self._tabs.get(component)
                target.startTag('tab')
                if not tab.isEnabled() and tab.isVisible():
                    target.addAttribute('disabled', True)

                if not tab.isVisible():
                    target.addAttribute('hidden', True)

                if tab.isClosable():
                    target.addAttribute('closable', True)

                icon = tab.getIcon()
                if icon is not None:
                    target.addAttribute('icon', icon)

                caption = tab.getCaption()
                if caption is not None and len(caption) > 0:
                    target.addAttribute('caption', caption)

                description = tab.getDescription()
                if description is not None:
                    target.addAttribute('description', description)

                componentError = tab.getComponentError()
                if componentError is not None:
                    componentError.paint(target)

                target.addAttribute('key', self._keyMapper.key(component))
                if component == self._selected:
                    target.addAttribute('selected', True)
                    component.paint(target)
                    self._paintedTabs.add(component)

                elif component in self._paintedTabs:
                    component.paint(target)
                else:
                    component.requestRepaintRequests()
                target.endTag('tab')
            except StopIteration:
                break

        target.endTag('tabs')

        if self._selected is not None:
            target.addVariable(self, 'selected',
                               self._keyMapper.key(self._selected))

        # clean possibly orphaned entries in paintedTabs
        for component2 in orphaned:
            self._paintedTabs.remove(component2)

    def areTabsHidden(self):
        """Are the tab selection parts ("tabs") hidden.

        @return: true if the tabs are hidden in the UI
        """
        return self._tabsHidden

    def hideTabs(self, tabsHidden):
        """Hides or shows the tab selection parts ("tabs").

        @param tabsHidden:
                   true if the tabs should be hidden
        """
        self._tabsHidden = tabsHidden
        self.requestRepaint()

    def getTabCaption(self, c):
        """Gets tab caption. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @deprecated: Use L{getTab} and L{ITab.getCaption} instead.
        """
        warn('use getTab() and ITab.getCaption() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is None:
            return ''
        else:
            return info.getCaption()

    def setTabCaption(self, c, caption):
        """Sets tab caption. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @param caption: the caption to set.
        @deprecated: Use L{getTab} and L{ITab.setCaption} instead.
        """
        warn('use getTab() and ITab.getCaption() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is not None:
            info.setCaption(caption)
            self.requestRepaint()

    def getTabIcon(self, c):
        """Gets the icon for a tab. The tab is identified by the tab content
        component.

        @param c: the component in the tab
        @deprecated: Use L{getTab} and L{ITab.getIcon} instead.
        """
        warn('use getTab() and ITab.getIcon() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is None:
            return None
        else:
            return info.getIcon()

    def setTabIcon(self, c, icon):
        """Sets icon for the given component. The tab is identified by the
        tab content component.

        @param c:
                   the component in the tab
        @param icon:
                   the icon to set
        @deprecated: Use L{getTab} and L{ITab.setIcon} instead.
        """
        warn('use getTab() and ITab.getIcon() instead', DeprecationWarning)

        info = self._tabs.get(c)
        if info is not None:
            info.setIcon(icon)
            self.requestRepaint()

    def getTab(self, arg):
        """Returns the L{ITab} (metadata) for a component. The
        L{ITab} object can be used for setting caption,icon, etc
        for the tab.

        @param arg:
                   the component or the position of the tab
        """
        if isinstance(arg, IComponent):
            c = arg
            return self._tabs.get(c)
        else:
            position = arg
            c = self._components[position]
            if c is not None:
                return self._tabs.get(c)
            return None

    def setSelectedTab(self, c):
        """Sets the selected tab. The tab is identified by the tab content
        component.
        """
        if (c is not None and c in self._components and c != self._selected):
            self._selected = c
            self.updateSelection()
            self.fireSelectedTabChange()
            self.requestRepaint()

    def updateSelection(self):
        """Checks if the current selection is valid, and updates the selection
        if the previously selected component is not visible and enabled. The
        first visible and enabled tab is selected if the current selection is
        empty or invalid.

        This method does not fire tab change events, but the caller should do
        so if appropriate.

        @return: true if selection was changed, false otherwise
        """
        originalSelection = self._selected

        i = self.getComponentIterator()
        while True:
            try:
                component = i.next()
                tab = self._tabs.get(component)
                # If we have no selection, if the current selection is
                # invisible or if the current selection is disabled (but
                # the whole component is not) we select this tab instead
                selectedTabInfo = None
                if self._selected is not None:
                    selectedTabInfo = self._tabs.get(self._selected)

                if (self._selected is None or selectedTabInfo is None
                        or not selectedTabInfo.isVisible()
                        or not selectedTabInfo.isEnabled()):
                    # The current selection is not valid so we need to
                    # change it
                    if tab.isEnabled() and tab.isVisible():
                        self._selected = component
                        break
                    else:
                        # The current selection is not valid but this
                        # tab cannot be selected either.
                        self._selected = None
            except StopIteration:
                break

        return originalSelection != self._selected

    def getSelectedTab(self):
        """Gets the selected tab content component.

        @return: the selected tab contents
        """
        return self._selected

    def changeVariables(self, source, variables):
        if 'selected' in variables:
            select = self._keyMapper.get(variables.get('selected'))
            self.setSelectedTab(select)

        if 'close' in variables:
            tab = self._keyMapper.get(variables.get('close'))

            if tab is not None:
                self._closeHandler.onTabClose(self, tab)

    def replaceComponent(self, oldComponent, newComponent):
        """Replaces a component (tab content) with another. This can be used
        to change tab contents or to rearrange tabs. The tab position and some
        metadata are preserved when moving components within the same
        L{TabSheet}.

        If the oldComponent is not present in the tab sheet, the new one is
        added at the end.

        If the oldComponent is already in the tab sheet but the newComponent
        isn't, the old tab is replaced with a new one, and the caption and
        icon of the old one are copied to the new tab.

        If both old and new components are present, their positions are
        swapped.
        """
        if self._selected == oldComponent:
            # keep selection w/o selectedTabChange event
            self._selected = newComponent

        newTab = self._tabs.get(newComponent)
        oldTab = self._tabs.get(oldComponent)

        # Gets the captions
        oldCaption = None
        oldIcon = None
        newCaption = None
        newIcon = None

        if oldTab is not None:
            oldCaption = oldTab.getCaption()
            oldIcon = oldTab.getIcon()

        if newTab is not None:
            newCaption = newTab.getCaption()
            newIcon = newTab.getIcon()
        else:
            newCaption = newComponent.getCaption()
            newIcon = newComponent.getIcon()

        # Gets the locations
        oldLocation = -1
        newLocation = -1
        location = 0
        for component in self._components:
            if component == oldComponent:
                oldLocation = location
            if component == newComponent:
                newLocation = location
            location += 1

        if oldLocation == -1:
            self.addComponent(newComponent)
        elif newLocation == -1:
            self.removeComponent(oldComponent)
            self._keyMapper.remove(oldComponent)
            newTab = self.addTab(newComponent)
            self._components.remove(newComponent)
            self._components.append(oldLocation, newComponent)
            newTab.setCaption(oldCaption)
            newTab.setIcon(oldIcon)
        else:
            if oldLocation > newLocation:
                self._components.remove(oldComponent)
                self._components.append(newLocation, oldComponent)
                self._components.remove(newComponent)
                self._components.append(oldLocation, newComponent)
            else:
                self._components.remove(newComponent)
                self._components.append(oldLocation, newComponent)
                self._components.remove(oldComponent)
                self._components.append(newLocation, oldComponent)

            if newTab is not None:
                # This should always be true
                newTab.setCaption(oldCaption)
                newTab.setIcon(oldIcon)

            if oldTab is not None:
                # This should always be true
                oldTab.setCaption(newCaption)
                oldTab.setIcon(newIcon)

            self.requestRepaint()

    def addListener(self, listener, iface=None):
        """Adds a tab selection listener

        @param listener:
                   the Listener to be added.
        """
        if (isinstance(listener, ISelectedTabChangeListener) and
            (iface is None or issubclass(iface, ISelectedTabChangeListener))):
            self.registerListener(SelectedTabChangeEvent, listener,
                                  _SELECTED_TAB_CHANGE_METHOD)

        super(TabSheet, self).addListener(listener, iface)

    def addCallback(self, callback, eventType=None, *args):
        if eventType is None:
            eventType = callback._eventType

        if issubclass(eventType, SelectedTabChangeEvent):
            self.registerCallback(SelectedTabChangeEvent, callback, None,
                                  *args)
        else:
            super(TabSheet, self).addCallback(callback, eventType, *args)

    def removeListener(self, listener, iface=None):
        """Removes a tab selection listener

        @param listener:
                   the Listener to be removed.
        """
        if (isinstance(listener, IRepaintRequestListener) and
            (iface is None or issubclass(iface, IRepaintRequestListener))):
            super(TabSheet, self).removeListener(listener, iface)
            if isinstance(listener, CommunicationManager):
                # clean the paintedTabs here instead of detach to avoid subtree
                # caching issues when detached-attached without render
                self._paintedTabs.clear()

        if (isinstance(listener, ISelectedTabChangeListener) and
            (iface is None or issubclass(iface, ISelectedTabChangeListener))):
            self.withdrawListener(SelectedTabChangeEvent, listener,
                                  _SELECTED_TAB_CHANGE_METHOD)

        super(TabSheet, self).removeListener(listener, iface)

    def removeCallback(self, callback, eventType=None):
        if eventType is None:
            eventType = callback._eventType

        if issubclass(eventType, SelectedTabChangeEvent):
            self.withdrawCallback(SelectedTabChangeEvent, callback)
        else:
            super(TabSheet, self).removeCallback(callback, eventType)

    def fireSelectedTabChange(self):
        """Sends an event that the currently selected tab has changed."""
        event = SelectedTabChangeEvent(self)
        self.fireEvent(event)

    def setCloseHandler(self, handler):
        """Provide a custom L{ICloseHandler} for this TabSheet if you
        wish to perform some additional tasks when a user clicks on a tabs
        close button, e.g. show a confirmation dialogue before removing the
        tab.

        To remove the tab, if you provide your own close handler, you must
        call L{removeComponent} yourself.

        The default ICloseHandler for TabSheet will only remove the tab.
        """
        self._closeHandler = handler

    def setTabPosition(self, tab, position):
        """Sets the position of the tab.

        @param tab:
                   The tab
        @param position:
                   The new position of the tab
        """
        oldPosition = self.getTabPosition(tab)
        self._components.remove(oldPosition)
        self._components.append(position, tab.getComponent())
        self.requestRepaint()

    def getTabPosition(self, tab):
        """Gets the position of the tab

        @param tab: The tab
        """
        try:
            return self._components.index(tab.getComponent())
        except ValueError:
            return -1