class SetCollection(ContentCollection):
    """
    A collection class to wrap an arbitrary set that doesn't include trash
    """

    __metaclass__ = schema.CollectionClass
    __collection__ = 'set'

    set = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'))

    def withoutTrash(self, copy=True):
        return self
class KindCollection(ContentCollection):
    """
    A ContentCollection of all of the items of a particular kind.

    The C{kind} attribute determines the C{Kind} of the items in the
    C{KindCollection}.

    The C{recursive} attribute determines whether items of subkinds are
    included (C{False}) by default).
    """

    __metaclass__ = schema.CollectionClass
    __collection__ = 'set'

    set = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'))

    kind = schema.One(schema.TypeReference('//Schema/Core/Kind'))
    recursive = schema.One(schema.Boolean, defaultValue=False)

    def __setup__(self):
        setattr(self, self.__collection__, KindSet(self.kind, self.recursive))
Example #3
0
class KindCollection(AbstractCollection):
    """
    """
    schema.kindInfo(
        displayName="KindCollection"
    )

    kind = schema.One(schema.TypeReference('//Schema/Core/Kind'), initialValue=None)

    def contentsUpdated(self, item):
#        print "KindCollection.contentsUpdated: ",item
        self._collectionChanged('changed' , 'rep', item)
        pass

    def onValueChanged(self, name):
        if name == "kind":
            try:
                self.rep = KindSet(self.kind)
            except AttributeError:
                pass
Example #4
0
class Sidebar(ControlBlocks.Table):

    filterKind = schema.One(
        schema.TypeReference('//Schema/Core/Kind'), initialValue = None,
    )

    # A dictionary of display names of items that don't show as Calendar Views
    dontShowCalendarForItemsWithName = schema.Mapping (schema.Boolean)

    # A dictionary mapping a name,kindpathComponent string to a new name.
    # It would be much nicer if the key could be a (name, kindItem) tuple, but
    # that's not possible with current parcel XML
    nameAlternatives = schema.Mapping (schema.String)

    # A list of the items in the sidebar that are checked
    checkedItems = schema.Sequence (schema.Item, initialValue = [])

    # For each button, a list of left offset, right offset and height.
    # Left offset is the offset in pixels of the left edge of the button
    # where positive values are from the left edge of the cell rect and
    # negative values are from the left edge of the cell rect. Height
    # is the height of the button which is centered vertically in the
    # cell. A height of zero uses the height of the cell
    buttonOffsets = schema.Mapping (schema.List, initialValue = {})
    
    editRectOffsets = schema.Sequence (schema.Integer, required = True)

    schema.addClouds(
        copying = schema.Cloud(byRef=[filterKind])
    )

    def instantiateWidget (self):
        if '__WXMAC__' in wx.PlatformInfo:
            widget = wxSidebar (self.parentBlock.widget, Block.Block.getWidgetID(self), style=wx.BORDER_SIMPLE)
        else:
            widget = wxSidebar (self.parentBlock.widget, Block.Block.getWidgetID(self), style = wx.BORDER_STATIC)
        widget.RegisterDataType ("Item", SSSidebarRenderer(), SSSidebarEditor("Item"))
        return widget

    def onKindParameterizedEvent (self, event):                
        self.filterKind = event.kindParameter
        # We need to update the click state of the toolbar as well
        toolbar = Block.Block.findBlockByName("ApplicationBar")
        for button in toolbar.childrenBlocks:
            try:
                buttonEvent = button.event
            except:
                continue
            if buttonEvent == event:
                button.widget.selectTool()
                continue
        self.widget.Refresh()
        self.postEventByName("SelectItemBroadcast", {'item':self.selectedItemToView})

    def onRequestSelectSidebarItemEvent (self, event):
        # Request the sidebar to change selection
        # Item specified is usually by name
        try:
            item = event.arguments['item']
        except KeyError:
            # find the item by name
            itemName = event.arguments['itemName']
            for item in self.contents:
                if item.displayName == itemName:
                    break
            else:
                return

        self.postEventByName("SelectItemBroadcast", {'item':item})
        return item

    def onRemoveItemEvent(self, event):
        """
        Permanently remove the collection - we eventually need a user
        confirmation here
        """
        def deleteItem(item):
            # TODO: for item collectionsactually call item.delete(),
            # and also delete any items that exist only in the
            # doomed itemcollection (garbage collection would be a
            # big help here)

            # in the mean time, just remove it.
            self.contents.remove(item)

        self.widget.DeleteSelection(deleteItem)

    onDeleteItemEvent = onRemoveItemEvent

    def onDeleteEventUpdateUI(self, event):
        self.onRemoveEventUpdateUI(event)
        event.arguments['Text'] = _('Delete Collection')
        """
        this is enabled if any user item is selected in the sidebar
        """
        if (self.selectedItemToView and
            getattr(self.selectedItemToView, 'renameable', True)):
            event.arguments['Enable'] = True
        else:
            event.arguments['Enable'] = False
            
    def onRemoveEventUpdateUI(self, event):
        # doesn't seem to be taking effect?
        event.arguments['Enabled'] = False
            
    def getButtonState (self, buttonName, item):
        if buttonName == u'Icon':
            if item in self.checkedItems:
                return 'Checked'
        elif buttonName == u'SharingIcon':
            share = Sharing.getShare(item)
            if share is not None:
                if (share.sharer is not None and
                    str(share.sharer.itsPath) == "//userdata/me"):
                    return "Upload"
                else:
                    return "Download"
        return ""

    def setButtonState (self, buttonName, item, value):
        if buttonName == u'Icon':
            if item in self.checkedItems:
                if not value:
                    self.checkedItems.remove (item)
            else:
                if value:
                    self.checkedItems.append (item)
        elif buttonName == u'SharingIcon':
            """
              $$$ We need to hook up sharing here -- DJA
            """
            pass
        else:
            assert (False)
class AppCollection(ContentCollection):
    """
    AppCollections implement inclusions, exclusions, source,
    and trash along with methods for add and remove.
    """

    __metaclass__ = schema.CollectionClass
    __collection__ = 'set'

    set = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'))

    # must be named 'inclusions' to match ListCollection
    inclusions = inclusions

    # the exclusions used when no exclusions collection is given
    collectionExclusions = schema.Sequence(inverse=ContentItem.excludedBy,
                                           initialValue=[])

    exclusionsCollection = schema.One(inverse=ContentCollection.exclusionsFor,
                                      defaultValue=None)
    trashCollection = schema.One(inverse=ContentCollection.trashFor,
                                 defaultValue=None)

    schema.initialValues(
        # better hope osaf.pim has been loaded!
        trash=lambda self: schema.ns('osaf.pim', self.itsView).trashCollection,
        source=lambda self: None,
    )

    # __collection__ denotes a bi-ref set,
    # therefore it must be added to the copying cloud def for it to be copied.

    schema.addClouds(copying=schema.Cloud(byCloud=[exclusionsCollection],
                                          byRef=[
                                              trashCollection, __collection__,
                                              inclusions, collectionExclusions
                                          ]), )

    # an AppCollection may have another collection for exclusions and that
    # other collection may be the global trash collection. If no collection
    # is specified for exclusions, a local ref collection is used instead.

    def _getExclusions(self):
        exclusions = self.exclusionsCollection
        if exclusions is None:
            exclusions = self.collectionExclusions
        return exclusions

    def _setExclusions(self, exclusions):
        # Typically we will create a collectionExclusions ref collection;
        # however, a collection like 'All' will instead want to use the
        # Trash collection for exclusions
        if exclusions is None:
            self.collectionExclusions = []
            self.exclusionsCollection = None
        else:
            self.exclusionsCollection = exclusions
            if hasattr(self, 'collectionExclusions'):
                del self.collectionExclusions
        self._updateCollection()

    exclusions = property(_getExclusions, _setExclusions)

    # an AppCollection may have another collection for trash. If no
    # collection is given for trash, the collection's exclusions is used
    # instead following the logic above.

    def _getTrash(self):
        trash = self.trashCollection
        if trash is None:
            trash = self.exclusions
        return trash

    def _setTrash(self, trash):
        """
        In general trash should only be the well known Trash
        collection or None. None indicates that this collection does
        not participate in Trash-based activities.

        The special value of Default for trash is only a sentinel to
        let us know that nothing has been passed in and that the
        default trash should be looked up in osaf.pim. During parcel
        loading, this allows us to pass the trash into the constructor
        and avoid trying to look it up in osaf.pim while osaf.pim is
        being loaded.
        """
        # You can designate a certain ListCollection to be used for this
        # collection's trash; in this case, an additional DifferenceCollection
        # will be created to remove any trash items from this collection. Any
        # collections which share a trash get the following benefits:
        # - Adding an item to the trash will make the item disappear from
        #   collections sharing that trash collection
        # - When an item is removed from a collection, it will automatically
        #   be moved to the trash if it doesn't appear in any collection which
        #   shares that trash
        self.trashCollection = trash
        self._updateCollection()

    trash = property(_getTrash, _setTrash)

    def _getSource(self):
        return self.__source

    def _setSource(self, source):
        if hasattr(self, 'source'):
            self.__source = source
            self._updateCollection()
        else:
            self.__source = source

    source = property(_getSource, _setSource)

    def add(self, item):
        """
        Add an item to the collection.
        """
        self.inclusions.add(item)

        exclusions = self.exclusions
        if item in exclusions:
            exclusions.remove(item)

        # If a trash is associated with this collection, remove the item
        # from the trash.  This has the additional benefit of having the item
        # reappear in any collection which has the item in its inclusions

        trash = self.trash
        if trash is not None and item in trash:
            trash.remove(item)

    def remove(self, item):
        """
        Remove an item from the collection.
        """

        isDeleting = item.isDeleting()

        # never add occurrences to the trash or exclusions lists, bug 10777
        isOccurrence = item.itsRefs.get('inheritFrom') is not None

        # adding to exclusions before determining if the item should be added to
        # the trash was a problem at one point (bug 4551), but since the mine/
        # not-mine mechanism changed, this doesn't seem to be a problem anymore,
        # and removing from a mine collection was actually misbehaving if the
        # test was done first, so now logic for moving to the trash has moved
        # back to after addition to exclusions and removal from inclusions.
        if not isDeleting and not isOccurrence:
            self.exclusions.add(item)

        if item in self.inclusions:
            self.inclusions.remove(item)

        trash = self.trash
        pim_ns = schema.ns('osaf.pim', self.itsView)

        if not (isDeleting or trash is None or isOccurrence):
            if isinstance(trash, ContentCollection):
                for collection in itertools.chain(trash.trashFor,
                                                  [pim_ns.allCollection]):
                    # allCollection isn't in trash.trashFor, but needs to be
                    # considered
                    if collection is not self and item in collection:
                        # it exists somewhere else, definitely don't add
                        # to trash
                        break
                else:
                    # we couldn't find it anywhere else, so it goes in the trash
                    trash.add(item)

    def __setup__(self):
        # Ensure that the collection is properly set up
        self._updateCollection()

    def _updateCollection(self):
        if not hasattr(self, 'source'):
            # Can't initialize the collection until the source is known
            return

        exclusions = self.exclusionsCollection
        if exclusions is None:
            exclusions = (self, 'collectionExclusions')

        innerSource = (self, 'inclusions')
        if self.source is not None:
            innerSource = Union(self.source, innerSource)

        set = Difference(innerSource, exclusions)
        trash = self.trashCollection
        if trash is not None:
            set = Difference(set, trash)
        setattr(self, self.__collection__, set)

    def withoutTrash(self, copy=True):
        """
        Pull out the non-trash part of AppCollection.

        Smart collections are 'special' - they almost always include
        the trash as a part of their structure on the _right side of
        their Difference set. This means that when they are hooked
        into a larger collection tree, they need to only give out
        the _left side, which has no trash.
        """

        if self.trash is schema.ns('osaf.pim', self.itsView).trashCollection:
            left = getattr(self, self.__collection__)._left
            if copy:
                return left.copy(self.itsUUID)
            return left

        return self
class WrapperCollection(ContentCollection):
    """
    A class for collections wrapping other collections
    """

    __metaclass__ = schema.CollectionClass
    __collection__ = 'set'

    set = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'))

    sources = schema.Sequence(inverse=ContentCollection.sourceFor,
                              doc="the collections being wrapped",
                              initialValue=[])

    # When a source is removed and the resulting set is an EmptySet
    # this collection gets deleted if autoDelete is True.
    autoDelete = schema.One(schema.Boolean, defaultValue=False)

    schema.addClouds(copying=schema.Cloud(byCloud=[sources]))

    def __setup__(self):
        self._sourcesChanged_('add')
        self.watchCollection(self, 'sources', '_sourcesChanged')

    def _sourcesChanged(self, op, item, attribute, sourceId, dirties):

        if op in ('add', 'remove'):
            view = self.itsView
            source = view[sourceId]
            name = source.__collection__

            if op == 'add':
                set = self._sourcesChanged_(op)
                sourceChanged = set.sourceChanged
                actualSource = set.findSource(sourceId)
                for uuid in source.withoutTrash(False).iterkeys():
                    view._notifyChange(sourceChanged, 'add', 'collection',
                                       source, name, False, uuid, dirties,
                                       actualSource)

            elif op == 'remove':
                set = getattr(self, self.__collection__)
                sourceChanged = set.sourceChanged
                actualSource = set.findSource(sourceId)
                for uuid in source.withoutTrash(False).iterkeys():
                    view._notifyChange(sourceChanged, 'remove', 'collection',
                                       source, name, False, uuid, dirties,
                                       actualSource)
                set = self._sourcesChanged_(op)

                if self.autoDelete and type(set) is EmptySet:
                    self.delete()

    def addSource(self, source):

        if source not in self.sources:
            self.sources.append(source)

    def removeSource(self, source):

        if source in self.sources:
            self.sources.remove(source)
Example #7
0
class AbstractCollection(items.ContentItem):
    """
    """
    schema.kindInfo(
        displayName="AbstractCollection"
    )

    indexName   = schema.One(schema.String, initialValue="__adhoc__")
    renameable  = schema.One(schema.Boolean)

    invitees = schema.Sequence(
        "osaf.pim.mail.EmailAddress",
        doc="The people who are being invited to share in this item; filled "
            "in when the user types in the DV's 'invite' box, then cleared on "
            "send (entries copied to the share object).\n\n"
            "Issue: Bad that we have just one of these per item collection, "
            "though an item collection could have multiple shares post-0.5",
        otherName="inviteeOf",  # can't use inverse here while ItemCollection lives!
        initialValue=()
    )   

    # redirections 
    about = schema.Role(redirectTo="displayName")

    schema.addClouds(
        copying = schema.Cloud(
            invitees,
            byCloud=[items.ContentItem.contentsOwner]
        ),
        sharing = schema.Cloud( none = ["displayName"] ),
    )

    rep = schema.One(schema.TypeReference('//Schema/Core/AbstractSet'), initialValue=None)
    subscribers = schema.Sequence(initialValue=[])

    def __init__(self, *args, **kw):
        super(AbstractCollection, self).__init__(*args, **kw)
        self.subscribers = []

    def collectionChanged(self, op, item, name, other, *args):
        if op:
            # use mapChanges to propagate any updates (not add/removes) that
            # happened since the last
            self.itsView.mapChanges(mapChangesCallable, True)
            self.notifySubscribers(op, item, name, other, *args)

    def notifySubscribers(self, op, item, name, other, *args):
        for i in self.subscribers:
            method_name = getattr(i, "collectionEventHandler")
            method = getattr(i, method_name)
            method(op, item, name, other, *args)

    def contentsUpdated(self, item):
        pass

    def __iter__(self):
        for i in self.rep:
            yield i

    def size(self):
        return len(self.rep)

    # index delegates
    def addIndex(self, indexName, indexType, **kwds):
        return self.rep.addIndex(indexName, indexType, **kwds)

    def removeIndex(self, indexName):
        return self.rep.removeIndex(indexName)

    def setIndexDescending(self, indexName, descending=True):
        return self.rep.setDescending(indexName, descending)

    def getByIndex(self, indexName, position):
        return self.rep.getByIndex(indexName, position)

    def removeByIndex(self, indexName, position):
        return self.rep.removeByIndex(indexName, position)

    def insertByIndex(self, indexName, position, item):
        return self.rep.insertByIndex(indexName, position, item)

    def replaceByIndex(self, indexName, position, with):
        return self.rep.replaceByIndex(indexName, position, with)
        
    def placeInIndex(self, item, after, *indexNames):
        return self.rep.placeInIndex(item, after, indexNames)
        
    def iterindexkeys(self, indexName):
        for key in self.rep.iterindexkeys(indexName):
            yield key
        
    def iterindexvalues(self, indexName):
        for value in self.rep.iterindexvalues(indexName):
            yield value
        
    def iterindexitems(self, indexName):
        for pair in self.rep.iterindexitems(indexName):
            yield pair
        
    def getIndexEntryValue(self, indexName, item):
        return self.rep.getIndexEntryValue(indexName, item)
        
    def setIndexEntryValue(self, indexName, item, value):
        return self.rep.setIndexEntryValue(indexName, item, value)
        
    def getIndexPosition(self, indexName, item):
        return self.rep.getIndexPosition(indexName, item)
        
    def firstInIndex(self, indexName):
        return self.rep.firstInIndex(indexName)
        
    def lastInIndex(self, indexName):
        return self.rep.lastInIndex(indexName)
        
    def nextInIndex(self, previous, indexName):
        return self.rep.nextInIndex(previous, indexName)

    def previousInIndex(self, next, indexName):
        return self.rep.previousInIndex(next, indexName)
Example #8
0
class KindParameterizedEvent(BlockEvent):
    kindParameter = schema.One(
        schema.TypeReference('//Schema/Core/Kind'),
        required=True,
    )
    schema.addClouds(copying=schema.Cloud(byRef=[kindParameter]))