Working with the Shell Listing
The Shell Listing represents the list of files, objects, views, groupings, or other content shown within the current object listing. Changes in the listing view affect various parts of the client. For example: when objects are selected or deselected the Preview and Metadata tabs display information about the selected object(s).
The ActiveListing is a property of IShellFrame and there are two main events, which are fired when it changes:
MFiles.Event.NewShellListingis fired when a new listing is created, for example when user navigates to another view.MFiles.Event.SelectionChangedis fired when user makes a new selection in the active view, for example by clicking the object with mouse.
All properties and methods of the listing are described in IShellListing. Properties and methods of the selected items are described in IShellItems.
The ActiveListing has two properties which enumerate the items inside the Listing
Itemswhich is instance of IShellItems holds all items in the listingCurrentSelectionwhich is instance of IShellItems holds the user selected items
Observing the changes to Current Selection
Current selection represents the user selected items from the Listing View.
To observe the selection changes of the active selection you can listen to MFiles.Event.SelectionChanged event.
// Used for unregistering previous event handler.
const state = {
listing: null,
selectionChangedEventId: 0
}
// Observe changes to the active view.
shellListing.Events.Register(
MFiles.Event.NewShellListing,
async (listing) => {
// Before registering a new handler, unregister the previous event handler.
if( state.selectionChangedEventId ) {
state.listing.Events.Unregister( currentSelection.selectionChangedEventId );
state.selectionChangedEventId = 0;
}
// Save the old listing item so we can unregister the event handler.
state.listing = listing;
// Save the event handler id so we can unregister if the view changes.
state.selectionChangedEventId = await listing.Events.Register(
MFiles.Event.SelectionChanged,
(currentSelection) => {
// currentSelection is IShellItems instance holding the current selection.
// Log number of selected items.
console.log( currentSelection.Count );
// Get the object versions and log the titles of the files.
const list = currentSelection.ObjectVersions || [];
list.forEach( item => {
console.log( item.object_version?.title );
})
}
)
}
);
Add New File to a Multifile Document
Here is an example of how to add a new file into a Multifile Document (MFD). In this example, we've
registered an event listener for MFiles.Event.NewShellListing. We've also registered a listener to the listing's
MFiles.Event.SelectionChanged event, like in the previous example. The following code snippet resides inside the
MFiles.Event.SelectionChanged handler.
// currentSelection refers to the current selection.
// We assume that only a single object is selected which is a multifile document. We
// will add a file to this object.
// Get objectVersionEx of the object that we want to add a file to.
const myMultiFileDocument = currentSelection.ObjectVersions[0];
// After this call, the multifile document contains a new file.
await listing.AddObjectFile(
{
object_info: myMultiFileDocument // Multifile Document's ObjectVersionEx to which we add a new file to.
},
null,
dataURL, // String containing data URL (base64 + mime) representation of a file that we want to add.
filename // Filename to use. For example, "notes.txt"
);
Replacing a File
Here is an comprehensive example of how to replace a file. In this example when two objects are selected, the file in object 1 is replaced by a downloaded copy of file from object 2.
// Used for unregistering previous event handler. Also store information if
// replacement is already done.
const state = {
listing: null,
selectionChangedEventId: 0,
isReplaced: false
};
// Register to be notified when the ShellFrame is started.
shellFrame.Events.Register(
MFiles.Event.NewShellListing,
async ( listing ) => {
// Before registering a new handler, unregister the previous event handler.
if( state.selectionChangedEventId ) {
state.listing.Events.Unregister( state.selectionChangedEventId );
state.selectionChangedEventId = 0;
}
// Save the event handler id so that we can unregister if the view changes.
state.selectionChangedEventId = listing.Events.Register(
MFiles.Event.SelectionChanged,
async (selectedItems) => {
// Get the selected object versions.
const selectedObjectVersions = selectedItems.ObjectVersions || [];
// Ensure we have two selected objects.
if( selectedObjectVersions.length == 2 && !state.isReplaced ) {
// Two object versions.
const objectVersionEx1 = selectedObjectVersions[0];
const objectVersionEx2 = selectedObjectVersions[1];
const objVer2 = {
obj_id: objectVersionEx2.object_info.obj_id,
version: objectVersionEx2.version_info.version
};
// Get two files, their relevant information and download the second file.
const file1 = objectVersionEx1.version_info.files[0];
const file1Id = file1.file_ver.file_id.internal_id;
const file2 = objectVersionEx2.version_info.files[0];
const file2Name = file2.title + '.' + file2.extension;
const file2DataURL = await MFiles.DownloadFileAsDataUri( objVer2, file2.file_ver );
// Generally, data URLs were previously known as data URIs, but the data URI
// naming was retired by the WHATWG. Hence the naming inconsistency.
// Replace the files in the object version.
// This will replace the file in object 1 with the file from object 2.
// Prematurely mark the replacement done, otherwise we would get into an infinite
// loop here because the replacement operation itself triggers a selection changed
// event due to the change to the object which is selected.
state.isReplaced = true;
await listing.ReplaceFile(
file1Id, // File ID of the file that we will replace.
{
object_info: objectVersionEx1 // ObjectVersionEx of the object that contains the file1.
},
null, // Not used parameter in this example.
file2DataURL, // The content of file 2, which will replace the content of file 1.
file2Name // Name of file 2 with extension.
);
}
}
)
}
);
Modifying Selection
Note that these operations will trigger new selection changed events.
Selecting Next Object
When we have a selection we can select the next object.
listing.SelectNextObject();
Selecting Previous Object
The previous object can also be selected.
listing.SelectPrevObject();
Selecting the first object in the listing
In this example we're selecting the first object in the listing. However, the same logic can be applied to select a certain object.
// Here, 'listing' refers to the current ShellListing.
const objVer = {
obj_id: listing.Items.ObjectVersions[0].object_info.obj_id,
version: listing.Items.ObjectVersions[0].version_info.version
};
listing.SelectObjectVersion(objVer);