dirbrowse (lx_dirbrowse.hpp)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


Contents

DirBrowser Cache

The DirBrowser cache provides a complete directory hierarchy containing all of the base paths and their child dirs/files. For plug-ins this is primarily used to provide access for filtering through custom ILxFilter objects.

ILxDirCacheService

The service provides access to the root entires of the cache (which are all of the base paths from all of the browsers).

(1) SDK: Declarations
 #define LXu_DIRCACHESERVICE             "b61119fa-4cd9-4067-ba48-ba83ad1fa544"
 #define LXa_DIRCACHESERVICE             "dircacheservice"

As with all services, we start with the ScriptQuery method, although it is not implemented.

(2) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 ScriptQuery) (
         LXtObjectID              self,
         void                   **ppvObj);

Entries within a cache are represented by objects with an ILxDirCacheEntry interface. These can represent a file or directory, which can be either "real" (as in, it exists on disk) a synthetic construct. Often these objects also implement an ILxAttributes interface to expose various bits of metadata.

Its worth noting that not all non-synthetic files are in base paths. For example, a synthetic directory may be displaying all clips loaded in the scene, and those clips may not be from the standard content that are part of the default base paths.

The root paths usually match the base paths of all of the DirBrowserBasePaths instances. However, if a base path is the child of another base path, then only the more root path will be in the root of the cache, as the other path will already be in the cache as a child.

These functions allow the cache to be walked. Passing in NULL for the parent will return the root-level paths (ie: the base paths from all the DirBrowserBasePaths).

(3) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 RootCount) (
         LXtObjectID               self,
         int                      *count);
 
         LXxMETHOD(  LxResult,
 RootByIndex) (
         LXtObjectID               self,
         int                       index,
         void                    **ppvObj);

When using the above two methods, it is important to lock the list so that it doesn't change out from under you. This is accomplished with the locking methods. Lock calls are renetrant within a thread, and can be nested.

It is usually wise to make a copy of the root list and then unlock it. This avoids any possible deadlocks that may arise from a lock being held on the root list while one of the dir cache threads attempts to lock it while already having a lock on one of its entries. The deadlock arises from each thread waiting for the other to release its lock so that it can obtain the next lock. This isn't a problem if you always walk up the list (from base to child), but it will be an issue if you walk up the list (say, checking to see if a specific entry is a root entry by locking the entry and then locking the root). As long as the root is locked before any entries are locked in the thread, this issue can be avoided -- or you can just copy the root list.

(4) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 RootLock) (
         LXtObjectID               self);
 
         LXxMETHOD(  LxResult,
 RootUnlock) (
         LXtObjectID               self);

Lookup a cache entry by its path anywhere in the hierarchy. The path is expected to be in local platform format. This is thread-safe, and implicitly locks the list. This means it cannot be called from within the above Count/ByIndex methods or when any other entry is locked (via ILxDirCacheEntry::ClientLock()) in this thread, or the app will deadlock.

(5) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 Lookup) (
         LXtObjectID               self,
         const char               *path,
         void                    **ppvObj);

This aborts any pending async operations given an identifier string. This is tested against that returned by all of the pending async objects' Ident() methods, and all that match are canceled.

(6) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 CachedThumbnailAsyncCancel) (
         LXtObjectID               self,
         const char               *ident);

Manual Order and Grid Position on Drop

It is not uncommon to create a new file by D&D'ing some content into a Preset Browser. If the view supports manual ordering or grid mode, it is often desirable to respect that and place the newly-created file in the appropraite location.

This method handles all of that for you. You just need to provide an LXtObjectID from the drop destination and the path of the file you created, and this will update the manual order and grid position as appropraite. Note that the file's path must match that of the directory provided by the destination object, or this will do nothing.

It is important to note that this is only realy good for updating a single file; calling this multiple times may result in the paths being shuffled around oddly, as you really want to take into account the previosuly positioned file instead of just the information present in the destination object.

(7) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 SetPosOnDrop) (
         LXtObjectID               self,
         const char               *path,
         LXtObjectID               dest);

Notifying the Service of Attribute Changes

If you make changes to a markup attribute, you need to notify the dir cache service through this method so that it can udpate its state and so the attributes can be saved into the files. Simply pass in the ILxDirCacheEntry and the name of the attribute you updated, or NULL if multiple attributes were changed on that entry. The "which" flag indicates if the attribute was changed in the user markup, the shared markup or both.

Note that contrary to most other entry methods, if the entry is a file then it must NOT be locked before calling this method. The method will do its own locking, and attempting to lock it yourself can result in a deadlock. Directories can be locked without issue, as only the directory itself is updated, while for files, the parent directory also needs to be updatred.

(8) SDK: Declarations

(9) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 AttributesChanged) (
         LXtObjectID               self,
         LXtObjectID               dirCacheEntry,
         int                       which,
         const char               *attribute);

Scanning for Changes

The dir cache needs to be told when a file or directory changes. On app activation it will automatically rescan all directories looking for changes, and update as appropriate. If the files or dirs are added or removed while the app is running, this function should be called by the client that did that modification so that the cache will be refreshed.

If the path provided points to a directory, that directory and its children will be scanned for changes. If the path is a file, the directory that contains the file and its children will be scanned. If the path is NULL, the entire cache is rescanned.

Rescanning involves hitting the disk, testing each file and its sidecar files for changes. Scanning the entire standard content directory hierarchy in a development build on a spinning disk hard drive takes about four seconds, which isn't too bad for 4000 or so files and dirs. Targetting a specific directory should be nearly instant from the user's point of view.

(10) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 ScanForChanges) (
         LXtObjectID               self,
         const char               *path);

Nexus 901

While it operates as a low-priority thread, dir cache background isn't always desirable. A preference determines if the cache should always upgrade in the background or only when a client is actively using it.

Clients declare that they're using it by calling this method to increment the use count. This keeps the cahce thread running and makes sure that everything is up to date. If a client attempts to access the cache without increasing the use count, they may have stale cache data or no cache data at all.

(11) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 AddClient) (
         LXtObjectID               self);

When a client no longer needs to access the cache, it should remove itself. This just keeps the cache from doing any further updates, but it will first finish writing any changes to disk as needed.

(12) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 RemoveClient) (
         LXtObjectID               self);

Nexus 10.1

With the introduction of synthetic paths, we need a way to parse those paths. This will automatically handle both file system paths and synthetic paths, with the caveat that the paths must be absolute (ie: a synthetic path starts with "[servername]:") so that it can be properly identified.

(13) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 ParseName) (
         LXtObjectID              self,
         const char              *filename,
         char                    *baseName,
         int                      baseNameLen,
         char                    *path,
         int                      pathLen);

Similarly, this will compose a path, inserting the appropriate seperators for synthatic vs. local paths. The filename buffer will be a combination of the path, a separator, and the basename, although if the basename is absolute it will be the only thing in the filename buffer.

(14) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 PathCompose) (
         LXtObjectID              self,
         char                    *filename,
         int                      filenameLen,
         const char              *baseName,
         const char              *path);

Test to see if a path is a child of another path, returning LXe_TRUE if it is and LXe_FALSE if it's not. This works on both real and synthetic paths.

(15) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 IsChildOfPath) (
         LXtObjectID              self,
         const char              *possibleChild,
         const char              *parentToTestAgainsts,
         int                      orIsSame);

This retuns the local version of a path. Synthetic paths are simply returned as the original path, while non-synethic paths are returned as though ILxFileService::ToLocalAlias() was called on it.

(16) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 ToLocalAlias) (
         LXtObjectID              self,
         char                    *path,
         char                    *buf,
         int                      len);

This returns if two paths are equal. Synthetic paths are considered to be case insensitive.

(17) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 ArePathsEqual) (
         LXtObjectID              self,
         const char              *path1,
         const char              *path2);

This returns true if a path can be renamed with the file.rename and dircache.rename commands.

(18) SDK: ILxDirCacheService interface
         LXxMETHOD(  LxResult,
 CanBeRenamed) (
         LXtObjectID              self,
         const char              *path);

Empty DirCache service user classes.

(19) User Service Class: DirCacheService method

(20) PY: DirCacheService method
 pass

ILxDirCacheEntry

The ILxDirCacheEntry represents a single file or directory. It may be "real" (as in, it exists on disk), or it may be a synthetic object that exists only in memmory. Objects with this interface usually also implement an ILxAttributes interface so that their metadata can be read.

(21) SDK: Declarations
 #define LXu_DIRCACHEENTRY               "1013a289-aa27-416a-b273-666732ce3d88"
 #define LXa_DIRCACHEENTRY               "dircacheentry"

Locks

ILxDirCacheEntry objects are commonly used by threads, and are generally thread-safe and will internally lock to ensure that two threads don't read/write data at the same time.

Locking is done automatically with read/write locks. For the most part you don't have to worry about it, but you do need to be careful in certain circumstances. The most important is tha read locks are reentrant within a thread, but write locks are not, including when you already have a read lock. For example, if you have obtained a DirList without setting asCopy, you will have a read lock on the directory's entry until you release it. This means that attempts to modify parts of the directory (such as changing the manual sort order or grid position of the entries within) will cause a deadlock, as all read locks must be released before the entry can be modified.

There are some additional rules for locking. The primary one is that you should only lock down the parenting chain -- meaning, if you have a lock on an entry, then you may also lock its childrne. However, you should NOT try to lock the parent of an entry that you also have a lock on. Doing so can lead to cases where Thread 1 locks Entry A, which in turn locks its child Entry B, while Thread 2 locks Entry B, which in turn locks its parent Entry A. Since Thread 1 has a lock on A, and 2 on B, the two threads are now deadlocked waiting for the other thread to return its lock. By ensuring that you only lock the from parent to child, you don't have to worry about this happening. (We could also have gone with always locking from child to parent, but parent to child is more common and is thus the convention.) If you do need to lock from child to parent, be sure to unlock the child first to avoid any possible deadlock situations.

Reading State

This returns the type of the file as a bitmask. Files represent an end-point in the hierarchy, while directories contain child entries. References are synthetic entries that point to other files/dirs (synthetic or real), and are commonly used to create things similar to "playlists" -- user-defined in-memory fodlers that contain arbitrary collections of files -- and smart directories whose contents are defined by filters. Note that references never point to other references; attempting to do so weill cause both to point to the actual referenced entry.

The FILE and DIR flags indicate the file type, and may be merged with the SYNTHETIC or REFERENCE flags. If neither FILE or DIR are set, then the entry does not exist. Usually non-existant entries simply aren't in the cache, but they are used for base paths.

(22) SDK: Declarations
 // Masks
 #define LXmDCETYPE_FILEDIR              0x0000000F
 #define LXmDCETYPE_FLAGS                0x000000F0
 #define LXmDCETYPE_SYNTH                0x00000F00
 #define LXmDCETYPE_SPECIAL              0x0000F000
 
 // Bits
 #define LXfDCETYPE_FILE                 0x00000001                              // File
 #define LXfDCETYPE_DIR                  0x00000002                              // Directory
 
 #define LXfDCETYPE_READONLY             0x00000010                              // Read-only, and cannot be deleted, renamed or otherwise modified
 #define LXfDCETYPE_DIR_AS_FILE          0x00000020                              // Directory that is treated as a file.  It will show up as a file in the browser, but exists as a directory for access purposes (ie: it can't be opened for read/write like a file)
 
 #define LXfDCETYPE_SYNTHETIC            0x00000100                              // Synthetic entry that does not exist on disk
 #define LXfDCETYPE_REFERENCE            0x00000200                              // Synthetic entry that references another entry, which may itself be synthetic.  References do not point to other references
 #define LXfDCETYPE_FILTERED             0x00000400                              // A synthetic directory whose contents is defined by its filter
 
 // Macros
 #define LXxDCETYPE_IS_FILE(t)           ((t) & LXfDCETYPE_FILE)                                                                 // File flag is set
 #define LXxDCETYPE_IS_DIR(t)            ((t) & LXfDCETYPE_DIR)                                                                  // Dir flag is set
 
 #define LXfDCETYPE_EXISTS(t)            (LXxDCETYPE_IS_FILE(t) || LXxDCETYPE_IS_DIR(t))
 #define LXxDCETYPE_IS_DIR_AS_FILE(t)    (((t) & LXfDCETYPE_DIR) && ((t) & LXfDCETYPE_DIR_AS_FILE))
 
 #define LXxDCETYPE_AS_FILE(t)           (LXxDCETYPE_IS_FILE(t) || (LXxDCETYPE_IS_DIR(t) && LXxDCETYPE_IS_DIR_AS_FILE(t)))       // Treat the path as a file
 #define LXxDCETYPE_AS_DIR(t)            (LXxDCETYPE_IS_DIR(t)  && !LXxDCETYPE_IS_DIR_AS_FILE(t))                                // Treat the path as a dir
 
 #define LXxDCETYPE_IS_READONLY(t)       ((t) & LXfDCETYPE_READONLY)
 #define LXxDCETYPE_IS_READWRITE(t)      (!((t) & LXfDCETYPE_READONLY))
 
 #define LXxDCETYPE_IS_REAL(t)           (((t) & LXmDCETYPE_SYNTH) == 0)         // AKA "not synthetic" (also not a reference)
 #define LXxDCETYPE_IS_SYNTHETIC(t)       ((t) & LXfDCETYPE_SYNTHETIC)
 #define LXxDCETYPE_IS_REFERENCE(t)       ((t) & LXfDCETYPE_REFERENCE)
 #define LXxDCETYPE_IS_FILTERED(t)       (((t) & LXfDCETYPE_FILTERED) && DBCExTYPE_IS_DIR(t))

(23) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Type) (
         LXtObjectID               self,
         int                      *type);

This macro just returns true if the path string is synthetic by testing the first character, for cases where you don't have the type.

(24) SDK: Declarations
 #define LXxDIRCACHE_IS_SYNTHETIC_PATH(p)        ((((p) != NULL) && ((p)[0] != '[')) ? 0 : 1)

For directory types, this returns the child files and/or directories as a read-only ILxArray object. For conveience, this can return just files (including dirs-as-files), just dirs, or both.

It's important to note that the returned array object should be considered transient, and should be released as soon as possible. The array object maintains a read lock on the entry to ensure that it doesn't change whil you are using it. This means that no part of the entry can be modified until the array is released.

If you need to modify the directory entry while you have the array object, you can avoid the read lock by requesting a copy of the array. This is slower than just getting the array directly, but there is no lock to worry about, and since it's a copy the list won't change out form under you.

(25) SDK: Declarations

(26) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 DirList) (
         LXtObjectID               self,
         int                       listMode,
         void                    **ppvObj,
         int                       asCopy);

There are times when you just need a quick count of the entries or a way to tell if there are any at all. This returns that count.

(27) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  unsigned int,
 DirCount) (
         LXtObjectID               self,
         int                       listMode);

These can be used to get the local format path, name part (the last part of the path, including any extension) and extension, if available.

(28) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Path) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);
 
         LXxMETHOD(  LxResult,
 Name) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);
 
         LXxMETHOD(  LxResult,
 Extension) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

The size of the file in bytes can also be read as a float. This is only supported for files, and will not currently return the size of the contents of a directory hierarchy (it doesn't fail; it just returns 0 instead).

(29) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Size) (
         LXtObjectID               self,
         double                   *size);

The time the file or directory was created or modified on disk (whichever time is more recent) can be read as a string. The format is "YYYY:MM:DD HH:MM:SS", which makes it easy to compare dates with strcmp().

(30) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ModTime) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

This returns the parent of an entry, or fails with NOTFOUND if this is a base entry. Be sure to note the restrictions in the ClientLock() documentation about ensuring that the entry is not locked before attempting to locking the parent to avoid deadlocks.

(31) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Parent) (
         LXtObjectID               self,
         void                    **ppvObj);

If the entry is a reference, this returns the entry it is referencing.

(32) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ReferenceSource) (
         LXtObjectID               self,
         void                    **ppvObj);

These can be used to get an ILxArrayID containing the list of entries referencing this entry as their source. As with the dir/file lists, the array maintains a read lock on the entry until it is released, and thus should be released as soon as possible. If asCopy is true, a copy of the list will be placed in the array, and no lock will be held.

(33) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ReferencedList) (
         LXtObjectID               self,
         void                    **ppvObj,
         int                       asCopy);

This quickly returns a count of referencess to this entry, without the overhead of allocating an array object.

(34) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  unsigned int,
 ReferencedCount) (
         LXtObjectID               self);

This returns LXe_TRUE if the file was recognized by a server, and LXe_FALSE if it was not. Unrecognized files are usually hidden from the browser.

(35) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 WasRecognized) (
         LXtObjectID               self);

These methods return ILxAttributes interfaces assocaited with the entry. This metadata is basic information like image resolution or the number of polygons, and is read only.

(36) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Metadata) (
         LXtObjectID               self,
         void                    **ppvObj);

Markup contains user-defined features like tags and star ratings. These objects are normally read only, but you can request a writable object by setting the isWrite flag to true.

(37) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 UserMarkup) (
         LXtObjectID               self,
         void                    **ppvObj,
         int                       asWritable);
 
         LXxMETHOD(  LxResult,
 SharedMarkup) (
         LXtObjectID               self,
         void                    **ppvObj,
         int                       asWritable);

The writable object is a copy of the original markup to which you can make any changes you like. Once you are finished, you must call one of the following functions to commit the changes. The contents of the object will be copied into the entry, after which your object can be freed. Note that you can also create a new object yourself (meaning, there is no need to use a writable object obtained by calling the above functions), which is most useful when you need to add or remove attributes instead of simply changing their values.

Since this obtains a write lock, care must be taken to ensure that you do not currently have a lcok on the entry (say, by having a non-copied array returned from DirList()) or else the app will deadlock.

(38) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 CommitUserMarkup) (
         LXtObjectID               self,
         LXtObjectID               userMarkup);
 
         LXxMETHOD(  LxResult,
 CommitSharedMarkup) (
         LXtObjectID               self,
         LXtObjectID               sharedMarkup);

These utilities return label, tooltip and description strings. If they aren't available from the metadata, they are generated from the filename.

(39) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Label) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);
 
         LXxMETHOD(  LxResult,
 Desc) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);
 
         LXxMETHOD(  LxResult,
 ToolTip) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

Thumbnails

Get the thumbnail for an entry. You can request any size, but the image you actually get back may be larger or smaller than the one requested. The ideal width and height are returned indirectlry, and represent the full-resolution image.

There are also two special image resolutions available, and are used if either the width or height are set to them:

IDEAL_ONLY
*ppvObj will be NULL, but idealW and idealH will be set to the full image resolution.
FULL
*ppvObj will be set to the full-resolution image.

(40) SDK: Declarations

There is one other special case: If the client returns an idealW/idealH of 0,0, the image is unbounded and any size is valid.

Note that this method does not use the cache, and will always hit the disk, but it is capacble of getting an image up to the ideal size (the cache is limited to 512x512).

Also note that if the image is from a resource, this will fail unless it is called from the main thread, as image resources cannot be processed from any other thread.

(41) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Thumbnail) (
         LXtObjectID               self,
         int                       w,
         int                       h,
         unsigned int             *idealW,
         unsigned int             *idealH,
         void                    **ppvObj);

The cache system does mantain an on-disk cache of thumbnails in a few fiuxed sizes. Furthermore, there is an in-memory cache capacble of storing images of arbitrary sizes. Requesting one of these is much faster than loading the full-size image from the source file with Thumbnail().

If no cached version exists, the 512x512 size is loaded from the source, resized and saved into the various cache images, and then returned in the desired size. This means that the first time a cached thumbnail is requested may take longer than normal, but subsequent requests will be faster, even though it still has to be loaded from disk.

(42) SDK: Declarations

This method will block until it can return an image or fail. Note that images for resrouces can only be read from the main thread, meaning that if the thumbnail isn't already cached this call will simply fail for those cases.

(43) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 CachedThumbnail) (
         LXtObjectID               self,
         int                       size,
         unsigned int             *idealW,
         unsigned int             *idealH,
         void                    **ppvObj);

This related method returns a cached thumbnail scaled to the desired size. If necessary, it will load the closest cached thumbnail from disk and resize it as needed. The maximum size is 512x512. As above, this will block until it can return an image or has failed.

(44) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 CachedThumbnailCustom) (
         LXtObjectID               self,
         int                       w,
         int                       h,
         unsigned int             *idealW,
         unsigned int             *idealH,
         void                    **ppvObj);

This version of the method returns as quickly as possible. If an image is avaialble in the in-memory cache, it returns that image directly; if not, it will be queued and loaded asynchronously and the method will return LXe_NOTREADY. The ILxDirEntryThumbAsyncID object provided will be called from the main thread when the thumbnail is ready. The async handler provided will be owned by this system and released when no longer needed.

If this filename is already queued with htis ident, the existing one will be removed and the new one will be added to the head of the list.

These operations can be canceled by calling ILxDirCacheService::CachedThumbnailAsyncCancel().

(45) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 CachedThumbnailAsync) (
         LXtObjectID               self,
         int                       size,
         unsigned int             *idealW,
         unsigned int             *idealH,
         void                    **ppvObj,
         LXtObjectID               asyncHandler);
 
         LXxMETHOD(  LxResult,
 CachedThumbnailCustomAsync) (
         LXtObjectID               self,
         int                       w,
         int                       h,
         unsigned int             *idealW,
         unsigned int             *idealH,
         void                    **ppvObj,
         LXtObjectID               asyncHandler);

Empty DirCacheEntry Python user class.

(46) User Class: DirCacheEntry method

(47) PY: DirCacheEntry method
 pass

The ILxDirEntryThumbAsync interface is pretty simple. If the image was successfully loaded, the Ready() method is called with the image and ideal w/h. If it fails, the Failed() method is called instead. Both methods include the ILxDirCacheEntry that the method was called on, and are always called from the main thread.

(48) SDK: Declarations
 #define LXu_DIRENTRYTHUMBASYNC          "0a3c97de-37b8-44b2-a52b-b1b91c77e597"
 #define LXa_DIRENTRYTHUMBASYNC          "direntrythumbasync"

(49) SDK: ILxDirEntryThumbAsync interface
         LXxMETHOD(  LxResult,
 Ready) (
         LXtObjectID               self,
         LXtObjectID               dirCacheEntry,
         unsigned int              idealW,
         unsigned int              idealH,
         LXtObjectID               image);
 
         LXxMETHOD(  LxResult,
 Failed) (
         LXtObjectID               self,
         LXtObjectID               dirCacheEntry);

The Ident() method is used to allow the client to cancel any pending async operations. It returns an arbitrary string, and is compared against the one passed into CachedThumbnailAsyncCancel(). All instances with that ident are canceled. If this method returns NOTIMPL, the operation cannot be canceled.

(50) SDK: ILxDirEntryThumbAsync interface
         LXxMETHOD(  LxResult,
 Ident) (
         LXtObjectID               self,
         const char              **ident);

Empty DirEntryThumbAsync Python user class.

(51) User Class: DirEntryThumbAsync method

(52) PY: DirEntryThumbAsync method
 pass

Manual Ordering

A directory maintains a manual sort list of all of the files/dirs contained within. Initially, all entries are alpha-sorted with directories sorted first.

The manual sort position of an entry within a directory can be read with this method. The function takes either the name part of the entry or the full path (the last part of the path is always used), and returns the index of that entry relative to the other entries in the directory.

Note that manual sort order is intended for sorting files relative to each other. It is possible for the order to return empty gaps between two otherwise adjacent entries. The most common cause of this is when a file is both copied to a directory and its order is set. Since the directory hasn't been rescanned for the new file(s) yet, the manual sort list contains paths that don't currently exist in the child file/dir list.

The childPath argument can be a full path or simply the name of the file/dir itself.

(53) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ChildManualOrderLookup) (
         LXtObjectID              self,
         const char              *childPath,
         unsigned int            *pos);

This method allows the sort order of a child entry to be set. The special defines can be used to move the entry to the first or last position in the list. Passing in a position greater than the highest position will move it to the end of the list, and is equivalent to LXiDCE_MANUALORDER_LAST.

Again, the childPath argument can be either the full path or just name part of the file/dir.

(54) SDK: Declarations
 #define LXiDCE_MANUALORDER_FIRST        0xFFFFFFFF
 #define LXiDCE_MANUALORDER_LAST         0xFFFFFFFE

(55) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ChildManualOrderSet) (
         LXtObjectID              self,
         const char              *childPath,
         unsigned int             pos);

Grid Positions

A directory maintains the grid positions of each of the files within when (directories are not supported for grid mode). Grid mode is commonly used in things like color pickers, where a palette of user-defined colors can be re-arranged at will. Entries in the grid need not be contiguous, allowing for gaps between entries. Grid positions are arbitrary, but must be positive numb.

Directories cannot be placed the grid; this is only available for files.

This looks up the grid position for an given child path. Only the filename portion of the path need be provided, but you can pass the whole path if you like.

If no grid positions are assigned when this is called, the files in the dir are implicitly assigned new positions, starting with 0,0, with each row being 10 enries wide.

If new files are added to the directory after the grid has been initialized, they are added to a new row below the last entry This ensures that new files are still in the grid, but aren't inadvertently filling in gaps that were intentionally left by the user.

Note that when doing a lookup on a file's path, none of the children should be locked in the same thread, as it may be necessary to walk the children from this function. If any children are locked in the same thread, this will result in a deadlock.

(56) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ChildGridPositionLookup) (
         LXtObjectID              self,
         const char              *childPath,
         unsigned int            *x,
         unsigned int            *y);

This sets the grid position of an entry. If another entry is already at that position, the two will be swapped. If the new entry didn't have a previous position, it will be moved to after the last entry in the grid.

(57) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 ChildGridPositionSet) (
         LXtObjectID              self,
         const char              *childPath,
         unsigned int             x,
         unsigned int             y);

This returns the rightmost and bottommost coordinates of a region all of the entries within the grid. While the box they define (starting from 0,0) will never be smaller than the region defined by the thumbnails coordinates, it may be larger. This larger space allows for "padding" into which users can D&D files outside of the bounds of the existing entries.

(58) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridExtents) (
         LXtObjectID              self,
         unsigned int            *bottom,
         unsigned int            *right );

This allows the extents to be changed at any time. Attempting to make the extents smaller than the coordinates of the entries (meaning, attempting to make it so that some entries are not within the extents) will result in the extents being resized to fit the thumbnails. Thus, passing in 0,0 will result in the bottom right enclosing all entries.

(59) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridSetExtents) (
         LXtObjectID              self,
         unsigned int             bottom,
         unsigned int             right );

This allows empty rows and columns to be insert into the grid by shifting the coordinates of the entries at a given grid position. right (columns) or down (rows) Passing a count of 0 will result in no change. If doRows is true, "count" rows will be added at the coordinate provided; otherwise, it adds columns.

The x and y coordinates determine which entries are moved. If both x and y are set along with doRows, then "count" empty cells are inserted below the specific cell at (x,y). If instead x was set to LXiDCEGRID_NONE, then "count" row of blank cells would be inserted between all entries at "y" or lower

Inserting always increases the extents of the grid, even if adding empty rows/columns to the far bottom/right. If the x/y are beyond the extents, the extents are padded to that position and then the rows are inserted.

(60) SDK: Declarations
 #define LXiDCEGRID_NONE         0xFFFFFFFF

(61) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridInsert) (
         LXtObjectID              self,
         int                      doRows,
         unsigned int             x,
         unsigned int             y,
         unsigned int             count );

This similarly removes rows or columns from the grid. If force is false, then rows/columns will be removed only if no entries exist in that row/columm. If true, then entries will be shifted over to fill the empty slots, but nothing will happent to entries occupying those slots. Passing in a very larger number for "count" will effectively remove any empty space between entries.

As with inserting rows, setting both x and y to valid coordinates will affect only cells on that row/column, while setting one of them to LXiDCEGRID_NONE will affect all entries with positions equal to or greater than the x or y coordinates provided.

Removing may decrease the extents of the grid, including when removing empty rows/columns from the far bottom/right.

(62) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridRemove) (
         LXtObjectID              self,
         int                      doRows,
         unsigned int             x,
         unsigned int             y,
         unsigned int             count,
         int                      force );

This returns LXe_TRUE if a grid row or column is empty, and LXe_FALSE if not. Setting both x and y to non-LXiDCEGRID_NONE values will test the row or column from that point on.

(63) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridIsEmpty) (
         LXtObjectID              self,
         int                      doRows,
         unsigned int             x,
         unsigned int             y);

This returns LXe_TRUE if a grid cell or column is empty, and LXe_FALSE if not

If it returns false, the ILxDirCachenEntry representing the thumb at that cell can be returned; if you don't need this, ppvObj can be set to NULL. If it is set, the object must be released when no longer needed.

(64) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 GridIsEmptyCell) (
         LXtObjectID               self,
         unsigned int              x,
         unsigned int              y,
         void                    **ppvObj);

Nexus 10.1

This returns the username of the entry. It's primarily intended for synthetic directories, which may have usernames that are different from the internal name. For everything else, this just returns the internal name, which is that component of the path. Files can provide alternate names through the "label" property of their metadata.

(65) SDK: ILxDirCacheEntry interface
         LXxMETHOD(  LxResult,
 Username) (
         LXtObjectID              self,
         char                    *buffer,
         int                      bufLen);

ILxDirCacheFileMetrics

An object with this interface is returned by clients registed with the cache system. Currently this is only used internally, and won't be called form the SDK, but is made public in case future expansion warrants it. See ILxPresetType for information on exposing file state from the Preset Browser system, even if your file is not a preset per say.

ILxDirCacheFileMetrics objects are commonly used in threads, and must be thread safe.

(66) SDK: Declarations
 #define LXu_DIRCACHEFILEMETRICS         "2c00a80c-ac90-401c-8cb8-abfdf3ba26dd"
 #define LXa_DIRCACHEFILEMETRICS         "dircachefilemetrics"

This method returns an object with an ILxAttributes interface containing metadata from the file. This includes properties that are part of the file's definition and won't be changed from the browser itself, such as an image's dimensions and bit depth, or a meshes polygon count. Metadata is considered read only.

(67) SDK: ILxDirCacheFileMetrics interface
         LXxMETHOD(  LxResult,
 Metadata) (
         LXtObjectID               self,
         void                    **ppvObj);

This returns an object with an ILxAttributes interface representing the user-defined markup that is stored in the file itself. This includes properties like tags, star ratings and so on. Such markup is defined by the cache system; the file is just storing it for us.

If this method returns NOTIMPL, it is assumed that markup cannot be stored in the file at all. Returning NOTFOUND indicates that markup is supported, but that the file simply didn't contain any. An OK code indicates that markup was found and the object returned is valid.

(68) SDK: ILxDirCacheFileMetrics interface
         LXxMETHOD(  LxResult,
 Markup) (
         LXtObjectID               self,
         void                    **ppvObj);

This returns some special flags for the file.

(69) SDK: Declarations
 #define LXiDCFM_DYNAMIC_THUMBNAILS      0x00000001

LXiDCE_DYNAMIC_THUMBNAIL
If set, this indicates that the thumbnail images can be quickly dynamically generated and do not need to be stored in the on-disk cache. A prime example of this is the color switches used by color presets.

(70) SDK: ILxDirCacheFileMetrics interface
         LXxMETHOD(  LxResult,
 Flags) (
         LXtObjectID               self,
         int                      *flags);

Empty DirCacheFileMetrics Python user class.

(71) User Class: DirCacheFileMetrics method

(72) PY: DirCacheFileMetrics method
 pass

Synthetic Servers

The core dir cache looks at a subset of the file system defined by base paths. It is also possible for plug-ins to serve content that doesn't exist on disk through synthetic folders.

Each server defines a single synthetic base path derrived from its server name. These are considered root-level paths in the cache, using the volume identifier "[servername]" Paths inside those dirs are accessed in neutral format, prefixed with the "[servername]:" identifier (ie: "[servername]:dir/file.ext"). If there a path component can be represented as a username or an internal name, it will always be encoded as the internal name.

Information is read from the paths representing "files" through preset servers, just like for any other path. Such servers have to be flagged to support synthetic paths. Similarly, drag and drop requires clients to explicitly handle LXsDROPSOURCE_SYNTHPATH, which may include both real and synthetic paths.

Since all cache scanning is done through threads, all methods on the server must be thread safe.

(73) SDK: Declarations
 #define LXu_DIRCACHESYNTHETIC           "d269abf7-7bd3-4982-a6bd-cb46fcebe1e3"
 #define LXa_DIRCACHESYNTHETIC           "dircachesynthetic"

This server tag indicates the backing of the synthetic folder. It directly determiens the scan priority of the server's folders relative to other servers. The current values are:

MEMORY
State that already exists in memory and is very fast to load.
NETWORK_SINGLE
Network access to a server. This is specifically with regards to quickly pulling down a single file that contains all of the information for a directory. As such, the individual files inside the folder will be prioritized as MEMORY.
NETWORK_MULTIPLE
Same, but both files and dirs will be processed after in-memory files, as files are assumed to require extra network calls to get information about them.
FILE_SINGLE
An entired directory populated by a single file. Directories will be processed after NETWORK backings, but their files will be processed with MEMORY priority.
FILE_MULTIPLE
Each file and directory is separately processed by hitting the disk. These are always prioritized last.

If the tag isn't set or has an unknown value, OTHER is assumed.

(74) SDK: Declarations
 #define LXsDCSYNTH_BACKING                      "dirCacheSynthetic.backing"
 
 #define LXsDCSYNTH_BACKING_MEMORY               "memory"
 #define LXsDCSYNTH_BACKING_NETWORK_SINGLE       "network.single"
 #define LXsDCSYNTH_BACKING_NETWORK_MULTIPLE     "network.multiple"
 #define LXsDCSYNTH_BACKING_FILE_SINGLE          "file.single"
 #define LXsDCSYNTH_BACKING_FILE_MULTIPLE        "file.multiple"

The synthetic server returns synthetic entry objects representing directories with an and files with an ILxDirCacheSyntheticEntry interface.

Given a path, this returns a matching interface, or returns LXe_NOTAVAILABLE if the path can't be found. Note that all paths will be absolute and start with "[servername]:", although the root ommits the colon.

(75) SDK: ILxDirCacheSynthetic interface
         LXxMETHOD(  LxResult,
 Lookup) (
         LXtObjectID               self,
         const char               *path,
         void                    **ppvObj);

This returns the root entry, and is identical to calling Lookup() with "[servername]".

(76) SDK: ILxDirCacheSynthetic interface
         LXxMETHOD(  LxResult,
 Root) (
         LXtObjectID               self,
         void                    **ppvObj);

Synthetic Entry

The syntheric entry represents a single directory or file, from the root at "[servername]:" through to to individual files. For directories, it provides access files and dirs within as yet more synthetic entries.

All methods on this interface must be thread safe. The most common way to constuct an entry is to build one when requested such that all of the state it needs is part of the entry object, without any external dependencies. This ensures that if whatever was used to build the entry changes in another thread, the scan thread will still have a valid entry object. In effect, the entry is a snapshot of the dir/file state and is considered disposable.

The first time an directory entry is requested, the server may need to do some expensive setup. Rather than blocking the scan thread, it can return that it has zero children, do the setup separately, and later call ILxDirCacheService::ScanForChanges() to tell the dir cache to update that path.

(77) SDK: Declarations
 #define LXu_DIRCACHESYNTHETICENTRY      "c85d528d-50aa-4974-bbf0-6122b4e00d5b"
 #define LXa_DIRCACHESYNTHETICENTRY      "dircachesyntheticentry"

Returns the path to this entry, including the "[servername]:" portion. Due to the nature of synthetic dirs, the path should be a generic path without any localization (ie: an internal name string).

(78) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 Path) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

These return the username and internal name of the entry itself. The internal name is suitable for building a path For files, the name is execpted to include any extension, if present.

The username is for display purposes, and is only used for directories. Files get their username from the label stored in the metadata.

(79) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 Name) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);
 
         LXxMETHOD(  LxResult,
 DirUsername) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

For files, this returns the extension, if there is any. Files are not required to have extensions, and some synthetic files may have periods that are not mark an extension, so this makes it very clear what the extension is to the caller.

If there is no extension, this can return LXe_NOTFOUND, or simply LXe_NOTIMPL when no extensions are supported. Other codes will be considered actual failures. As usual, SHORTBUF should be returned if the buffer isn't big enough.

(80) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 FileExtension) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

This returns LXe_TRUE if the entry is a file, and LXe_FALSE if it is a directory that can contain other files.

(81) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 IsFile) (
         LXtObjectID               self);

For directories, these methods allow the list of files and directories to be walked.

First, a scan request is made. Often the child list is burned into the synthetic entry on creation, in which case this does nothing and returns immediately. In other cases but in some cases it is desirable to build the child list just before it is walked. If this fails, we consider the directory to be empty, but it can be marked for rescan later ILxDirCacheService::ScanForChanges().

Since these are commonly used from threads, care should be taken to not change this list out from under the caller.

(82) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 DirBuild) (
         LXtObjectID               self);

listMode is a LXvDCELIST_ indicating if files, directories or both should are going to be requested by the upcoming DirCount() and DirByIndex() calls.

The object returned from DirByIndex() should have the ILxDirCacheSyntheticEntry interface.

(83) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  unsigned int,
 DirCount) (
         LXtObjectID               self,
         int                       listMode);
 
         LXxMETHOD( LxResult,
 DirByIndex) (
         LXtObjectID               self,
         int                       listMode,
         unsigned int              index,
         void                    **ppvObj);

This returns the modification time of the entry as a string. The exact nature of this is arbitrary and up to the server, but it should be possible to compare it with strcmp() such that more recent dates are considered larger than less recent dates. This is used to decide if the dir cache needs ot be updated with new state from the synthetic folder.

If this is a directory, then if any of the files inside of it have changed, the directory time should also have changed. If the stirng is empty, it is always considered to have changed.

(84) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  LxResult,
 ModTime) (
         LXtObjectID               self,
         char                     *buffer,
         int                       bufLen);

Get the size of a file in bytes. This is mostly used for searches/filtering, and is expected to return 0 for directories or for files where size doesn't make sense. This is returned as a double to handle very large file sizes beyond what a 32 bit int can hold.

(85) SDK: ILxDirCacheSyntheticEntry interface
         LXxMETHOD(  double,
 Size) (
         LXtObjectID               self);

ILxBasePathAddDest interface

An object with this interface is exposed during drag and drop in the Dir Browser. Its single method can be called by an ILxDrop server to add a new base path to the browser.

(86) SDK: Declarations
 #define LXu_BASEPATHADDDEST             "1710a8c1-a493-4e65-a190-009effc4a134"
 #define LXa_BASEPATHADDDEST             "basepathadddest"

Calling this method adds a new base path to the associated browser.

(87) SDK: ILxBasePathAddDest interface
         LXxMETHOD(  LxResult,
 AddBasePath) (
         LXtObjectID               self,
         const char               *path);

(88) User Class: BasePathAddDest method

(89) PY: BasePathAddDest method
 pass

ILxDirCacheManualOrderDest interface

The dir cache also manages the manual sort order of files/dirs within a directory. This is exposed through drag and drop through an ILxDirCacheManualOrderDest interface on the destination object.

(90) SDK: Declarations
 #define LXu_DIRCACHEMANUALORDERDEST     "fb69c873-9f95-4ba1-ba1f-1235307e3c88"
 #define LXa_DIRCACHEMANUALORDERDEST     "dircachemanualorderdest"

This method returns the names of the two files/dirs that the drop point is between. It will return NULL for a name if the drop point is at the beginning/end of the list. Clients can call ILxDirCacheEntry::ManualChildOrderSet() on the parent object (returned as ppvObj) to set the position of a path relative to another path. If the asPaths is true, then the strings returned represent the full path instead of just the name.

(91) SDK: ILxDirCacheManualOrderDest interface
         LXxMETHOD(  LxResult,
 BetweenPaths) (
         LXtObjectID               self,
         void                    **ppvObj,
         const char              **nameBefore,
         const char              **nameAfter,
         int                       asPaths);

(92) User Class: DirCacheManualOrderDest method

Empty DirCacheManualOrderDest Python user class.

(93) PY: DirCacheManualOrderDest method
 pass

ILxDirCacheGridPosDest interface

The dir cache also manages the grid positions of files within a directory (directories themseves cannot be placed on the grid, and are not supported). This is exposed through drag and drop through an ILxDirCacheGridPosDest interface on the destination object.

(94) SDK: Declarations
 #define LXu_DIRCACHEGRIDPOSDEST         "09a9db0d-131f-40ed-910a-8916a636dc36"
 #define LXa_DIRCACHEGRIDPOSDEST         "dircachegridposdest"

This method returns the ILxDirCacheEntry containing the drop point, as well as the x and y coordinates of the drop point. The coordinates represent an empty cell or a cell already in use by a file. Clients can call ILxDirCacheEntry::ChildGridPositionSet() on the directory (returned as ppvObj) to set new coordiantes for a file within.

(95) SDK: ILxDirCacheGridPosDest interface
         LXxMETHOD(  LxResult,
 GridPos) (
         LXtObjectID               self,
         void                    **ppvObj,
         unsigned int             *x,
         unsigned int             *y);

(96) User Class: DirCacheGridPosDest method

Empty DirCacheGridPosDest Python user class.

(97) PY: DirCacheGridPosDest method
 pass

ILxFileSysDest

The FilePath destination simply provides a path to a file or directory. This is often used to create new files or move existing files at the time of the drop.

(98) SDK: Declarations
 #define LXu_FILESYSDEST         "79d4f661-3249-4455-bfb9-486120c18f24"
 #define LXa_FILESYSDEST         "fileSystemDestination"

This returns the destination's path. The server can use this to create new files, move existing files to the path, or overwrite an existing file with a new one.

(99) SDK: ILxFileSysDest interface
         LXxMETHOD( LxResult,
 Path) (
         LXtObjectID               self,
         const char              **path);

This utility returns LXe_TRUE if the path is a file, and LXe_FALSE if it is a directory.

(100) SDK: ILxFileSysDest interface
         LXxMETHOD( LxResult,
 IsDir) (
         LXtObjectID               self);

This is similar to Path(). The difference is that the path returned by Path() may point to a file or directory. AsDir() will return Path() if IsDir() would return false, but if not it will instead return the parent of Path(). This is useful if the intention of the drop is to create a new file in the same directory as a file path returned by Path().

(101) SDK: ILxFileSysDest interface
         LXxMETHOD( LxResult,
 AsDir) (
         LXtObjectID               self,
         const char              **dir);

(102) User Class: FileSysDest method

Empty FileSysDest Python user class.

(103) PY: FileSysDest method
 pass