/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/


include("renderLibrary.js")

/**
 * Create a contribution that sets a property if its value
 * is not equal to a given default.  
 * @param phase the phase to supply
 * @param indent extra indentation to add (i.e. 1 if inside an "if")
 * @param instanceMemberName the name of the member variable
 * @param propertyValue the current value of the property
 * @param defaultValue the value considered the default
 * @param format a format string for setting the property, where
 *		{0} is substituted with instanceMemberName and 
 *		{1} is substituted with the propertyValue
 * @return a contribution, or null
 */
function createSetPropertyForPhase(contribs, phase, indent, instanceMemberName, 
			propertyValue, defaultValue, format) {
	if (propertyValue != defaultValue) {
		contrib = Engine.createContributionForPhase(phase);
		contrib.indentAdjust(indent);
		contrib.setFormattedText(format, new Array( instanceMemberName, propertyValue) );
		return contrib;
	}
	return null;
}

/**
 * Generate a contribution that sets a property if its value
 * is not equal to a given default.  
 * @param contribs the contribution list
 * @param phase the phase to supply
 * @param indent extra indentation to add (i.e. 1 if inside an "if")
 * @param instanceMemberName the name of the member variable
 * @param propertyValue the current value of the property
 * @param defaultValue the value considered the default
 * @param format a format string for setting the property, where
 *		{0} is substituted with instanceMemberName and 
 *		{1} is substituted with the propertyValue
 * @param checkNull if true, insert check for null instance around sette
 */
function setPropertyForPhase(contribs, phase, indent, instanceMemberName, 
			propertyValue, defaultValue, format, checkNull) {
	contrib = createSetPropertyForPhase(contribs, phase, indent, 
			instanceMemberName, propertyValue, defaultValue, format);
	if (contrib == null)
		return;
	if (checkNull) {
		acontrib = Engine.createContributionForPhase(phase);
		acontrib.indentAdjust(indent);
		acontrib.setFormattedText("if ( {0} )\n", [ instanceMemberName ]);
		contribs.add(acontrib);
		
		acontrib = Engine.createContributionForPhase(phase);
		acontrib.indentAdjust(indent+1);
		acontrib.setText("{\n");
		contribs.add(acontrib);
		
		contribs.add(contrib);
		
		acontrib = Engine.createContributionForPhase(phase);
		acontrib.indentAdjust(indent+1);
		acontrib.setText("}\n");
		contribs.add(acontrib);
	} else {
		contribs.add(contrib);
	}
}

/**
 * Generate a set of contributions to set multiple possible
 * properties.
 * Like #setPropertyForPhase(), where the 5th and later arguments
 * are taken in groups of three as [propertyValue, defaultValue, format].
 */
function setPropertiesForPhase(contribs, phase, indent, instanceMemberName, checkNull) {
	var ourContribStr = "";
	var any = false;
	var innerIndent = indent + (checkNull ? 1 : 0);
	for (var h = 5; h < arguments.length; h += 3) {
		contrib = createSetPropertyForPhase(contribs, phase, innerIndent, instanceMemberName,
			arguments[h], arguments[h+1], arguments[h+2]);
		if (contrib == null)
			continue;
		if (!any && checkNull) {
			acontrib = Engine.createContributionForPhase(phase);
			acontrib.indentAdjust(indent);
			acontrib.setFormattedText("if ( {0} )\n", [ instanceMemberName ]);
			ourContribStr = acontrib.getText();
			
			acontrib = Engine.createContributionForPhase(phase);
			acontrib.indentAdjust(indent+1);
			acontrib.setText("\t" + "{\n");
			ourContribStr = ourContribStr + acontrib.getText();
			
			any	= true;
		}
		ourContribStr = ourContribStr + "\t" + contrib.getText();
	}

	if (any && checkNull) {
		acontrib = Engine.createContributionForPhase(phase);
		acontrib.indentAdjust(indent+1);
		acontrib.setText("\t" + "}\n");
		ourContribStr = ourContribStr + acontrib.getText();
	}
	acontrib = Engine.createContributionForPhase(phase);
	acontrib.indentAdjust(indent);
	acontrib.setText(ourContribStr);
	contribs.add(acontrib);
}

/**
 *	Get the base filename
 *	@param path a path
 */
function getBaseFileName(path) {
	var idx = path.lastIndexOf('/');
	if (idx >= 0)
		base = path.substring(idx+1);
	else {
		idx = path.lastIndexOf('\\');
		if (idx >= 0)
			base = path.substring(idx+1);
		else
			base = path;
	}
	idx = base.lastIndexOf('.');
	if (idx >= 0)
		base = base.substring(0, idx);
		
	return base;
}

/**
 *	Get the bitmap header file
 *	@param path the system MBM/MIF name from the image.bmpfile property
 */
function getBitmapHeaderName(path) {
	var base = getBaseFileName(path);
	base = base + ".mbg";	
	return base.toLowerCase();
}

function addContrib(contribs, phase, loc, indent, text) {
	var c;
	if (phase != null)
		c = Engine.createContributionForPhase(phase);
	else
		c = Engine.createContributionForLocation(loc);
	c.setText(text);
	c.indentAdjust(indent);
	contribs.add(c);
}

function addFormattedContrib(contribs, phase, loc, indent, format, args) {
	var c;
	if (phase != null)
		c = Engine.createContributionForPhase(phase);
	else
		c = Engine.createContributionForLocation(loc);
	c.setFormattedText(format, args);
	c.indentAdjust(indent);
	contribs.add(c);
}

/**
 *	Get the nearest enclosing instance that defines a class.
 */
function getClassHolder(instance) {
	while (instance != null && !instance.properties.className) {
		instance = instance.parent;
	}
	return instance;
}

/**
 *	Get the table of multi-image filenames to literal names
 */
function getFilenameToLiteralTable(instance) {
	var holder = getClassHolder(instance);
	var key = "ImageSupport.filenameToLiteralTable:" + holder;
	var table = Engine.getGlobalDictionary().get(key);
	if (table == null) {
		table = new java.util.HashMap();
		Engine.getGlobalDictionary().put(key, table);
	}
	return table;
}

/**
 *	Get an identifier name from a MBM/MIF filepath
 */
function makeLiteralIdentifier(map, path) {
	// no need to uniquify, as the base mbmdef/mifdef name must be unique as well
	var base = getBaseFileName(path);
	var litname = "K" + base + "File";
	return litname;
}

/**
 *	Prepare a container for sourcegen that involves generating
 *	image properties.  We need to ensure the MBM/MIF filename for
 *	an image is emitted as a constant only once.
 */
function resetImagePropertyState(instance) {
	var classHolder = getClassHolder(instance);
	classHolder.filenameToLiteralTable = new java.util.HashMap();
}

/**
 *	Tell if an image property is set.  If not, it should be ignored.
 */		
function isImagePropertySet(imageProperty) {
	return imageProperty.bmpfile != "" 
		&& imageProperty.bmpid != "";
}

/** 
 *	Tell if we support scaling at all
 */
function isScalingIcons() {
	var version = getComponentVersions();
	if (version.getMajor() >= 3)
		return true;
	if (version.getMajor() == 2 && version.getMinor() >= 8) {
		appUi = getRootModelInstanceOfType("com.nokia.sdt.series60.CAknAppUi");
		if (appUi != null)
			return appUi.properties.layoutAware;
	}
	return false;
}

/**
 *	Set up contributions that set up an image property given
 *	its compound property.  This uses CFbsBitmap* objects and
 *	thus allows for resizing behavior.  Use a separate routine if
 *	setting up multiple images, since the repeated code for SVG setup
 *	can get quite long.
 *	@param contribs the contributions to append to
 *	@param instance instance the image is for (used to find the class)
 *	@param phase primary phase to write to (or null, loc must be set)
 *	@param loc primary location to write to (or null, phase must be set)
 *	@param indent indentation adjust
 *	@param imageProperty the property value
 *	@param aspectOption the appropriate enumeration for aspect ratio handling (or null)
 *	@param bitmapPattern when only a bitmap is set,
 *		a MessageFormat string to be substituted with arguments ([ CFbsBitmap* ])
 *	@param bitmapAndMaskPattern when a bitmap and mask is set,
 *		a MessageFormat string to be substituted with arguments (or null)
 *		([ CFbsBitmap*, CFbsBitmap* ])
 *	@param theSize text representing the TSize argument (e.g. "control->Size()" or "TSize(45, 66)")
 *			or null to use the image's real size
 */
function setupImageFromPropertyViaCFbsBitmap(contribs, instance, phase, loc, indent,
		imageProperty, aspectOption, 
		bitmapPattern, bitmapAndMaskPattern, theSize) {

	var version = getComponentVersions();
	var contrib;

	// moved here to precede system includes of .mbg files	
	var isSVG = imageProperty.editableValue.isSVG();

	if (isScalingIcons() || isSVG) {
		// get icon utilities header
		addContrib(contribs, "MainSystemIncludes", loc, 0,
			"#include <akniconutils.h>\n");
	}
	
	if (imageProperty.bmpfile != "") {
		// make the literal string for the filename
		var table = getFilenameToLiteralTable(instance);
		var litname = table.get(imageProperty.bmpfile);
		if (!litname) {
			litname = makeLiteralIdentifier(table, imageProperty.bmpfile);
			addContrib(contribs, "MainConstants", null, 0,
				"_LIT( " + litname +", \"" + 
					TextUtils.escape(imageProperty.bmpfile, '"') + "\" );\n");
	
			// get the mbg header
			addContrib(contribs, "MainSystemIncludes", null, 0,
				"#include <" + getBitmapHeaderName(imageProperty.bmpfile) + ">\n");
	
			table.put(imageProperty.bmpfile, litname);
		}
	}

	if (!isScalingIcons() && !isSVG) {
		// only unscalable bitmaps allowed, and initialized via CFbsBitmap
		indent++;
		addContrib(contribs, phase, loc, indent,
			"{\n");
		if (bitmapAndMaskPattern != null 
				&& imageProperty.bmpfile != ""
				&& imageProperty.bmpmask != "" 
				&& imageProperty.bmpid != "") {
			addContrib(contribs, phase, loc, indent,
				"CFbsBitmap* bitmap = iEikonEnv->CreateBitmapL( " + litname + ", " + imageProperty.bmpid + " );\n"
				);
			addContrib(contribs, phase, loc, indent,
				"CFbsBitmap* mask = iEikonEnv->CreateBitmapL( " + litname + ", " + imageProperty.bmpmask + " );\n"
				);
			
			addFormattedContrib(contribs, phase, loc, indent,
				bitmapAndMaskPattern, [ "bitmap", "mask" ]);
		}
		else if (bitmapPattern != null) {
			if (imageProperty.bmpfile != "" && imageProperty.bmpid != "") {
				addContrib(contribs, phase, loc, indent,
					"CFbsBitmap* bitmap = iEikonEnv->CreateBitmapL( " + litname + ", " + imageProperty.bmpid + " );\n"
					);
			} else {
				// no image, but at least a bitmap is required
				addContrib(contribs, phase, loc, indent,
					"CFbsBitmap* bitmap = new (ELeave) CFbsBitmap;\n"
					);
			}						
			addFormattedContrib(contribs, phase, loc, indent,
				bitmapPattern, [ "bitmap" ]);
		}

		addContrib(contribs, phase, loc, indent,
			"}\n");
		indent--;
		
	} else {
		// S60 >= 3.0
		
		indent++;

		addContrib(contribs, phase, loc, indent,
			"{\n");

		var sizeArg = theSize;
		if (sizeArg == null)
			sizeArg = "size"; 	// for SVG 

		if (imageProperty.bmpfile != "") {		
			// load the bitmap and/or mask
			addContrib(contribs, phase, loc, indent,
				"CFbsBitmap *bitmap, *mask;\n");
				
			addContrib(contribs, phase, loc, indent,
				"AknIconUtils::CreateIconL( bitmap, mask,\n"+
				"\t\t" + litname + ", " + 
				((bitmapPattern != null && imageProperty.bmpid != "") ?
					imageProperty.bmpid : "-1") + 
					", " +
				((bitmapAndMaskPattern != null && imageProperty.bmpmask != "") ?
					imageProperty.bmpmask : "-1") +
					" );\n");
					
			// prepare to issue two calls needing SVG info
			if (isSVG && theSize == null) {
				addContrib(contribs, phase, loc, indent,
					"TSize size;\n"+
					"AknIconUtils::PreserveIconData( bitmap );\n"+
					//"AknIconUtils::PreserveIconData( mask );\n"+
					"");
			}

		} else {
			// no image, but at least a bitmap WITH CONTENT is required
			addContrib(contribs, phase, loc, indent,
				"CFbsBitmap *bitmap = new (ELeave) CFbsBitmap;\n"+
				"bitmap->Create( TSize( 1, 1), EGray2 );\n");
		}
		
		// set the size
		var aspectText;
		if (aspectOption != null && aspectOption != "")
			aspectText = ", " + aspectOption;
		else
			aspectText = ", EAspectRatioNotPreserved";

		if (imageProperty.bmpfile != "") {		
			if (isSVG && theSize == null) {
				// get the size
				addContrib(contribs, phase, loc, indent,
					"AknIconUtils::GetContentDimensions( bitmap, size );\n");
			}
		}

		if (bitmapAndMaskPattern != null 
				&& imageProperty.bmpfile != ""
				&& imageProperty.bmpmask != "" 
				&& imageProperty.bmpid != "") {

			addContrib(contribs, phase, loc, indent,
				"AknIconUtils::SetSize( bitmap, " + sizeArg + aspectText + " );\n");
			addContrib(contribs, phase, loc, indent,
				"AknIconUtils::SetSize( mask, " + sizeArg + aspectText + " );\n");

			addFormattedContrib(contribs, phase, loc, indent,
				bitmapAndMaskPattern, [ "bitmap", "mask" ]);
					
		} else if (bitmapPattern != null) {
			addContrib(contribs, phase, loc, indent,
				"AknIconUtils::SetSize( bitmap, " + theSize + aspectText + " );\n");

			addFormattedContrib(contribs, phase, loc, indent,
				bitmapPattern, [ "bitmap" ]);
		}

		if (isSVG && theSize == null) {
			addContrib(contribs, phase, loc, indent,
				"AknIconUtils::DestroyIconData( bitmap );\n");
		}
		
		addContrib(contribs, phase, loc, indent,
			"}\n");
		
		indent--;
	}
}

/**
 *	Set up contributions that set up an image property given
 *	its compound property.  This passes arguments to a function
 *	which takes the (TDesc aFileName, TInt bmpid, TInt maskid)
 *	arguments.  Image resizing is not allowed.
 *	@param contribs the contributions to append to
 *	@param instance instance the image is for (used to find the class)
 *	@param phase primary phase to write to (or null, loc must be set)
 *	@param loc primary location to write to (or null, phase must be set)
 *	@param indent indentation adjust
 *	@param imageProperty the compound property providing bmpfile, bmpid, bmpmask
 *	@param bitmapPattern when only a bitmap is set,
 *		a MessageFormat string to be substituted with arguments
 *		([ filename, id ])
 *	@param bitmapAndMaskPattern when a bitmap and mask is set,
 *		a MessageFormat string to be substituted with arguments (or null)
 *		([ filename, id, maskid ])
 */
function setupImageFromPropertyViaTuple(contribs, instance, phase, loc, indent,
		imageProperty,
		bitmapPattern, bitmapAndMaskPattern) {

	if (!isImagePropertySet(imageProperty))
		return;
		
	var version = getComponentVersions();
	var contrib;
	
	// make the literal string for the filename
	var table = getFilenameToLiteralTable(instance);
	var litname = table.get(imageProperty.bmpfile);
	if (!litname) {
		litname = makeLiteralIdentifier(table, imageProperty.bmpfile);
		addContrib(contribs, "MainConstants", null, 0,
			"_LIT( " + litname +", \"" + 
				TextUtils.escape(imageProperty.bmpfile, '"') + "\" );\n");

		// get the mbg header
		addContrib(contribs, "MainSystemIncludes", null, 0,
			"#include <" + getBitmapHeaderName(imageProperty.bmpfile) + ">\n");

		table.put(imageProperty.bmpfile, litname);
	}
		
	if (bitmapAndMaskPattern != null 
			&& imageProperty.bmpmask != "") {
		
		addFormattedContrib(contribs, phase, loc, indent,
			bitmapAndMaskPattern, [ litname, imageProperty.bmpid, imageProperty.bmpmask ]);
	}
	else if (bitmapPattern != null) {
		addFormattedContrib(contribs, phase, loc, indent,
			bitmapPattern, [ litname, imageProperty.bmpid ]);
	}
	
}

/**
 *	Create a method that loads and scales SVG or BMP icons, for use when
 *	multiple icons are loaded at once.  This assumes the function defined in
 *	LoadAndScaleIconL.inc.  For S60 2.8 or newer.
 */
function defineIconLoadingRoutines(contribs, location, className) { 
	// get icon utilities headers.  
	addContrib(contribs, "HeaderIncludes", null, 0,
		"#include <akniconutils.h>\n");
	addContrib(contribs, "HeaderIncludes", null, 0,
		"#include <gulicon.h>\n");
		
	//// needs to be permanent or else it's duplicated
	//addContrib(contribs, "UserHandlers", null, 0,
	addContrib(contribs, "ClassMethods", null, 0,
		"static CGulIcon* LoadAndScaleIconL(\n"+
		"\t\tconst TDesC& aFileName,\n"+
		"\t\tTInt aBitmapId,\n"+
		"\t\tTInt aMaskId,\n"+
		"\t\tTSize* aSize,\n"+
		"\t\tTScaleMode aScaleMode );\n"
	);
	
	// force the location to be instantiated
	addContrib(contribs, null, location, 0, "");
	return;
}

var alignmentMap = {
	"EHLeft+EVTop" : "EHLeftVTop",
	"EHLeft+EVCenter" : "EHLeftVCenter",
	"EHLeft+EVBottom" : "EHLeftVBottom",
	"EHCenter+EVTop" : "EHCenterVTop",
	"EHCenter+EVCenter" : "EHCenterVCenter",
	"EHCenter+EVBottom" : "EHCenterVBottom",
	"EHRight+EVTop" : "EHRightVTop",
	"EHRight+EVCenter" : "EHRightVCenter",
	"EHRight+EVBottom" : "EHRightVBottom"
};

function getTGulAlignmentValue(horiz, vert) {
	return alignmentMap[horiz+"+"+vert];	
}

//////////////////////////

/**
 *	Get the table of unique images in an icon array
 *	@return an ImageList
 */
function getImageList(instance) {
	var key = "srcgenLibrary.ImageList:" + instance.name;
	var table = Engine.getGlobalDictionary().get(key);
	if (table == null) {
		table = new ImageList(instance);
		Engine.getGlobalDictionary().put(key, table);
	}
	return table;
}


/**
 *	This is a prototype used to track arrays of icons.  
 *	Create an instance of the class and populate it with image
 *	properties, then generate contributions that create a
 *	CArrayPtr<CGulIcon> array, then look up indices of image
 *	properties either by index or generated enum.
 *
 *	@param ownerInstance the instance that owns all the images
 */
function ImageList(ownerInstance) {
	this.ownerInstance = ownerInstance;
	this.imageMap = new java.util.LinkedHashMap();
	this.enumSet = new java.util.LinkedHashSet();
	this.imageIdx = 0;
	this.lastEnum = "0";
}

ImageList.prototype.getImagePropertyKey = function(imageProperty) {
	if (isImagePropertySet(imageProperty)) {
		return imageProperty.bmpfile + "|" + imageProperty.bmpid + "|" + imageProperty.bmpmask;
	} else {
		return null;
	}
}

ImageList.prototype.uniquifyEnum = function(enm) {
	var idx = 1;
	var base = enm;
	while (this.enumSet.contains(enm)) {
		enm = base + (++idx);
	}
	return enm;
}

IL_PROPERTY_INDEX = 0;
IL_INDEX_INDEX = 1;
IL_ENUM_INDEX = 2;

/**
 *	Register an image property.  Only adds if unique.
 *	@param instance the instance containing the property
 *	@param property the property ID
 */
ImageList.prototype.registerImageProperty = function(imageProperty) {
	var key = this.getImagePropertyKey(imageProperty);
	if (key != null && !this.imageMap.containsKey(key)) {
		var base = imageProperty.bmpid;
		if (base == "")
			base = imageProperty.bmpmask;
		if (base.substring(0, 4) == "EMbm")
			base = base.substring(4);
		
		var enm = "E" + titleCase(this.ownerInstance.name) + base + "Index";
		enm = this.uniquifyEnum(enm);
		this.enumSet.add(enm);
		this.lastEnum = enm;
			
		this.imageMap.put(key, [imageProperty, this.imageIdx++, enm]);
	}
}

ImageList.prototype.getSystemImageKey = function(bitmap, mask) {
	if (bitmap == null || bitmap.length == 0)
		return null;

	var file = "KAvkonBitmapFile";	// constant in aknconsts.h
	var prefix = "EMbmAvkon";
	var key = file 
		+ "|" 
		+ (bitmap.length > 0 ? prefix + titleCase(bitmap) : "") 
		+ "|" 
		+ (mask.length > 0 ? prefix + titleCase(mask) : "");
	return key;
}

/**
 *	Register an Avkon system image (only added if unique).  
 */
ImageList.prototype.registerAvkonSystemImage = function(bitmap, mask) {
	var key = this.getSystemImageKey(bitmap, mask);
	if (key != null && !this.imageMap.containsKey(key)) {
		var enm = "E" + titleCase(this.ownerInstance.name) 
			+ "Avkon" + titleCase(bitmap != null ? bitmap : mask) 
			+ "Index";
		enm = this.uniquifyEnum(enm);
		this.lastEnum = enm;

		this.imageMap.put(key, [null, this.imageIdx++, enm]);
	}
}

/**
 *	Get the icon index for the given image.
 */
ImageList.prototype.getListIconIndexForProperty = function(imageProperty) {
	var key = this.getImagePropertyKey(imageProperty);
	var value = this.imageMap.get(key);
	if (value != null)
		return value[IL_INDEX_INDEX];
	else
		return -1;
}

/**
 *	Get the enumerator for the given image.
 */
ImageList.prototype.getListIconEnumForProperty = function(imageProperty, getEnum) {
	var key = this.getImagePropertyKey(imageProperty);
	var value = this.imageMap.get(key);
	if (value != null)
		return value[IL_ENUM_INDEX];
	else
		return null;
}

/**
 *	Get the number of images, also the upper bound of the indices
 */
ImageList.prototype.getImageCount = function() {
	return this.imageIdx;
}

ImageList.prototype.getLastEnumerator = function() {
	return this.lastEnum;
}

/**
 *	Generate code to set up a CGulIcon icon array.  Since S60 doesn't
 *	tolerate empty arrays, the generated code allocates the array only if needed,
 *	leaving it on the cleanup stack.  The caller of this code/routine
 *	needs to examine the return value and handle cleaning up appropriately.
 *	@param contribs
 *	@param iconsVar name of array variable to create (must be declared)
 *	@param location the function to add code to
 *	@param mainLocation preferably owned section in main file to add function to if needed
 *	@param instance instance to search upwards for owning class
 *	@param isScaling true if scaling bitmaps and icons, false otherwise
 *  @return true if icon array allocated
 */
ImageList.prototype.generateSetupIconArrayL = function(contribs, iconsVar, location, mainLocation, instance, isScaling) {
	// must not generate a zero-size array or set an empty icon array
	var anyIcons = false;
	
	var iconVar = (iconsVar.match(".*s") ? iconsVar.substring(0, iconsVar.length - 1) : iconsVar + "_instance");
	
	for (var iter = this.imageMap.entrySet().iterator(); iter.hasNext(); ) {
		var entry = iter.next();
		var key = entry.getKey();
		var value = entry.getValue();
		var property = value[IL_PROPERTY_INDEX];
		var index = value[IL_INDEX_INDEX];
		var enm = value[IL_ENUM_INDEX];
		var granularity = this.enumSet.size() > 0? this.enumSet.size() : 1;

		if (!anyIcons) {
			addFormattedContrib(contribs, null, location, 0,
				 "{0} = new (ELeave) CAknIconArray( {1} );\n",
				 [ iconsVar,
				 granularity
				 ] );
		
			addFormattedContrib(contribs, null, location,  0,
					"CleanupStack::PushL( {0} );\n",
					[ iconsVar ]);

			if (isScaling) {
				defineIconLoadingRoutines(contribs, mainLocation, getClassHolder(instance).properties["className"]);
			}

			addFormattedContrib(contribs, null, location,  0,
					"CGulIcon* {0};\n",
					[ iconVar ]);

			anyIcons = true;		
		}
		
		//println("querying " + instance + ", property="+property);

		// This assumes the user doesn't modify the ordering and
		// all enums map evenly to 0...N.
		// We can't use ->InsertL or ->ExtendL since they're stupid.
		var adder = "->AppendL";
		
		// Bug 7621: please ensure that a leave in the AppendL doesn't 
		// leak the allocated image.
		var saveIcon = "CleanupStack::PushL( " + iconVar + " );\n";
		var restoreIcon = "CleanupStack::Pop( " + iconVar + " );\n";
		var addIcon = iconsVar + adder + "( " + iconVar + " );\n";
		var saveAddAndRestore = saveIcon + addIcon + restoreIcon;

		contrib = Engine.createContributionForLocation(location);
		contrib.setText("// for " + enm + "\n");
		contribs.add(contrib);
	
		if (property == null) {
			// system image
			var parts = key.split("\\|");
			
			var contrib = Engine.createContributionForPhase("MainSystemIncludes");
			contrib.setText("#include <aknconsts.h>\n");
			contribs.add(contrib);

			contrib = Engine.createContributionForPhase("MainSystemIncludes");
			contrib.setText("#include <avkon.mbg>\n");
			contribs.add(contrib);
			
			var format;
			
			if (isScaling) {
				if (parts[2] != "") {
					format = iconVar + " = LoadAndScaleIconL(\n\t\t{0}, {1}, {2},\n\t\tNULL, EAspectRatioPreserved );\n"
						+ saveAddAndRestore;
				} else {
					format = iconVar + " = LoadAndScaleIconL(\n\t\t{0}, {1}, -1,\n\t\tNULL, EAspectRatioPreserved );\n"
						+ saveAddAndRestore;
				}
			} else {
				if (parts[2] != "") {
					format = iconVar + " = CEikonEnv::Static()->CreateIconL(\n\t\t{0}, {1}, {2} );\n"
						+ saveAddAndRestore;
				} else {
					format = iconVar + " = CEikonEnv::Static()->CreateIconL(\n\t\t{0}, {1} );\n"
						+ saveAddAndRestore;
				}
			}			
			contrib = Engine.createContributionForLocation(location);
			contrib.setFormattedText(format, parts);
			contribs.add(contrib);
			
		} else {
			if (isScaling) {
				setupImageFromPropertyViaTuple(contribs, instance, null, location, 0,
					property,
					iconVar + " = LoadAndScaleIconL(\n\t\t{0}, {1}, -1,\n\t\tNULL, EAspectRatioPreserved );\n"
						+ saveAddAndRestore,
					iconVar + " = LoadAndScaleIconL(\n\t\t{0}, {1}, {2},\n\t\tNULL, EAspectRatioPreserved );\n"
						+ saveAddAndRestore
				);
			} else {
				setupImageFromPropertyViaTuple(contribs, instance, null, location, 0,
					property, 
					iconVar + " = CEikonEnv::Static()->CreateIconL(\n\t\t{0}, {1} );\n"
						+ saveAddAndRestore,
					iconVar + " = CEikonEnv::Static()->CreateIconL(\n\t\t{0}, {1}, {2} );\n"
						+ saveAddAndRestore);
			}
		}
	}
		
	if (anyIcons) {
		return true;
	}
	else
		return false;
}

/**
 *	Generate text to be substituted into an enum{} declaration.
 */
ImageList.prototype.generateImageListEnums = function() {
	var text = "";
	for (var iter = this.imageMap.entrySet().iterator(); iter.hasNext(); ) {
		var entry = iter.next();
		var index = entry.getValue()[IL_INDEX_INDEX];
		var enm = entry.getValue()[IL_ENUM_INDEX];
		text += enm + " = " + index + ",\n";
	}
	text += "E" + titleCase(this.ownerInstance.name) + "FirstUserImageIndex\n";
	return text;
}

ImageList.prototype.dump = function() {
	println("dump of image list: ");
	for (var iter = this.imageMap.entrySet().iterator(); iter.hasNext(); ) {
		var entry = iter.next();
		var key = entry.getKey();
		var value = entry.getValue();
		var property = value[IL_PROPERTY_INDEX];
		var index = value[IL_INDEX_INDEX];

		println("key="+key+", property="+property+", index="+index);
	}
}
