Table of Contents

Install Manager Config (to DAZ Studio)

Summary

Below is an example demonstrating how one might go about parsing information from an .ini file, converting that information into a JSON object, providing a dialog that allows an end user to select options for importing the data, and then modify the application settings to incorporate the data.

API Areas of Interest

Example

InstallManagerConfig_to_DAZStudio.dsa
// DAZ Studio version 4.7.0.12 filetype DAZ Script
 
// Define an anonymous function;
// serves as our main loop,
// limits the scope of variables
(function(){
 
	var s_sToolName = "Install Manager Configuration";
	var s_sScriptPath = getScriptFileName();
	var s_oScriptFile = new DzFileInfo( s_sScriptPath );
 
	// Install Manager configuration file keywords
	var s_sAccount = "Account";
	var s_sAccountTitle = "AccountTitle";
	var s_sAllowDesktopShortcuts = "AllowDesktopShortcuts";
	var s_sAllowStartMenuShortcuts = "AllowStartMenuShortcuts";
	var s_sAppBitArch = "AppBitArch";
	var s_sApplicationPaths = "ApplicationPaths";
	var s_sAppName = "AppName";
	var s_sAppPath = "AppPath";
	var s_sAppVersion = "AppVersion";
	var s_sAutoDelete = "AutoDelete";
	var s_sAutoBackup = "AutoBackup";
	var s_sAutoInstall = "AutoInstall";
	var s_sAutoShowInstallModule = "AutoShowInstallModule";
	var s_sBackupPath = "BackupPath";
	var s_sCurInstallPath = "CurInstallPath";
	var s_sCurrentTabIdx = "CurrentTabIdx";
	var s_sCustomUserData = "CustomUserData";
	var s_sDownloadDetailsToggled = "DownloadDetailsToggled";
	var s_sDownloadDisplayHidden = "DownloadDisplayHidden";
	var s_sDownloadPath = "DownloadPath";
	var s_sDownloadSortIdx = "DownloadSortIdx";
	var s_sGeneral = "General";
	var s_sHideCurrentNotificationId = "HideCurrentNotificationId";
	var s_sInstallDetailsToggled = "InstallDetailsToggled";
	var s_sInstallDisplayHidden = "InstallDisplayHidden";
	var s_sInstalledDetailsToggled = "InstalledDetailsToggled";
	var s_sInstallSortIdx = "InstallSortIdx";
	var s_sInstallPath = "InstallPath";
	var s_sInstallPaths = "InstallPaths";
	var s_sInstallPathTitle = "InstallPathTitle";
	var s_sInstalledSortIdx = "InstalledSortIdx";
	var s_sMarkInstalledContentAsNew = "MarkInstalledContentAsNew";
	var s_sMaxConnections = "MaxConnections";
	var s_sOverrideColorScheme = "OverrideColorScheme";
	var s_sOverrideManifestDir = "OverrideManifestDir";
	var s_sOverrideThumbnailDir = "OverrideThumbnailDir";
	var s_sOverrideThumbnailMaxHeight = "OverrideThumbnailMaxHeight";
	var s_sRememberPassword = "RememberPassword";
	var s_sServerSideThumbnailResize = "ServerSideThumbnailResize";
	var s_sShowDateColumn = "ShowDateColumn";
	var s_sShowItemThumbnails = "ShowItemThumbnails";
	var s_sShowLaunchOpen = "ShowLaunchOpen";
	var s_sShowMetadataColumn = "ShowMetadataColumn";
	var s_sShowReadMeColumn = "ShowReadMeColumn";
	var s_sShowProductInfo = "ShowProductInfo";
	var s_sShowSizeColumn = "ShowSizeColumn";
	var s_sShowTips = "ShowTips";
	var s_sSize = "size";
	var s_sSoftware32Path = "Software32Path";
	var s_sSoftware64Path = "Software64Path";
	var s_sTagID = "TagID";
	var s_sTags = "Tags";
	var s_sTagValue = "TagValue";
	var s_sUpdatesToPrevious = "UpdatesToPrevious";
 
	// Local keywords
	var s_sItems = "items";
 
	// Keyword collections
	var s_aMultiPartSectionNames = [ s_sInstallPaths, s_sApplicationPaths, s_sTags ];
	var s_aValidSectionNames = [].concat( s_sGeneral, s_aMultiPartSectionNames, s_sCustomUserData );
 
	var s_aGeneralKeyNames = [ 
			s_sAccount, s_sAccountTitle, s_sAllowDesktopShortcuts, s_sAllowStartMenuShortcuts,
			s_sAutoDelete, s_sAutoInstall, s_sAutoShowInstallModule, s_sCurInstallPath, s_sCurrentTabIdx,
			s_sDownloadDetailsToggled, s_sDownloadDisplayHidden, s_sDownloadPath, s_sDownloadSortIdx,
			s_sHideCurrentNotificationId, s_sInstallDetailsToggled, s_sInstallDisplayHidden, s_sInstalledDetailsToggled,
			s_sInstallSortIdx, s_sInstalledSortIdx, s_sMarkInstalledContentAsNew, s_sMaxConnections, s_sOverrideManifestDir,
			s_sOverrideThumbnailDir, s_sOverrideThumbnailMaxHeight, s_sRememberPassword,
			s_sShowDateColumn, s_sShowItemThumbnails, s_sShowLaunchOpen, s_sShowMetadataColumn, s_sShowReadMeColumn,
			s_sShowProductInfo, s_sShowSizeColumn, s_sShowTips, s_sSoftware32Path, s_sSoftware64Path, s_sUpdatesToPrevious
		];
	var s_aExperimentalKeyNames = [ s_sAutoBackup, s_sBackupPath, s_sOverrideColorScheme, s_sServerSideThumbnailResize ];
	var s_aInstallPathsKeyNames = [ s_sInstallPathTitle, s_sInstallPath ];
	var s_aApplicationPathsKeyNames = [ s_sAppName, s_sAppVersion, s_sAppBitArch, s_sAppPath ];
	var s_aTagsKeyNames = [ s_sTagID, s_sTagValue ];
 
	var s_aMultiPartKeyNames = [].concat( s_sSize, s_aInstallPathsKeyNames, s_aApplicationPathsKeyNames, s_aTagsKeyNames );
 
	var s_aValidKeyNames = [].concat( s_aGeneralKeyNames, s_aExperimentalKeyNames, s_aMultiPartKeyNames );
 
	// Common strings
	var s_sNativeFormats = text( "DAZ Studio Formats" );
	var s_sPoserFormats = text( "Poser Formats" );
	var s_sOtherFormats = text( "Other Import Formats" );
 
	// Helpers
	var s_oStringHelper = new DzStringHelper();
	var s_oArrayHelper = new DzArrayHelper();
 
	// Listview created in one function; referenced in another
	var s_wLocalView = undefined;
 
	/*********************************************************************/
	// String : A function for retrieving a translation if one exists
	function text( sText )
	{
		// If the version of the application supports qsTr()
		if( typeof( qsTr ) != "undefined" ){
			// Return the translated (if any) text
			return qsTr( sText );
		}
 
		// Return the original text
		return sText;
	};
 
	/*********************************************************************/
	// void : Checks unique mapped items, unchecks non-unique, non-mapped items
	function setUniqueMappedItems( wListView )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Declare working variables
		var wListViewItem;
		var sType, sLabel, sPath, sLast;
		var aPaths, aValue;
		var bMapped;
		var nPaths;
 
		// Get all of the items from the listview
		var aListItems = wListView.getItems( DzListView.All );
		// Iterate over the items
		for( var i = 0; i < aListItems.length; i += 1 ){
			// Get the 'current' item
			wListViewItem = aListItems[ i ];
			// Get the label of the item
			sLabel = wListViewItem.text( 0 );
			// If the item is a root
			if( wListViewItem.depth() == 0 ){
				// Set the type to the label of the item
				sType = sLabel;
 
				// Initialize unique path variables
				aPaths = [];
				nPaths = aPaths.length;
 
				// Uncheck it; if it's a controller it'll get checked
				// according to the checked state of it's children
				wListViewItem.on = false;
 
				// Next!!
				continue;
			}
 
			// Get the path of the item
			sPath = wListViewItem.text( 2 );
 
			// Based on the type, check whether the path is mapped
			switch( sType ){
				case s_sNativeFormats:
					bMapped = oContentMgr.contentDirectoryIsMapped( sPath );
					break;
				case s_sPoserFormats:
					bMapped = oContentMgr.poserDirectoryIsMapped( sPath );
					break;
				case s_sOtherFormats:
					bMapped = oContentMgr.importDirectoryIsMapped( sPath );
					break;
				default:
					bMapped = false;
					return;
			}
 
			// If the path is mapped
			if( bMapped ){
				// Split the path into parts
				aValue = sPath.split( "/" );
				// Get the last part; lowercased for case-insensitive compares
				sLast = aValue[ aValue.length - 1 ].toLowerCase();
 
				// Lowercase the label; for case-insensitive compares
				sLabel = sLabel.toLowerCase();
 
				// If the end of the path does not match the label
				if( sLast != sLabel ){
					// It [tenitively] does not match the pattern used by the application
					bMapped = false;
 
					// If this is a special case
					if( sLast == "content" ){
						// Remove the last part
						aValue.pop();
						// Get the new last part; lowercase for case-insensitive compares
						sLast = aValue[ aValue.length - 1 ].toLowerCase();
						// If the end of the special case matches the label
						if( sLast == sLabel ){
							// It matches the pattern the application uses
							bMapped = true;
						}
					}
				}
 
				// Append the path to our list, if it is not already there
				aPaths = s_oArrayHelper.addToArray( aPaths, sPath );
				// If the path did not already exist in the list;
				// the length of the array has grown
				if( aPaths.length > nPaths ){
					// Check the item; i.e. it's unique
					wListViewItem.on = true;
					// Update our count
					nPaths = aPaths.length;
				// If the path already existed
				} else {
					// Uncheck the item; i.e. it is not unique
					wListViewItem.on = false;
				}
			// If the path is not mapped
			} else {
				// Uncheck the item
				wListViewItem.on = false;
			}
		}
	};
 
	/*********************************************************************/
	// void : Checks uniquely mapped directory items, unchecks non-unique items
	function setUniqueMappedDirs()
	{
		// If our listview is not defined
		if( typeof( s_wLocalView ) == undefined ){
			// We are done...
			return;
		}
 
		// Check the items that are uniquely mapped
		setUniqueMappedItems( s_wLocalView );
	};
 
	/*********************************************************************/
	// void : Adds the mapped directories known by Install Manager
	function addUniqueRemoteMappedPaths( wListView, aPathItems )
	{
		// If we do not have paths
		if( aPathItems.length < 1 ){
			// We are done
			return;
		}
 
		// Declare working variables
		var wPathItem, wLastItem;
		var sPath;
		var oPathItem;
		var nChildIdx, nPathIdx;
		var aPaths;
 
		// Create the root item
		var wRootItem = wListView.firstChild();
		// Iterate until we no longer have a root item
		while( wRootItem ){
			// Adding a new item prepends it to te list, so
			// we need to get the last item before we start
			// adding new items so that we can move new
			// items to the end of the list
 
			// Get the first child
			wLastItem = wRootItem.firstChild();
			// Set the child index based on whether we have a child
			nChildIdx = (wLastItem ? 1 : 0);
 
			// Pre-size an array of mapped paths
			aPaths = new Array( wRootItem.childCount() );
 
			// Until we've reached the last child
			while( nChildIdx < aPaths.length ){
				// Set the value of the n'th element; offset for 0 vs 1 based
				aPaths[ nChildIdx - 1 ] = wLastItem.text( 1 );
 
				// Get the next child item
				wLastItem = wLastItem.nextSibling();
				// Increment the child index
				nChildIdx += 1;
			}
 
			// Iterate over the mapped items, in reverse order,
			// because we want the same order as they currently
			// exist, and adding an item prepends to the list
			for( var i = aPathItems.length; i > 0; i -= 1 ){
				// Get the 'current' item; we adjust the index
				// because we are iterating in reverse order
				oPathItem = aPathItems[ i - 1 ];
				// Get the path from the item
				sPath = oPathItem[ s_sInstallPath ];
 
				// Get the index of the path
				nPathIdx = aPaths.indexOf( sPath );
				// If the path is already in the list
				if( nPathIdx > -1 ){
					// Initialize
					nChildIdx = 0;
					// Get the first child
					wPathItem = wRootItem.firstChild();
					// While we have an item and we have not reached our path
					while( wPathItem && nChildIdx != nPathIdx ){
						// Increment the child count
						nChildIdx += 1;
						// Get the next sibling
						wPathItem = wRootItem.nextSibling();
					}
 
					// If we have an item
					if( wPathItem ){
						// Set the source to both
						wPathItem.setText( 1, "Both" );
					}
 
					// Next!!
					continue;
				}
 
				// Create an item for the path
				wPathItem = new DzCheckListItem( wRootItem, DzCheckListItem.CheckBox );
				// Set the label
				wPathItem.setText( 0, oPathItem[ s_sInstallPathTitle ] );
				// Set the source
				wPathItem.setText( 1, "DIM" );
				// Set the path
				wPathItem.setText( 2, sPath );
 
				// If we have a last item
				if( wLastItem ){
					// Move the new item after it
					wPathItem.moveItem( wLastItem );
				}
 
				// Update the last item to the newly added item
				wLastItem = wPathItem;
			}
 
			// Get the next root item
			wRootItem = wRootItem.nextSibling();
		}
	};
 
	/*********************************************************************/
	// void : Adds the mapped directories known by DAZ Studio as items, based on type
	function addLocalMappedPaths( wListView, sType )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Initialize the number of directories
		var nDirs = -1;
 
		// Based on type, set the number of directories
		switch( sType ){
			case s_sNativeFormats:
				nDirs = oContentMgr.getNumContentDirectories();
				break;
			case s_sPoserFormats:
				nDirs = oContentMgr.getNumPoserDirectories();
				break;
			case s_sOtherFormats:
				nDirs = oContentMgr.getNumImportDirectories();
				break;
			default:
				return;
		}
 
		// If we have directories
		if( nDirs >= 0 ){
			// Create the root item for the type
			var wRootItem = new DzCheckListItem( wListView, DzCheckListItem.CheckBoxController );
			// Set the text to the type
			wRootItem.setText( 0, sType );
			// Expand the item; show it's children
			wRootItem.open = true;
 
			// Declare working variables
			var nDir;
			var sPath;
			var oFolder;
			var wPathItem;
 
			// Iterate over the directories, in reverse order,
			// because we want them displayed in the same order
			// as they currently exist, and adding an item
			// prepends to the list
			for( var i = nDirs; i > 0; i -= 1 ){
				// Adjust the index because we are iterating in reverse order
				nDir = (i - 1);
				// Based on type, get the n'th folder
				switch( sType ){
					case s_sNativeFormats:
						oFolder = oContentMgr.getContentDirectory( nDir );
						break;
					case s_sPoserFormats:
						oFolder = oContentMgr.getPoserDirectory( nDir );
						break;
					case s_sOtherFormats:
						oFolder = oContentMgr.getImportDirectory( nDir );
						break;
					default:
						oFolder = undefined;
						break;
				}
 
				// If we do not have a folder
				if( !oFolder ){
					// Next!!
					continue;
				}
 
				// Get the full path of the folder
				sPath = oFolder.fullPath;
 
				// Create an item for the path
				wPathItem = new DzCheckListItem( wRootItem, DzCheckListItem.CheckBox );
				// Set the label
				wPathItem.setText( 0, oFolder.label );
				// Set the source
				wPathItem.setText( 1, "DS" );
				// Set the path
				wPathItem.setText( 2, sPath );
			}
		}
	};
 
	/*********************************************************************/
	// Object : Retrieve checked path items from a listview
	function getCheckedPathItems( wListView )
	{
		// Define working variables
		var oPaths = {};
		var aPaths = [];
		var sType = "";
 
		// Declare working variables
		var wItem;
		var oItem;
 
		// Get all of the items
		var aCheckedItems = wListView.getItems( DzListView.All );
		// Iterate over the checked items
		for( var i = 0; i < aCheckedItems.length; i += 1 ){
			// Get the 'current' item
			wItem = aCheckedItems[ i ];
			// If the item is at the root depth; i.e. it's a checkbox controller
			if( wItem.depth() == 0 ){
				// If we have a type
				if( !sType.isEmpty() ){
					// Set the paths for the type
					oPaths[ sType ] = aPaths;
				}
 
				// Get the label
				sType = wItem.text( 0 );
 
				// Get the paths for the type
				aPaths = oPaths[ sType ];
				// If paths haven't been defined for the type yet
				if( aPaths == undefined ){
					// Create a new array
					aPaths = [];
				}
 
				// Next!!
				continue;
			}
 
			// If the item is checked
			if( wItem.on ){
				// Add the path to the array
				aPaths = s_oArrayHelper.addToArray( aPaths, wItem.text( 2 ) );
			}
 
			// If we have a type
			if( !sType.isEmpty() ){
				// Set the paths for the type
				oPaths[ sType ] = aPaths;
			}
		}
 
		// Return the path items
		return oPaths;
	};
 
	/*********************************************************************/
	// Object : Prompt the user to choose directories to be mapped
	function getUserMappedDirs( sAccount, aAccountMappedItems )
	{
		// Define common values
		var sObjectNamePrefix = s_oStringHelper.stripSpaces( s_sToolName );
		var sLabel = text( "Label" );
		var sSource = text( "Source" );
		var sPath = text( "Path" );
		var sInstallManager = text( "Install Manager" );
		var sContentPathShortcuts = text( "Content Path Shortcuts" );
		var sRemoteTitle = String("%1 %2 :").arg( sInstallManager ).arg( sContentPathShortcuts );
		var sLocalTitle = text( "%1 Mapped Directories :" ).arg( App.appName );
		var sSelectCurrent = text( "Select Currently Mapped" );
 
		// Create a basic dialog
		var wDlg = new DzBasicDialog();
		// Set the title
		wDlg.caption = String("%1 (%2)").arg( s_sToolName ).arg( sAccount );
 
		// Get the wrapped QWidget
		var oDlg = wDlg.getWidget();
		// Set the object name; used to store/retrieve unique dialog geometry
		oDlg.objectName = sObjectNamePrefix;
 
		// Create a label
		var wLbl = new DzLabel( wDlg );
		// Get the wrapped QWidget
		var oLbl = wLbl.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oLbl.objectName = String("%1DescriptionLbl").arg( sObjectNamePrefix );
		// Set styling options on the label
		wLbl.wordWrap = true;
		// If the application version is 4.10.0.22 or newer
		if( App.version64 >= 0x0004000a00000016 ){
			// Disable eliding
			wLbl.elideMode = DzWidget.ElideNone;
		}
		// Set the text
		wLbl.text = text( "Use the list displayed below to select which paths to " +
			"map within %1.<br/><br/>The \"What's This?\" button, in the lower left " +
			"corner of this dialog, can be used to view more information about a " +
			"particular widget; simply press the button, then click on the widget." +
			"<hr>").arg( App.appName );
		// Add the label to the dialog
		wDlg.addWidget( wLbl );
 
		// Get the current style
		var oStyle = App.getStyle();
		// Get pixel metrics from the style
		var nMargin = oStyle.pixelMetric( "DZ_GeneralMargin" );
		var nStepSize = oStyle.pixelMetric( "DZ_TreeStepSize" );
 
		// Create a label
		var wLocalGrpBx = new DzVGroupBox( wDlg );
		// Get the wrapped QWidget
		var oLocalGrpBx = wLocalGrpBx.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oLocalGrpBx.objectName = String("%1LocalGrpBx").arg( sObjectNamePrefix );
		// Set the title
		wLocalGrpBx.title = sLocalTitle;
		// Set styling options on the group
		wLocalGrpBx.flat = true;
		wLocalGrpBx.insideMargin = nMargin;
		// Set the what's this text
		wLocalGrpBx.whatsThis = String("<b>%1</b><br/><br/>%2")
			.arg( sLocalTitle.replace( " :", "" ) )
			.arg( text( "Use this list to indicate which of the paths you would like to " +
			"make available within %1.<br/><br/>" +
			"Avoid selecting more than one entry with the same path in this list.  " +
			"Use the \"%2\" button to help select the paths that are currently mapped " +
			"and unique.")
			.arg( App.appName )
			.arg( sSelectCurrent ) );
		// Add the label to the dialog
		wDlg.addWidget( wLocalGrpBx );
 
		// Create a listview; for locally known mappings
		// we'll see why its defined as a global shortly
		s_wLocalView = new DzListView( wLocalGrpBx );
		// Get the wrapped QWidget
		oListView = s_wLocalView.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oListView.objectName = String("%1LocalLView").arg( sObjectNamePrefix );
 
		// Add the label column
		s_wLocalView.addColumn( sLabel );
		// Add the source column
		s_wLocalView.addColumn( sSource );
		// Add the path column
		s_wLocalView.addColumn( sPath );
		// Set selection to highlight all columns
		s_wLocalView.allColumnsShowFocus = true;
		// Disable sorting; set the sorting column to none
		s_wLocalView.setSorting( -1 );
		// Set styling options on the listview
		s_wLocalView.itemMargin = nMargin;
		s_wLocalView.treeStepSize = nStepSize;
		// Set the what's this text
		s_wLocalView.whatsThis = wLocalGrpBx.whatsThis;
 
		// Add items for the mapped directories;
		// we do this in reverse of the order we want them
		// to be displayed in, because adding an item
		// prepends it to the list and we are not sorting
		addLocalMappedPaths( s_wLocalView, s_sOtherFormats );
		addLocalMappedPaths( s_wLocalView, s_sPoserFormats );
		addLocalMappedPaths( s_wLocalView, s_sNativeFormats );
 
		// Add items for the Install Manager mappings;
		addUniqueRemoteMappedPaths( s_wLocalView, aAccountMappedItems );
 
		// Set the [unique] paths checked by default
		setUniqueMappedDirs();
 
		// Create a button
		var wCurrentBtn = new DzPushButton( wDlg );
		// Get the wrapped QWidget
		var oCurrentBtn = wCurrentBtn.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oCurrentBtn.objectName = String("%1CurrentBtn").arg( sObjectNamePrefix );
		// Set the text
		wCurrentBtn.text = sSelectCurrent;
		// Set the tooltip text
		wCurrentBtn.toolTip = text( "Click to restore the selection of currently mapped unique paths" );
		// Set the what's this text
		wCurrentBtn.whatsThis = String("<b>%1</b><br/><br/>%2.<br/><br/>%3")
			.arg( wCurrentBtn.text ).arg( wCurrentBtn.toolTip )
			.arg( text( "The path of each item in the \"%1\" list is checked for whether it " +
				"is mapped and unique.  If the path of an item is mapped and unique, " +
				"that item becomes checked.  If the path of an item is not mapped and " +
				"unique, that item becomes unchecked.  Root items are \"tri-state\"; " +
				"they become checked or unchecked according to the checked state of " +
				"their respective children.").arg( sLocalTitle.replace( " :", "" ) ) );
		// Add the button to the dialog button box
		wDlg.addButton( wCurrentBtn );
 
		// Connect the button pressed signal to the function that checks unique paths;
		// the function depends on the listview being defined, and so this is why we
		// defined the listview in the global scope
		connect( wCurrentBtn, "pressed()", setUniqueMappedDirs );
 
		// If the user cancelled
		if( !wDlg.exec() ){
			// Return an empty array
			return {};
		}
 
		// Return the checked items
		return getCheckedPathItems( s_wLocalView );
	};
 
	/*********************************************************************/
	// String : Prompt the user to choose an account
	function getUserAccount( aAccounts )
	{
		// Get the current author
		var oAuthor = App.getCurrentAuthor();
		// Get the name of the author
		var sAuthor = oAuthor.name
 
		// Create a basic dialog
		var wDlg = new DzBasicDialog();
		// Set the title
		wDlg.caption = s_sToolName;
 
		// Get the wrapped QWidget
		var oDlg = wDlg.getWidget();
		// Set the object name; used to store/retrieve unique dialog geometry
		oDlg.objectName = s_oStringHelper.stripSpaces( s_sToolName ) + "Account";
 
		// Create a button group
		var wAccountGrp = new DzVButtonGroup( wDlg );
		// Get the wrapped QWidget
		oAccountGrp = wAccountGrp.getWidget();
		// Set the object name; used for interactive lessons and inline help
		oAccountGrp.objectName = oDlg.objectName + "BGrp";
		// Set the title of the group
		wAccountGrp.title = text( "Choose an Account :" );
 
		// Declare working variables
		var wRadioBtn;
		var oRadioBtn;
		var sLabel;
 
		var sWhatsThis = String("<b>%1</b><br/><br/>%2.");
 
		// Iterate over the accounts
		for( var i = 0; i < aAccounts.length; i += 1 ){
			// Create a radio button
			wRadioBtn = new DzRadioButton( wAccountGrp );
			// Get the wrapped QWidget
			oRadioBtn = wRadioBtn.getWidget();
			// Set the object name; used for interactive lessons and inline help
			oRadioBtn.objectName = oDlg.objectName + "RBtn";
 
			// Get the URI decoded name of the account
			sLabel = decodeURIComponent( aAccounts[i] );
			// Strip the file extension from the label
			sLabel = sLabel.substring( 0, sLabel.lastIndexOf(".") );
			// Set the button text
			wRadioBtn.text = sLabel;
			// Set the tooltip text
			wRadioBtn.toolTip = text( "Select to read from the \"%1\" account" ).arg( sLabel );
			// Set the what's this text
			wRadioBtn.whatsThis = sWhatsThis.arg( sLabel ).arg( wRadioBtn.toolTip );
			// Add the button to the group
			wAccountGrp.addButton( wRadioBtn, i );
 
			// If the label is the current author
			if( sLabel == sAuthor ){
				// Set that button as the default
				wRadioBtn.checked = true;
			}
		}
 
		// If no default was set
		if( wAccountGrp.selected < 0 && wRadioBtn ){
			// Set the last button as the defalt
			wRadioBtn.checked = true;
		}
 
		// Add the group to the dialog
		wDlg.addWidget( wAccountGrp );
 
		// If the user doesn't cancel
		if( wDlg.exec() ){
			// Return the user's choice
			return aAccounts[ wAccountGrp.selected ];
		}
 
		// Return no choice made
		return "";
	};
 
	/*********************************************************************/
	// Object : Reads an Install Manager configuration *.ini and converts it to a JSON object
	function parseInstallManagerConfig( sConfigPath )
	{
		// Initialize the return object
		var oData = {};
 
		// Create a file object for the config
		var oConfigFile = new DzFile( sConfigPath );
		// Open the file for reading
		oConfigFile.open( DzFile.ReadOnly );
 
		// Read the lines from the file
		var aLines = oConfigFile.readLines();
 
		// Close the file
		oConfigFile.close();
 
		// Declare working variables
		var sLine = "";
		var aParts = [];
		var aItems = [];
		var sSection = "";
		var sKey = "";
		var sValue = "";
		var nKey = 0;
		var nValue = 0;
		var vValue = undefined;
		var oSection;
		var oItem;
		var bInRange;
 
		// Iterate over the lines
		for( var i = 0; i < aLines.length; i += 1 ){
			// Get the 'current' line, trimmed of leading/trailing whitespace
			sLine = aLines[i].trim();
			// If the line is empty
			if( sLine.isEmpty() ){
				// Next!!
				continue;
			}
 
			// Split the line between key and value
			aParts = sLine.split( "=" );
			// Set the key to the first part
			sKey = aParts.shift();
			// Set the value to the rest
			sValue = aParts.join( "=" );
 
			// If the key starts and ends with square brackets
			if( sKey.startsWith( "[" ) && sKey.endsWith( "]" ) ){
				// If we have a section and a name
				if( oSection && !sSection.isEmpty() ){
					// Update the section
					oData[ sSection ] = oSection;
				}
 
				// Get the name of the section
				sSection = sKey.substring( 1, sKey.length - 1 );
				// If the section name is not valid
				if( s_aValidSectionNames.indexOf( sSection ) < 0 ){
					// Reset the section name
					sSection = "";
					// Record/report the problem
					print( "Invalid Section :", sKey );
				}
 
				// Create a section object
				oSection = {};
 
				// Next line!!
				continue;
			}
 
			// If we do not have a value
			if( sValue.isEmpty() ){
				// Next!!
				continue;
			}
 
			// If the key is multi-part;  index\key
			if( sKey.indexOf( "\\" ) > -1 ){
				// Split the key
				aParts = sKey.split( "\\" );
				// Set the index from the first part; adjust for zero-based
				nKey = Number( aParts.shift() ) - 1;
				// Set the key to the rest
				sKey = aParts.join( "\\" );
			// If the key is not multi-part
			} else {
				// Set the index to be outside the valid multi-part range
				nKey = -1;
			}
 
			// If the key name is not valid
			if( s_aValidKeyNames.indexOf( sKey ) == -1
			&& sSection != s_sCustomUserData ) {
				// Record/report the problem
				print( "Invalid Key :", sKey );
				// Next line!!
				continue;
			}
 
			// If the value is the string representation of true
			if( sValue == "true" ){
				// Set the value to record to the boolean
				vValue = true;
			// If the value is the string representation of false
			} else if( sValue == "false" ){
				// Set the value to record to the boolean
				vValue = false;
			// If the value is the string representation of a number
			} else if( Number( sValue ) == sValue ){
				// Convert the string to a number
				nValue = Number( sValue );
				// If the number is fractional
				if( nValue % 1 > 0 ){
					// Set the value to record to the floating point conversion
					vValue = parseFloat( sValue );
				// If the number is whole
				} else {
					// Set the value to record to the integer conversion
					vValue = parseInt( sValue );
				}
			// If the value is a string
			} else {
				// Set the value to record to the string
				vValue = sValue;
			}
 
			// If the name of the key is 'size'
			if( sKey == s_sSize ){
				// Pre-size an array for the items
				aItems = new Array( vValue );
			}
 
			// If the key index is within range; it's a multi-part key
			bInRange = (nKey > -1 && nKey < aItems.length);
			if( bInRange ){
				// Get the 'current' item
				oItem = aItems[ nKey ];
				// If the object is null; it will be the first time,
				// due to pre-sizing the array
				if( oItem == null ){
					// Create a new object
					oItem = {};
				}
			}
 
			// If we have an item and the index is in range; multi-part key
			if( oItem && bInRange ){
				// Set the item's key to the value
				oItem[ sKey ] = vValue;
				// Update the item in the array
				aItems[ nKey ] = oItem;
				// Update the section with the items
				oSection[ s_sItems ] = aItems;
			// If the key is anything other than 'size'
			} else if( sKey != s_sSize ){
				// Set the value of the key, in the 'current' section
				oSection[ sKey ] = vValue;
			}
		}
 
		// If the section is named
		if( !sSection.isEmpty() ){
			// Update the section on the data object
			oData[ sSection ] = oSection;
		}
 
		// Return the data object
		return oData;
	};
 
	/*********************************************************************/
	// void : Set the directories that are mapped
	function setMappedPaths( oPaths )
	{
		// Get the content manager
		var oContentMgr = App.getContentMgr();
 
		// Declare working variables
		var sType, sPath;
		var aPaths;
 
		// Get the mapping types from the object
		var aTypes = Object.keys( oPaths );
 
		// If we have types
		if( aTypes.length > 0 ){
			// Remove all mapped directories
			oContentMgr.removeAllContentDirectories();
			oContentMgr.removeAllPoserDirectories();
			oContentMgr.removeAllImportDirectories();
		}
 
		// Iterate over the type names
		for( var i = 0; i < aTypes.length; i += 1  ){
			// Get the 'current' type
			sType = aTypes[ i ];
			// Get the 'current' paths
			aPaths = oPaths[ sType ];
			// Iterate over the paths
			for( var j = 0; j < aPaths.length; j += 1  ){
				// Get the 'current' path
				sPath = aPaths[ j ];
				// Based on mapping type, add the path
				switch( sType ){
					case s_sNativeFormats:
						oContentMgr.addContentDirectory( sPath, false );
						break;
					case s_sPoserFormats:
						oContentMgr.addPoserDirectory( sPath, false );
						break;
					case s_sOtherFormats:
						oContentMgr.addImportDirectory( sPath, false );
						break;
					default:
						break;
				}
			}
		}
 
		// Saves all currently mapped directories
		oContentMgr.saveAllMappedDirectories();
	};
 
	/*********************************************************************/
	// void : Do... whatever it is we do
	function begin()
	{
		// Get the appdata directory
		var oConfigDir = new DzDir( App.getAppDataPath() );
		// Up a directory; out of the application's directory
		oConfigDir.cdUp();
		// Change to the Install Manager directory
		oConfigDir.cd( "InstallManager" );
		// Change to the user accounts directory
		oConfigDir.cd( "UserAccounts" );
 
		// Declare working variable for the config path
		var sConfigPath = String("%1/Account.ini").arg( oConfigDir.path() );
 
		// Get all of the ini files in the directory
		var aConfigFiles = oConfigDir.entryList( "*.ini", DzDir.Files );
 
		// If there is only one
		if( aConfigFiles.length == 1 ){
			// Use it
			sConfigPath = String("%1/%2").arg( oConfigDir.path() ).arg( aConfigFiles[0] );
		// If there are multiple
		} else if( aConfigFiles.length > 1 ) {
			// Prompt the user to pick one
			sConfigPath = String("%1/%2").arg( oConfigDir.path() ).arg( getUserAccount( aConfigFiles ) );
		}
 
		// Create a file info object
		var oConfigFile = new DzFileInfo( sConfigPath );
		// If the file doesn't exist
		if( !oConfigFile.exists() ){
			// Inform the user
			MessageBox.information(
				text( "An account configuration file could not be found. Create an account " +
					"using Install Manager and then try again."),
				s_sToolName, text( "&OK" ) );
			// We cannot continue
			return;
		}
 
		// Convert the config file at the path to a JSON object
		var oConfigData = parseInstallManagerConfig( sConfigPath );
 
		// If the JSON object has install paths defined
		if( oConfigData.hasOwnProperty( s_sInstallPaths ) ){
			// Prompt the user to choose which ones they want mapped
			var oPaths = getUserMappedDirs( decodeURIComponent( oConfigFile.baseName() ), oConfigData[ s_sInstallPaths ][ s_sItems ] );
			// Update mapped directories
			setMappedPaths( oPaths );
		}
	};
 
	/*********************************************************************/
	// Start doing stuff
	begin();
 
// Finalize the function and invoke
})();