ITEMIDLIST

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

ITEMIDLIST

Louis Sumberg-2
Andy/Blair,

Please include DolphinHarbor's ITEMIDLIST in D6.  Blair agreed to this
awhile ago.  Attached is a package of the class from the latest version
on DolphinHarbor along with some changes that eliminate 3 warnings and 1
error message when installing in D6.

In making the changes I noticed the following:

- The Code Mentor will display "All checks passed" on a method that has
a temporary that's assigned but never used.  BTW, that's a nice touch,
having the Transcript display warnings & errors, with the specific
methods listed, when installing a package - it makes it easy to track
down the specific methods.

- When the Code Mentor displays "Refers to class name instead of 'self
class'" in the bottom left pane, it does not list the "offending"
method(s) in the bottom right pane.

-- Louis

"Filed out from Dolphin Smalltalk X6"!

OLEStructure subclass: #ITEMIDLIST
        instanceVariableNames: 'back'
        classVariableNames: ''
        poolDictionaries: ''
        classInstanceVariableNames: ''!
ITEMIDLIST guid: (GUID fromString: '{928070C1-45D5-4739-B2B1-01E8C27361DC}')!
ITEMIDLIST comment: 'ITEMIDLIST is an <ExternalStructure> that wraps the Windows ITEMIDLIST struct.  ITEMIDLIST serves the same purpose as a file system path, i.e., to identify objects (be they files, folders, virtual or part of the filesystem) in the Windows Shell namespace.  An ITEMIDLIST contains one or more item IDs (a Windows SHITEMID structure), terminated by a two-byte null.

There are two types of ITEMIDLIST -- full and relative.  A full (or absolute) ITEMIDLIST contains a packed list of item ids that together uniquely identifies a shell object relative to the desktop.  A relative ITEMIDLIST contains a packed list of item ids that together uniquely identifies a shell object relative to its parent folder.  In most cases, a relative ITEMIDLIST will contain only one item id.  However, multi-level relative ITEMIDLISTs are also used.  See individual method comments for which type of ITEMIDLIST is expected.

For the Shell API, namespace objects are usually identified by a pointer to their ITEMIDLIST structure, also known as a PIDL.  For this reason, most methods that create an ITEMIDLIST actually create a pointer that is passed to Windows which allocates memory and fills the structure.  The pointer is a WindowsShellMemory instance which is finalizable and so will free the memory it points to when it is garbage collected.

Instance Variables:
        back <ITEMIDLIST> Used to prevent an ITEMIDLIST being garbage collected when a new ITEMIDLIST has been constructed on the same memory.'!
!ITEMIDLIST categoriesForClass!Unclassified! !
!ITEMIDLIST methodsFor!

append: anITEMIDLIST
        "Answer a copy of the receiver with anITEMIDLIST appended"

        | mySize totalSize copy |
        totalSize := (mySize := self getSize) + anITEMIDLIST getSize + 2.
        copy := self class new: totalSize.
        self
                replaceBytesOf: copy bytes
                from: 1
                to: mySize
                startingAt: 1.
        anITEMIDLIST
                replaceBytesOf: copy bytes
                from: mySize + 1
                to: totalSize
                startingAt: 1.
        ^copy!

back
        ^back!

back: anITEMIDLIST
        "Keep a back pointer to stop anITEMIDLIST being garbage collected while the receiver is still using its external memory"

        back := anITEMIDLIST!

bufferClass
        ^WindowsShellMemory!

cb
        "Answer the receiver's mkid.cb field as a Smalltalk object.
        This avoids creating the SHITEMID structure and directly accesses the mkid.cb field "

        ^bytes swordAtOffset: 0!

cb: anObject
        "Set the receiver's mkid.cb field to the value of anObject."

        bytes swordAtOffset: 0 put: anObject!

getSize
        "Answer the number of bytes that the receiver uses.  Calculate the size by walking the id list
        and keeping track of the size of each SHITEMID  structure. "

        | sum pidl |
        sum := 0.
        pidl := self.
        [pidl notNil] whileTrue:
                        [sum := sum + pidl cb.
                        pidl := pidl idNext].
        ^sum!

idCopy
        "Answer an <ITEMIDLIST> that is a copy of the receiver."

        | copy totalSize |
        totalSize := self getSize + 2.
        copy := self class new: totalSize.
        self
                replaceBytesOf: copy bytes
                from: 1
                to: totalSize
                startingAt: 1.
        ^copy!

idLast
        "Answer a single <ITEMIDLIST> that contains the last item id of the receiver."

        | pidl pidlNext |
        pidl := self.
        pidlNext := self.
        [pidlNext notNil] whileTrue:
                        [pidl := pidlNext.
                        pidlNext := pidl idNext].
        ^pidl!

idNext
        "Answer a single <ITEMIDLIST> that contains the next item id of the receiver, or nil if there are no more elements."

        | cb instance |
        (cb := self cb) = 0 ifTrue: [^nil].
        "// Add cb to pidl (casting to increment by bytes). pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb); "
        (instance := self class fromAddress: bytes yourAddress + cb) back: self.
        ^instance cb = 0 ifTrue: [nil] ifFalse: [instance]!

idParent
        "Answer a full <ITEMIDLIST> that is the receiver's parent."

        | copy pidl pidlNext |
        copy := self idCopy.
        copy cb = 0
                ifFalse:
                        [pidlNext := copy.
                        [pidlNext notNil] whileTrue:
                                        [pidl := pidlNext.
                                        pidlNext := pidl idNext].
                        pidl cb: 0].
        ^copy!

isDesktopID
        "Answer whether the receiver represents the Desktop item id list."

        ^self cb = 0!

isSingle
        "Answer whether the receiver is a single item id.  NB: The full id of a desktop child will be single."

        ^self idNext isNil!

isValid
        ^bytes notNil!

mkid
        "Answer the receiver's mkid field as a Smalltalk object."

        ^SHITEMID fromAddress: (bytes yourAddress)!

mkid: anObject
        "Set the receiver's mkid field to the value of anObject."

        anObject replaceBytesOf: bytes from: 1 to: 4 startingAt: 1!

onStartup
        "Instead of doing this, should we be freeing the Shell memory on shut down?"

        bytes beUnfinalizable.
        bytes := nil!

pointerClass
        "Private - Answer the default class to use as a pointer"

        ^WindowsShellMemory! !
!ITEMIDLIST categoriesFor: #append:!operations!public! !
!ITEMIDLIST categoriesFor: #back!accessing!private! !
!ITEMIDLIST categoriesFor: #back:!accessing!private! !
!ITEMIDLIST categoriesFor: #bufferClass!constants!private! !
!ITEMIDLIST categoriesFor: #cb!accessing!public! !
!ITEMIDLIST categoriesFor: #cb:!accessing!public! !
!ITEMIDLIST categoriesFor: #getSize!operations!public! !
!ITEMIDLIST categoriesFor: #idCopy!operations!public! !
!ITEMIDLIST categoriesFor: #idLast!operations!public! !
!ITEMIDLIST categoriesFor: #idNext!operations!public! !
!ITEMIDLIST categoriesFor: #idParent!operations!public! !
!ITEMIDLIST categoriesFor: #isDesktopID!public!testing! !
!ITEMIDLIST categoriesFor: #isSingle!public!testing! !
!ITEMIDLIST categoriesFor: #isValid!public!testing! !
!ITEMIDLIST categoriesFor: #mkid!**compiled accessors**!public! !
!ITEMIDLIST categoriesFor: #mkid:!**compiled accessors**!public! !
!ITEMIDLIST categoriesFor: #onStartup!event handling!public! !
!ITEMIDLIST categoriesFor: #pointerClass!constants!private! !

!ITEMIDLIST class methodsFor!

defineFields
        "Define the fields of the ITEMIDLIST structure.
                ITEMIDLIST compileDefinition
       
                typedef struct _ITEMIDLIST {
                        SHITEMID mkid;
                } ITEMIDLIST;"

        "self
                defineField: #mkid type: (StructureField type: SHITEMID) offset: 0."

        self byteSize: 4!

exampleCopying
        | path iDesktop id1 id2 |
        path := 'c:\program files'.
        iDesktop := ShellLibrary default getDesktopFolder.
        id1 := iDesktop idlFromPath: path.
        id1 getSize.
        id2 := id1 idCopy.
        id2 getSize!

exampleWalkID: anITEMIDLIST
        "Step through each item id (SHITEMID) in anITEMIDLIST.  Answer a collection of Associations,
        each consisting of an item id and its length.
                self exampleWalkID: (ShellLibrary default getDesktopFolder idlFromPath: 'c:\program files') "

        | idFull coll |
        idFull := anITEMIDLIST idCopy.
        coll := OrderedCollection new.
        [idFull notNil] whileTrue:
                        [coll add: idFull -> idFull getSize.
                        idFull := idFull idNext].
        ^coll!

exampleWalking
        | path iDesktop id1 id2 |
        path := 'c:\program files'.
        iDesktop := ShellLibrary default getDesktopFolder.
        id1 := iDesktop idlFromPath: path.
        id1 getSize.
        id2 := id1 idNext.
        id2 getSize!

exampleWalkPath: aString
        "Create an instance of the receiver based on the full pathname, aString, and step through each item id.
        Answer a collection of Associations, each consisting of an item id and its length.
                self exampleWalkPath: 'c:\program files' "

        ^self exampleWalkID: (ShellLibrary default getDesktopFolder idlFromPath: aString)!

initialize
        "Private - Initialize the receiver.
        Register with session events to clean up sub-instances on startup.
       
                ITEMIDLIST initialize
        "

        SessionManager current
                when: #sessionStarted
                send: #onStartup
                to: self!

new
        "Use #newPointer or #newBuffer to create an instance of the receiver."
        ^self shouldNotImplement

" ^self newPointer"!

onStartup
        "Private - Ensure all the receiver's subinstances are in their clean state on startup
        rather than attempting to use an old safe array hanging around from the sesssion
        when the image was saved."

        self primAllSubinstances do: [:i | i onStartup].
!

pointerClass
        "Private - Answer the default class to use as a pointer"

        ^WindowsShellMemory!

publishedAspectsOfInstances
        "Answer a <LookupTable> of the <Aspect>s published by instances of the receiver."

        ^(super publishedAspectsOfInstances)
                add: (Aspect integer: #getSize);
                add: (Aspect boolean: #isSingle);
                yourself!

uninitialize
        SessionManager current removeEventsTriggeredFor: self! !
!ITEMIDLIST class categoriesFor: #defineFields!initializing!public! !
!ITEMIDLIST class categoriesFor: #exampleCopying!examples!public! !
!ITEMIDLIST class categoriesFor: #exampleWalkID:!examples!public! !
!ITEMIDLIST class categoriesFor: #exampleWalking!examples!public! !
!ITEMIDLIST class categoriesFor: #exampleWalkPath:!examples!public! !
!ITEMIDLIST class categoriesFor: #initialize!development!initializing!private! !
!ITEMIDLIST class categoriesFor: #new!instance creation!public! !
!ITEMIDLIST class categoriesFor: #onStartup!event handling!private! !
!ITEMIDLIST class categoriesFor: #pointerClass!constants!private! !
!ITEMIDLIST class categoriesFor: #publishedAspectsOfInstances!development!must strip!public! !
!ITEMIDLIST class categoriesFor: #uninitialize!class hierarchy-removing!private! !