Mini Kabibi Habibi

Current Path : C:/Program Files/Adobe/Adobe Photoshop 2025/Presets/Deco/
Upload File :
Current File : C:/Program Files/Adobe/Adobe Photoshop 2025/Presets/Deco/_Deco Menu.jsx

// Script author: Radomir Mech. 
// Parts of this script are based on a script written by Chuck Uebele

#target photoshop

/////////////////////////
// SETUP
/////////////////////////

// Can be used to output messages to Extendscript Toolkit. This can be useful for debugging
function ESTKMessage(inMessage) 
{
     if ( ! BridgeTalk.isRunning( "estoolkit" ) )
         BridgeTalk.launch( "estoolkit" );
    
     if ( BridgeTalk.isRunning( "estoolkit" ) )
     {
        var bt = new BridgeTalk;
        bt.body = "$.writeln('" + inMessage + "');";
        bt.target = "estoolkit";
        bt.send();
     }
}


// all the strings that need to be localized
strDropdownListSelectionIcorrect = localize( "$$$/DecoScripts/DecoMenu/Alert1=You dropdown list selection is incorrect for menu item" );
strEntryCannotBeBlank = localize( "$$$/DecoScripts/DecoMenu/Alert2=The entry cannot be blank for menu item" );

// use this template to construct your own menu
var decoMenuTemplate = { 
    menuTitle : 'Test Menu',
    menuBackground : [0.94, 0.94, 0.94, 1],
    previewBackground : [1, 1, 1, 1],
    presetsDrowpdownlistWidth : 200,
    panels : [
     { panelName : 'Panel 1', 
        leftColumnWidth : 180,
        editTextWidth : 35,
        unitsWidth : 65,
        dropdownlistWidth : 160,
        panelMenu : [
         { itemName : 'Item 1 (range 1, 10)',  itemUnit : 'pixels', itemType : 'edittext', itemValue : 5, itemMin : 1, itemMax : 10, enabled: true/false, varName : 'var1' }, 
         { itemName : 'Item 2',  itemUnit : '', itemType : 'dropdownlist', itemList : ['selection 1', 'selection 2', { item : 'selection3', image : 'filename' }], itemValue : 2, itemMin : 0, itemMax : 0, varName : 'var2',
            disableItems : [ // optionally, you could specify which items in the current panel will be disabled (grayed out) for a specific selection.
             [0, [2,3], ["panel name", 0, 3]], // when selection 1 is chosed (index 0), it grays out menu item Item3 and Item4 (index 2, and 3 - indexed in order items are specified in the panelMenu array - from 0)
             [1, [3]]     // when selection 2 is chosed (index 1), it grays out menu item Item4 (index 3)
             ]  }, 
         { itemName : 'Item 3',  itemUnit : '', itemType : 'checkbox', itemValue : true, itemMin : 0, itemMax : 0, varName : 'var3' ,
             disableItems : [ // optionally, you could specify which items in the current panel will be disabled (grayed out) for a true or false
             [true, [2,3]],  // when the checkbox is checked, it grays out menu item Item3 and Item4 (index 2, and 3 - indexed in order items are specified in the panelMenu array - from 0)
             [false, [3]]     // when the checkbox is not set, it grays out menu item Item4 (index 3)
             ]  }, 
        { itemName : 'Item4', itemUnit  : '',  itemType : 'colorpicker', itemValue : [1, 1, 1], varName : 'color1'  },
         { itemName : 'Item5', itemUnit  : 'degrees',  itemType : 'slider', itemValue : 0, itemMin : -45, itemMax : 45, itemStep : 1, enabled: true/false, varName : 'angle1'  }
          // you can link two sliders together by using itemLEQitem : item_index (less or equal to value in item item_index) 
          // and itemGEQitem: item_index (). This can be used when the two slides control min and max range for some random parameter.
      ] }
   ] // end of panels
 } // end of menu

var skipRun = true // will be set to false if the user closes the window by pressing one of the two buttons

// the menu is set in decoMenu variable
var menu = decoMenu
var scriptMenuName = removeSpaces(menu.menuTitle) 

//=======================
var isPreview = typeof preview != 'undefined' ? preview : true
var isLivePreview =  1 //typeof livePreview != 'undefined' && livePreview != 0

var kSwatchBorderWidth = 2;

var cleanUpPath = function (path)
{
   if (path[0] == '/' && path[2] == '/')
  {
        path = path[1] + ':' + path.slice(2, path.length)
  }

    var newString = ""
    var lastIndex = 0
    for (var i = 0; i < path.length-2; i++)
    {
         if (path[i] == '%')
            if (path[i+1] == '2' && path[i+2] == '0')
            {
                newString += path.slice(lastIndex, i) + "\ "
                lastIndex = i+3
             }
     }

    if (lastIndex < path.length)
    {
        newString += path.slice (lastIndex, path.length)
        appPath = newString
    }
        
    path = newString
    if (path[0] == '~' && path[1] == '/')
    {
        if ($.getenv("HOMEPATH") != null)
        {
            if ($.getenv("HOMEDRIVE") != null)
                path = $.getenv("HOMEDRIVE") + '/' + $.getenv("HOMEPATH") + path.slice(1, path.length)
            else
                path = $.getenv("HOMEPATH") + path.slice(1, path.length)
        }
        else if ($.getenv("HOME") != null)
        {
            path = $.getenv("HOME") + path.slice(1, path.length)
        }
    }
    return path
}

//=====================================================
// set up paths
var scriptPathLocal = File.decode(app.path.fsName) + "/Presets/Deco";

scriptPath = cleanUpPath (scriptPathLocal)

//var decoPresetsFolder = app.preferencesFolder
var decoPresetsFolder = RenderAPI.getParameter(kpsUserPresetsFolder)

//alert("Deco presets folder: " + decoPresetsFolder)

var file = new Folder(decoPresetsFolder)
if (!file.exists)
    file.create()

//decoPresetsFolder += "/Presets"
//var file = new Folder(decoPresetsFolder)
//if (!file.exists)
//    file.create()

decoPresetsFolder += "/Deco"
file = new Folder(decoPresetsFolder)
if (!file.exists)
    file.create()

// Each script has its own subdirectory
decoPresetsFolder += "/" + scriptMenuName    // script name where spaces are replaced with _
file = new Folder(decoPresetsFolder)
if (!file.exists)
    file.create()
    
shippedPresetsFolder = scriptPathLocal + "/" + menu.menuTitle   // script name with possible spaces
if (!file.exists)
    shippedPresetsFolder = 0


var previewImagePath = File.decode(decoPresetsFolder) + "/decoPreview.png"

if (isPreview)
{
    uiPreviewImage = new File(previewImagePath) //app.path +  "/presets/deco/decoPreview.jpg" )
    //previewImagePath = uiPreviewImage.absoluteURI
    //previewImagePath = cleanUpPath (previewImagePath)
}

//alert (previewImagePath)

if (typeof previewZoom == 'undefined')
    previewZoom = false // default - can be overriden by the user script

if (previewZoom)
{
    if (typeof modelParameters.previewScaleFactor == 'undefined')
        modelParameters.previewScaleFactor = 1
}

//var uiDecoPref = new File(decoPresetsFolder +  "/decoUserPref.xml" ) //XXX make user specific
var uiPreviewImage
if (isPreview)
    uiPreviewImage = new File(previewImagePath) //app.path +  "/presets/deco/decoPreview.jpg" )

var startNodes

var presetList = [ ]

var presetsXML = 0
var numShippedPresets = 0
var defaultXML = 0
var customXML = 0
var lastUsedXML = 0
var allPresetsXML = 0
var dvar = new Object()

var win // needs to be defined outside main

//alert (app.locale);sav
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Preview
var clone = function(obj)
{
    if (typeof obj == "xml")
        return new XML(obj.toString())
        
   if (obj == null || typeof obj != "object")
        return obj
        
    if (obj.constructor != Object && obj.constructor != Array) 
        return obj
    
    if (obj.constructor == Date || obj.constructor == RegExp || 
        obj.constructor == Function || obj.constructor == String ||
        obj.constructor == Number || obj.constructor == Boolean)
        return new obj.constructor(obj)

    var res = new obj.constructor()

    for (var name in obj)
    {
        res[name] = typeof res[name] == "undefined" ? clone(obj[name]) : obj[name]
    }

    return res
}


previewParameters = clone (modelParameters)


restart = false
previewRunning = false


var screenSize = RenderAPI.getParameter(kpsScreenSize)
//alert("screen height = " + screenSize.y)
var maxHeight = screenSize.y

var previewSize = (typeof previewSize == 'undefined') ? 400 : previewSize; // can be overided by the main script
if (previewSize + 85 > maxHeight)
    previewSize = maxHeight - 85

previewAPI = RenderAPI.getParameter (kpsPreviewRenderer, previewSize, previewSize)
//if (!previewAPI.getParameter (kpsUseOpenGL))
//    isPreview = false

previewAPI.command (kpsFreezeProgress)  // freeze the progress bar
    
var _patternSize = typeof pattern != 'undefined' ? pattern.getParameter(kpsSize) : {x: 256/4, y: 256/4 };
var _maxPatternSize = Math.max ( _patternSize.x, _patternSize.y)
var inSetUIvar = false

function update (parameters)
{
    if (inSetUIvar) // don't call preview if update methods are triggered by setting values (e.g. on start or when presets are loaded)
        return
        
     var previewScale = 1
    
    if (_maxPatternSize < 256 / 5)       // Script could overwrite this
       previewScale = Math.ceil (256 / 5 / _maxPatternSize)
    else if (_maxPatternSize > 256 / 3)
        previewScale = 256 / 3 / _maxPatternSize
     
     if (previewZoom)
        previewScale *= parameters.previewScaleFactor
     
    //var useOpenGLinRegularRender = 0
    
    //if (typeof pattern != 'undefined')
    //{
    //    useOpenGLinRegularRender = pattern.getParameter (kpsUseOpenGL) 
    //    pattern.setParameter (kpsUseOpenGL, 1) // we use OpenGL in preview
    //}
    if (previewRunning)
    {
        return; // preview is not reentrant
    }
    
    previewAPI.command (kpsStartWatchCursor)
    previewRunning = true
    do {
        restart = false
        run (previewAPI, parameters, previewScale)
        if (restart)
        {
            previewAPI.command(kpsClear)
            Engine.clearModules()
        }
   } while (restart)
   if (typeof previewImagePath != 'undefined' && (typeof updatePreview == 'undefined' || updatePreview))
    {
        //alert('saving file ' + previewImagePath)
        previewAPI.command (kpsSaveToFile, previewImagePath)
    }
    previewAPI.command(kpsClear)
    Engine.clearModules()
    
    previewAPI.command (kpsStopWatchCursor)
    previewAPI.loadIdentity(); // set for the possible next run
         
    previewRunning = false
     
    //alert ("File saved")
    //previewAPI.popMatrix();
    //if (typeof pattern != 'undefined')
    //    pattern.setParameter (kpsUseOpenGL, useOpenGLinRegularRender) // restore original value
    //win.notify("onDraw")
}

var decimalPt = $.decimalPoint // decimal point for the current locale
    
function replaceDecimalPt(str, dp1, dp2)
{
    //return str // do not replace the decimal point yet - Photoshop doesn't seem to do that either so let's be consistent
    
    //if (dp2 != ".")
    //    numberTable[dp1] = dp2
        
    // we allow only one non-digit character - a decimal point
    var newStr =  ""
    str = str.toString()
   
    var dpPresent = false;
    for (var i = 0; i < str.length; i++)
    {
        var c = str[i] == dp1 ? dp2 : str[i]
        var cc = c.charCodeAt(0)
        
        // replace hindi and arabic numerals
        if (cc >= 1632 && cc <= 1641)
            cc = 48 + cc - 1632
        if (cc >= 1776 && cc <= 1785)
            cc = 48 + cc - 1776
         
        newStr += String.fromCharCode(cc)
         
         // we allow '-' (minus) at the beginning, decimalPoint and a number
         if (newStr[i] == dp2 /*|| newStr[i] == '.' */)
         {
             if (dpPresent)
                return str // we cannot have more than one decimal point
             dpPresent = true;
             continue;
         }
         if (isNaN(Number(newStr[i])) && (newStr[i] != '-' || i > 0))
         {
             return str
         }
    }
    return newStr
 }

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

var forceUsingModelParameters = false

// check if we are playing an action
var actionParameters = RenderAPI.getParameter (kpsParameterString)
var newModelParameters
var modelParametersBackup

if (actionParameters)
{
       modelParametersBackup = clone (modelParameters) 
       
       if (actionParameters != " " && actionParameters != "multiple")
        {
            
            newModelParameters = eval (actionParameters)
            // Merge the parameters and test them
            for (var pI = 0; pI < menu.panels.length; pI++)
            {
                for (var mI = 0; mI < menu.panels[pI].panelMenu.length; mI++)
                {
                    var currMenuItem = menu.panels[pI].panelMenu[mI]
                    var varName = currMenuItem.varName;
                    if (typeof modelParameters[varName] == 'undefined' && typeof newModelParameters[varName] == 'undefined')
                        continue; // This parameter is not defined in either of the parameter sets
                    if (typeof newModelParameters[varName] != 'undefined')
                    {
                        //alert("setting modelParameters[" + varName + "] = " + newModelParameters[varName])
                        modelParameters[varName] = newModelParameters[varName]
                    }
 
                    // test validity of the value
                    switch (currMenuItem.itemType)
                    {
                        case 'edittext':
                            // anything goes
                            break;
                            
                        case 'slider':
                            if (isNaN(modelParameters[varName]))
                                modelParameters[varName] = currMenuItem.itemValue
                            else
                            {
 
                                if (modelParameters[varName] < currMenuItem.itemMin)
                                    modelParameters[varName] = currMenuItem.itemMin
                                if (modelParameters[varName] > currMenuItem.itemMax)
                                    modelParameters[varName] = currMenuItem.itemMax
                            }
                            break;
                            
                        case 'colorpicker':
                            if (modelParameters[varName].constructor != Array)
                                modelParameters[varName] = currMenuItem.itemValue
                            else
                                for (var i = 0; i < 3; i++)
                                    if (modelParameters[varName][i] < 0 )
                                        modelParameters[varName][i] = 0
                                    else if (modelParameters[varName][i] > 1 )
                                        modelParameters[varName][i] = 1
                            break;
                            
                        case 'dropdownlist':
                            if (isNaN(modelParameters[varName]))
                                modelParameters[varName] = currMenuItem.itemValue
                            if (modelParameters[varName]  < 0)
                                modelParameters[varName] = 0
                            else if (modelParameters[varName]  >= currMenuItem.itemList.length)
                                modelParameters[varName] = currMenuItem.itemList.length - 1
                            break;

                        case 'checkbox':
                            if (modelParameters[varName] != false && modelParameters[varName] != true)
                                modelParameters[varName] = currMenuItem.itemValue
                            break;
                    }                  
                }
            }
        }
        skipRun = (typeof Window == 'undefined') ? true: false // skip the run if the window cannot be open (it may be a sign that graphics has limited capabilities)
        
        if ( RenderAPI.getParameter (kpsShowDialog))
        {
            app.bringToFront();
            forceUsingModelParameters = true
            newModelParameters = modelParameters
            modelParameters = modelParametersBackup // in case the user selects default
            main();
        }
}
else
{
    // check if we can create a window - extendscript fails to define a window in 32 bit version on some integrated graphics cards
    if (typeof Window == 'undefined')
    {
        alert(localize( "$$$/DecoScripts/DecoMenu/CannotOpenWindow=Cannot open scripted pattern dialog. Default values will be used."));
        skipRun = false
    }
    else
    {
        app.bringToFront();
        main();
    }
}


function outputResult (prefix)
{
    // output the variables 
    var outputText = "";
    var problem = false;
    for (var pI = 0; pI < menu.panels.length; pI++)
    {
        var currPanel = menu.panels[pI]
        for (var mI = 0; mI < currPanel.panelMenu.length; mI++)
        {
            var currMenu = menu.panels[pI].panelMenu[mI]
            var var1 = win.panels[pI].menu[mI].var1
            if (currMenu.itemType == 'dropdownlist')
            {
                    if (var1.selection < 0 || var1.selection >= currMenu.itemList.length)
                    {
                        alert(strDropdownListSelectionIcorrect + ": " + currMenu.itemName) //the alert strings is localized above
                        problem = true
                    }
                    else
                        outputText += prefix + currMenu.varName + "=" + var1.selection + ";\n"
            }
            else if (currMenu.itemType == 'checkbox')
            {
                outputText += prefix + currMenu.varName + "=" + var1.value + ";\n"
            }
            else if (currMenu.itemType == 'edittext')
            {
                if (typeof currMenu.itemMin != undefined && currMenu.itemMin != undefined)
                {
                    // edit text will contanin a number
                    var num = replaceDecimalPt(var1.text, decimalPt, '.')
                    if(var1.text == '')
                    {
                       alert (strEntryCannotBeBlank + " : " + currMenu.itemName);
                        problem = true;
                    }
                    else if(isNaN(num ))
                    {
                        alert(localize( "$$$/DecoScripts/DecoMenu/NotAnNumber=Your entry is not a number for menu item: ") + currMenu.itemName);
                        problem = true;
                    } 
                    else if (num < currMenu.itemMin)
                    {
                        alert(localize( "$$$/DecoScripts/DecoMenu/YourValueOf=Your value of ") +var1.text + 
                                localize( "$$$/DecoScripts/DecoMenu/CannotBeSmaller= cannot be smaller than ") + currMenu.itemMin + 
                                localize( "$$$/DecoScripts/DecoMenu/ForMenuItem= for menu item: ") + currMenu.itemName);
                        problem = true;
                    }                         
                    else if (num > currMenu.itemMax)
                    {
                        alert(localize( "$$$/DecoScripts/DecoMenu/YourValueOf=Your value of ") + var1.text + 
                                localize( "$$$/DecoScripts/DecoMenu/CannotBeLarger= cannot be larger than ") + currMenu.itemMax + 
                                localize( "$$$/DecoScripts/DecoMenu/ForMenuItem= for menu item: ") + currMenu.itemName);
                        problem = true;
                    }         
                    else
                        outputText += prefix + currMenu.varName + "=" + num+ ";\n"
                }
                else
                        ouputText += prefix + currMenu.varName + "=" + var1.text + ";\n"
            }
            else if (currMenu.itemType == 'slider')
            {
                  outputText += prefix + currMenu.varName + "=" + Math.round (var1.value / var1.valStep) * var1.valStep + ";\n"
            }
            else if (currMenu.itemType == 'colorpicker')
            {
                var number = parseInt(var1.value, 16) // convert hex string to int
                var red = Math.floor(number / 0x10000)
                number -= red * 0x10000
                var green = Math.floor( number / 0x100)
                var blue = number - green * 0x100
                
                outputText += prefix + currMenu.varName + "= [" + red / 255+ ", " + green / 255 + ", " + blue / 255+ "];\n"
            }
        }
     }
 
    if (!problem)
    {
        //alert (outputText)
        eval (outputText)
    }
    if (problem)
        alert (localize( "$$$/DecoScripts/DecoMenu/ProblemInOutputResult=Problem in outputResult"))
    
    return problem ? 0 : 1;
 }

function doPreview (parent)
{
    outputResult('previewParameters.') // update previewParameters
    update(previewParameters)
    //alert('after update')
    if (typeof win != undefined && typeof win.previewImage != "undefined" && (typeof updatePreview == 'undefined' || updatePreview))
    {
        win.previewImage.image = previewImagePath // updates the image
        win.previewImage.enabled = true
    }
    //win.previewPanel.updateBtn.enabled = false
    //if (typeof parent != 'undefined')
    //alert ("doPreview old " + parent.var1.oldValue + " new " + parent.var1.value)
    parent.var1.oldValue = parent.var1.value
 
return;
   
    if (isLivePreview)
    {
        //eval ('previewParameters.' + parent.var1.decoVarName + "=" + parent.var1.value)
        if (previewRunning)
        {
            restart = true
            //alert("restart");
        }
        else
        {
            update(previewParameters)
            win.previewImage.image = previewImagePath; // updates the image
            win.previewImage.enabled = true
        }
    }
    else
    {
        win.previewImage.enabled = false
        if (typeof win.previewPanel.updateBtn != "undefined")
            win.previewPanel.updateBtn.enabled = true 
    }
    //if (typeof parent != 'undefined')
        parent.var1.oldValue = parent.var1.value
}

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

function getTextWidth(parent, text)
{
    var group = parent.add ('group', undefined)
    var test = group.add('statictext', undefined, text)
    test.preferredSize = [-1, -1]
    group.layout.layout(true)
    //alert (test.preferredSize[0] + ", " + test.preferredSize[1])
    var width = test.preferredSize[0]
    parent.remove (group)
    return width
}


function enableMenuItem(pI, mI, enable, depth)
{
    //alert ('called enableMenu(' + pI + ", " + mI + ", " + enable + ", " + depth + ")")
    if (win.panels[pI].menu[ mI ].var1.enabled == enable)
        // no change
        return;
    
    win.panels[pI].menu[ mI ].var1.enabled = enable
    if (typeof win.panels[pI].menu[ mI ].st1 != 'undefined')
        win.panels[pI].menu[ mI ].st1.enabled = enable 
    if (typeof win.panels[pI].menu[ mI ].ut != 'undefined')
        win.panels[pI].menu[ mI ].ut.enabled = enable 
    if (typeof win.panels[pI].menu[ mI ].valt != 'undefined')
        win.panels[pI].menu[ mI ].valt.enabled = enable 
    if (typeof win.panels[pI].menu[ mI ].var1.backgroundColor != 'undefined') // for colorpicker
         setSwatchColor (win.panels[pI].menu[ mI ].var1, enable ? win.panels[pI].menu[ mI ].var1.value : '0xe0e0e0')

    if (depth < 4) // to prevent infinite loop
        updateDisableElements (win.panels[pI].menu[ mI ].var1, depth+1, enable)
}
 
 
 function updateDisableElements (var1, depth, enable)
{
    var disabledElements = new Array(0)
    // first enable, then disable (below)
    {
        // enable all items if there were some disabled last time - or store the values???
        for (var n = 0; n < var1.disabledElements.length; n+=2)
        {
            // We still need to skip the first element if the disabled elements araey contains a string
            var j = isNaN(parseFloat(var1.disabledElements[n+1][0])) ? 1 : 0
             for (; j < var1.disabledElements[n+1].length; j++)
            {
                enableMenuItem(var1.disabledElements[n], var1.disabledElements[n+1][j], true, depth)
            }
        }
        var1.disabledElements = new Array(0)
    }
    if (enable && var1.disableItems)
    {
        // gray out some menu items, if requested
       for (var i = 0; i < var1.disableItems.length; i++)
            if (var1.disableItems[i][0] == var1.value)
            {
                //alert ('gray out elements ' + this.disableItems[i][1])
                // there could be more than one array
                for (var n = 1; n < var1.disableItems[i].length; n++)
                {
                    var panelI = var1.pI
                    var firstItem = 0
                     // check whether the first item is a string (panel name) or not (current panel)
                    if (isNaN(parseFloat(var1.disableItems[i][n][0])))
                    {
                        // find the panel
                        var fp
                        for (fp = 0; fp < win.panels.length; fp ++)
                        {
                           if (win.panels[fp].name == var1.disableItems[i][n][0])
                           {
                               panelI = fp
                                break
                            }
                        }
                        if (fp == win.panels.length)
                        {
                           // panel name not found
                           continue; // go to the next array of disableItems (for(n ... above)
                         }
                         firstItem = 1
                     }
                     for (var j = firstItem; j < var1.disableItems[i][n].length; j++)
                     {
                        //alert("calling enableMenuItem(" + panelI + ", " + var1.disableItems[i][n][j] + ", false, 0)")
                        enableMenuItem(panelI, var1.disableItems[i][n][j], false, depth)
                     }
                    disabledElements.push(panelI)  // store the panel index
                    disabledElements.push(var1.disableItems[i][n]) // and the array of disable items for that panel
               }
            }
    }
    var1.disabledElements = disabledElements
}


function GetPanelSelection()
{
    return win.tabbedPanel.selection.text
}

function main()
{
    var updated=false;
    if(!documents.length || !menu || menu.panels.length == 0) return;

    // Define the menu window
    win = new Window( 'dialog', menu.menuTitle );
    win.margins = [10,10,10,10] // left top right bottom
    win.paramsOK = true

    //alert ("size = " + win.maximumSize)
    //win.preferredSize =  [4000, 4000]
    //win.layout.layout(false)
    //alert ("size2 = " + win.preferredSize);
    
    g = win.graphics;    
    var myBrush = g.newBrush(g.BrushType.THEME_COLOR, "appDialogBackground");
    
    win.g0 = win.add('group');
    win.g0.orientation = "column";
    //win.title = win.g0.add('statictext',undefined,menu.menuTitle);
    //win.title.alignment="center";
    //var g = win.title.graphics;
    //g.font = ScriptUI.newFont("Georgia","BOLDITALIC",18);
    
    win.p0 = win.g0.add('panel', undefined, '', {borderStyle:"none"});
    win.p0.margins = [0,0,0,0]
    win.p0.orientation = "row"; 
    win.p0.alignChildren = ['left','top']
   
    if (isPreview /*&& uiPreviewImage.exists */)
    {
        //win.previewPanel = win.p0.add('panel', undefined, 'Preview', {borderStyle:"black", backgrounColor : "white"});
        win.previewPanelBase = win.p0.add('panel', undefined);
        win.previewPanel = win.previewPanelBase.add('panel', [0,0,previewSize+4,previewSize+4], '', {borderStyle:"none"});
        g = win.previewPanel.graphics;
        myBrush = g.newBrush(g.BrushType.SOLID_COLOR, (typeof menu.previewBackground != 'undefined' ) ? menu.previewBackground : [0.94, 0.94, 0.94, 1]);
        g.backgroundColor = myBrush;
   }
       
    win.g1 = win.p0.add('panel', undefined, '', {borderStyle:"none"});
    win.g1.margins = [0,0,0,0];
    win.g1.orientation = "column"; 
    win.g1.alignChildren = ['left','top'];
     
       win.presetPn = win.g1.add('panel',undefined,'',{borderStyle:"none"});
       win.presetPn.margins = [0,0,0,0];
       win.presetPn.orientation = 'row';
       win.presetPn.alignChildren = ['left','middle'];
        
        //win.presetPn.st1 = win.presetPn.add('statictext', undefined, localize( "$$$/DecoScripts/DecoMenu/Preset=Preset:"));
        //win.presetPn.st1.margins = [0,0,0,0]
        dvar.presetsDbx = win.presetPn.add('dropdownlist',undefined,presetList)
        g = dvar.presetsDbx.graphics;
        myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 1, 1, 1]);
        //g.backgroundColor = myBrush;
        dvar.presetsDbx.margins = [0,0,0,0]
        dvar.presetsDbx.selection=0
        var presetsWidth = typeof menu.presetsDrowpdownlistWidth != 'undefined' ? menu.presetsDrowpdownlistWidth :  200
        var width = getTextWidth (win.g1, localize("$$$/DecoScripts/ScriptMenu/Preset=Preset: ") + localize("$$$/DecoScripts/ScriptMenu/Default=Default"))
        if (width + 30 > presetsWidth)
            presetsWidth = width + 30
        width = getTextWidth (win.g1, localize("$$$/DecoScripts/ScriptMenu/Preset=Preset: ") + localize("$$$/DecoScripts/ScriptMenu/Custom=Custom"))
        if (width + 30 > presetsWidth)
            presetsWidth = width + 30
       dvar.presetsDbx.size = [presetsWidth, 20]
        
        dvar.presetsDbx.onActivate = function()
        {
            //alert("onActivate")
            this.insideActivate = true
            this.selection =  0
           
           // if (this.selection2 == 0) // Default
           //     this.selection = 1
           // else if (this.selection2 == 1) // Custom
           ///     this.selection = 3 + (presetsXML.length > 0 ? presetsXML.length + 1 : 0) + 4
           // else if (this.selection2 >= 2)
            //    this.selection = 1 + this.selection2
            this.insideActivate = false
        }  
        dvar.presetsDbx.insideActivate = false
        
        dvar.presetsDbx.onChange = function()
        {
            if (this.insideActivate || this.insideSetPresetList) // don't make any changes if we are in onActivate method or in setPresetList method
                return
            //alert('onChange called ' + this.selection)
            var sel = this.selection
            // selection 0 is Default
            // selection 1 is last selected (or Default the very first time)
            // selection 2 is separator
            // if there are shipped presets
            //      selection 3-numShippedPresets is shipped presets 
            //      selection 3+numShippedPresets is a separator
            
            // we adjust the selection so that  the separator between shippedPresets and user's presets is not counted, since presetsXML arrey is continuous
            if (numShippedPresets > 0 && presetsXML.length  > numShippedPresets)
            {
                if (sel == 3 + numShippedPresets)
                    // separator, return
                    return
                if (sel > 3 + numShippedPresets)
                    sel--
            }
            // if there are user presets (presetsXML.length  > numShippedPresets)
            //      selection 3+ < numShippedPresets, presets.XML.lengh> are user's presets
            //      followed by separator
            // posMenu: here is where the menu Load/Save presets starts 
            var posMenu = 3 ;
            if (presetsXML.length > 0)
                posMenu += presetsXML.length + 1  // plus one separators, we already adjusted for the possible middle separator above
                
            //alert(posMenu + ", " + presetsXML.length)
            if (sel == posMenu + 4)
            {
                    if (dvar.presetsDbx .selection2 != 1)
                    {
                        menuApplyPreset(customXML)
                        setPresetList(1)
                    }
                    else
                        this.selection = 0
            }
            else if (sel == 1)
            {
                    if (dvar.presetsDbx .selection2 != 0)
                     {
                        //alert ('calling setPresetList(0)')
                        menuApplyPreset(defaultXML)
                        setPresetList(0)
                     }
                    else
                        this.selection = 0
           }
            else if (presetsXML.length > 0 && sel - 3 >= 0 && sel - 3 < presetsXML.length)
            {
                 if (dvar.presetsDbx .selection2 != sel - 1)
                 {
                    sel --
                    menuApplyPreset(presetsXML[sel - 2])
                    setPresetList(sel)
                }
                else
                    this.selection = 0
            }
            else if (sel == posMenu)
            {
                // load preset
                setPresetList (menuLoadPreset())
          }
            else if (sel == posMenu + 1)
            {
                    // save preset
                     setPresetList (menuSavePreset())
            }
            else if (sel == posMenu + 2)
            {
                   // delete preset
                   setPresetList (menuDeletePresets())
             }
            //alert ('on change, selection ' + this.selection)
        }  
    
        menuApplyPreset = function(xml)
        {
            //alert ('setting values to ' + xml)
            var storePreview = isPreview
            isPreview = false  // so that on changing methods are not triggering update
            setUIvar(xml,win)

             // call onClick for all checkboxes (to update disabledItems, if there are any)
            for (var pI = 0; pI < win.panels.length; pI++)
            {
                var currPanel = win.panels[pI]
                for (var mI = 0; mI < currPanel.menu.length; mI++)
                     if (currPanel.menu[mI].itemType == 'checkbox')
                            win.panels[pI].menu[mI].var1.onClick() 
              }
            isPreview = storePreview
 
            if (1 || !isLivePreview)
            {
                outputResult('previewParameters.') // update previewParameters
                update(previewParameters)
                win.previewImage.image = previewImagePath; // updates the image
                win.previewImage.enabled = true
                if (typeof win.previewPanel.updateBtn != "undefined")
                    win.previewPanel.updateBtn.enabled = false
            }
        }

        menuSavePreset = function()
        {
            var fileD = new File(decoPresetsFolder)
            var file = fileD.saveDlg ( localize( "$$$/DecoScripts/DecoMenu/SavePresetAs=Save preset as an xml file"), "*.xml")
            var xml
            if (file == null)
            {
                // saving was cancelled
                return dvar.presetsDbx .selection2
            }
            if (dvar.presetsDbx .selection2 == 0) // Default
                xml = clone(defaultXML)
            else if (dvar.presetsDbx .selection2 == 1) // Custom
            {
                // update the xml with current values
                setXML (customXML, win, 0)
                 xml = clone(customXML)
            }
            else if (dvar.presetsDbx .selection2 >= 2)
               xml = clone(presetsXML[dvar.presetsDbx .selection2-2])
           
           xml.preset.@presetName = File.decode (file.name)
           var dotInName = file.name.lastIndexOf('.')
           if (dotInName > 0)
                xml.preset.@presetName = File.decode (file.name.substr(0, dotInName));
           else 
                xml.preset.@presetName = File.decode (file.name);
           
           if (xml.preset.@presetName == 'Custom' /* && file.fsName == fileD.fsName */)
           {
                alert (localize( "$$$/DecoScripts/DecoMenu/CannotOverwriteCustom=You cannot use the Custom preset name. Choose a different name."))
                return dvar.presetsDbx .selection2
           }
           if (xml.preset.@presetName == 'Default' /* && file.fsName == fileD.fsName */)
           {
                alert (localize( "$$$/DecoScripts/DecoMenu/CannotOverwriteDefault=You cannot use the Default preset name. Choose a different name."))
                return dvar.presetsDbx .selection2
           }
 
           return addNewXML (xml, false /* don't test overwrite */) // write also to presets - so that it shows up the next time we use this script
        }
        
        menuLoadPreset = function()
        {
            //var fileD = new File(decoPresetsFolder)
            var file = File.openDialog ( localize( "$$$/DecoScripts/DecoMenu/LoadPresetFrom=Choose a preset file"), File.fs == 'Macintosh' ? function(f) { return f.name.slice(-4) == '.xml' } : "*.xml")
            if (file == null)
            {
                // loading was cancelled
                return dvar.presetsDbx .selection2
                }
                
            var xml = readFile(file)
            if (xml.preset.@presetName == 'Custom' /* && file.fsName == fileD.fsName */)
           {
                //alert (localize( "$$$/DecoScripts/DecoMenu/CannotLoadCustom=You cannot load a preset with name 'Custom'."))
                return 1 //dvar.presetsDbx .selection2
           }
           if (xml.preset.@presetName == 'Default' /* && file.fsName == fileD.fsName */)
           {
                //alert (localize( "$$$/DecoScripts/DecoMenu/CannotLoadDefault=You cannot load a preset with name 'Default'."))
                return 0 //dvar.presetsDbx .selection2
           }
          menuApplyPreset(xml)
          return addNewXML(xml, true /* test overwrite */)
        }
    
        addNewXML = function(xml, testOverwrite)
        {
            // check if preet name already exists - in fact find the highest number in case we have already added (n) to the name
            //alert (xml)
            var highestNumber = -1
            var highestIndex = -1
            var lowestIndex = -1
            for (var i = 0; i < presetsXML.length; i++)
            {
                var storedName = presetsXML[i].preset.@presetName.toString()
                var index = storedName.lastIndexOf('-')
                 if (index > 1 && storedName.slice(0, index) == xml.preset.@presetName)
                 {
                    // get the index after the name
                    var num = Number(storedName.slice(index+1,storedName.length))
                     //alert('num = ' + num)
                     if (num > highestNumber)
                     {
                         highestNumber = num
                         highestIndex = i
                     }
                }
                else if (storedName == xml.preset.@presetName)
                {
                    lowestIndex = i
                    if (highestNumber < 0)
                    {
                        highestNumber = 0
                        highestIndex = i
                    }
                }
           }
            
            if (highestIndex >= 0)
            {
                var overwritePre = true
                if (testOverwrite)
                    overwritePre = confirm (localize( "$$$/DecoScripts/DecoMenu/PresetOverwriteQuestion=Adding preset to menu - preset already exists. Do you want to overwrite the preset ") + ' "' + 
                                                        removePercent20 (xml.preset.@presetName) + '"' +
                                                        localize("$$$/DecoScripts/DecoMenu/QuestionMark=?"), true)

                if (overwritePre)
                {
                    presetsXML[lowestIndex] = xml
                    return 2+lowestIndex
                 }
                 else
                 {
                    xml.preset.@presetName =  xml.preset.@presetName + (-(Number(highestNumber) +1)) 
                 }
            }

            presetsXML.push(xml)
            writeXMLFile (xml)
            return 2 + presetsXML.length - 1
        }
        
        menuDeletePresets = function()
        {
            if(dvar.presetsDbx.selection2 == 0) 
            { 
                alert (localize( "$$$/DecoScripts/DecoMenu/CannotDeleteDefault=You cannot delete the Default preset"))
            }
            else if(parseInt(dvar.presetsDbx.selection2) == 1)
            { 
                 alert (localize( "$$$/DecoScripts/DecoMenu/CannotDeleteCustom=You cannot delete the Custom preset"))
            }
            else
            {
                var delPre = confirm (localize( "$$$/DecoScripts/DecoMenu/DoYouWantToDelete=Do you want to delete the preset ") + '"' + 
                                                 removePercent20 (presetsXML[parseInt(dvar.presetsDbx.selection2) - 2].preset.@presetName) + '"?', 
                                                 localize( "$$$/DecoScripts/DecoMenu/=Yes"), localize( "$$$/DecoScripts/DecoMenu/DeletePreset=Delete Preset"))
                if(delPre)
                {
                    var index = parseInt(dvar.presetsDbx.selection2) - 2
                    deleteXMLFile (presetsXML[index])
                    presetsXML.splice(index,1) 
                    return 1 // set to custom  //XXX although if we save the custom preset before changuing anything - it may have the original custom values
                }
            }
            return dvar.presetsDbx.selection2
        }


     win.panels = new Array()
     win.tabs = new Array()
     
     if (menu.panels.length > 1)
     {
         // make tabbed panels
         win.g1.spacing = 5
         win.tabbedPanel = win.g1.add ('tabbedpanel')
         win.tabbedPanel.margins = [0,0,0,5]
         for (var pI = 0; pI < menu.panels.length; pI++)
         {
            win.tabs[pI] = win.tabbedPanel.add('tab', undefined, menu.panels[pI].panelName, {borderStyle:"none"})
            win.tabs[pI].margins = [0,0,0,0]
            win.tabs[pI].spacing = 5
            win.tabs[pI].orientation = "column"; 
            win.tabs[pI].alignChildren = ['left','top'] 
         }
		 win.tabbedPanel.selection = win.tabs[0];
    }
    else
        win.tabs[0] = win.g1
        
	var panelWidth = 0;
    for (var pI = 0; pI < menu.panels.length; pI++)
    {
        var currPanel = menu.panels[pI]
        currPanel.leftColumnWidth = typeof currPanel.leftColumnWidth != 'undefined' ? currPanel.leftColumnWidth : 180
        currPanel.editTextWidth = typeof currPanel.editTextWidth != 'undefined' ? currPanel.editTextWidth : 46
        currPanel.unitsWidth = typeof currPanel.unitsWidth != 'undefined' ? currPanel.unitsWidth :  65
        currPanel.dropdownlistWidth = typeof currPanel.dropdownlistWidth != 'undefined' ? currPanel.dropdownlistWidth :  160
        
        var height1 = 44 // slider
        var height2 = 32
        var height3 = 30
        var height4 = 25 // checkbox
        var height5 = 22
        if (0)
        {
            height1 = 32
            height2 = 20
            height3 = 19
            height4 = 18
            height5 = 17
         }
       
        // check if  the widths are sufficient
        // also computes the height of the menu
        var textMargin = 5
        var height = 140
        var num3 = 0;
        var maxDropdownWidth = 0
        for (var mI = 0; mI < currPanel.panelMenu.length; mI++)
        {
            var textWidth = getTextWidth (win.tabs[pI], currPanel.panelMenu[mI].itemName)
            if (textWidth + textMargin > currPanel.leftColumnWidth)
                currPanel.leftColumnWidth = textWidth + textMargin 
            
            if (typeof currPanel.panelMenu[mI].itemUnit != undefined)
            {
                var itemWidth = getTextWidth (win.tabs[pI], currPanel.panelMenu[mI].itemUnit)
                if (itemWidth + textMargin > currPanel.unitsWidth)
                    currPanel.unitsWidth = itemWidth + textMargin
             }
         
            var currMenu = currPanel.panelMenu[mI];
             if (currMenu.itemType == 'dropdownlist')
             {
                    var itemWidth = currPanel.dropdownlistWidth  // default or user specified
                    currMenu.dropdownlistWidth = currPanel.dropdownlistWidth  // it would be ugly to have all dropdown lists long because one has long items, store it per menu item
                    
                    // go over all dropdownlist items and determine the maximum string length
                    for (var ii = 0; ii < currMenu.itemList.length; ii++)
                    {
                        if (typeof currMenu.itemList[ii] == 'object')
                        {
                            // some items store both the item name and path to a item icon
                            if (typeof currMenu.itemList[ii].item == 'string')
                               itemWidth = getTextWidth (win.tabs[pI], currMenu.itemList[ii].item)
                        }
                        else
                        {
                            itemWidth = getTextWidth (win.tabs[pI], currMenu.itemList[ii])
                        }
						
                        // we have to add width of the selection arrow
					    itemWidth += 25
                       
                        if (currMenu.dropdownlistWidth < itemWidth)
                            currMenu.dropdownlistWidth = itemWidth
                        if (itemWidth + textWidth + textMargin > maxDropdownWidth) // width of the whole line
                            maxDropdownWidth = itemWidth + textWidth + textMargin + 30;
                    }
            }
         
             if (currMenu.itemType == 'slider')
                height += height1 //+ height3+2
           else if (currMenu.itemType == 'checkbox')
                height += height4 + 2
           else
           {
                height += height3 + 2
                num3 ++
            }
     }
  
      //alert("height before is " + height)
      
      if (height > maxHeight)
      {
          var diff = Math.ceil((height - maxHeight) / currPanel.panelMenu.length) 
          //alert("diff = " + diff + " num3 = " + num3)
          height1 -= diff;
          height2 -= diff;
          if (height3 - diff < 20)
          {
              var h2 = num3 * (20 - (height3 - diff)) // we have to distribute this amount
              var diff2 = Math.ceil(h2 / (currPanel.panelMenu.length - num3)) 
              height1 -= diff2;
              height2 -= diff2;
              height3 = 20;
          }
          else
             height3 -= diff;
             
          height4 = Math.max(height4 - diff, 18);
          height5 = Math.max(height5 - diff, 18);
        }  
    
        currPanel.panelWidth = currPanel.leftColumnWidth + currPanel.editTextWidth + 7 + currPanel.unitsWidth + 17;
        if (currPanel.panelWidth < maxDropdownWidth)
            currPanel.panelWidth = maxDropdownWidth
   
		if (panelWidth < currPanel.panelWidth)
			panelWidth = currPanel.panelWidth
		else
			currPanel.panelWidth = panelWidth
		
        if (menu.panels.length == 1)
            win.panels[pI] = win.g1.add('panel', undefined, currPanel.panelName, {borderStyle:"none"});
        else
        {
              win.panels[pI] = win.tabs[pI].add('panel', undefined, '', {borderStyle:"none"})
              //alert("num panels " + win.panels.length)
        }
        win.panels[pI].margins = [0,3,0,0]
        win.panels[pI].spacing = 5
        win.panels[pI].name = currPanel.panelName
        //win.panels[pI].orientation = 'stack'
        win.panels[pI].alignChildren = ['left','top'];  

        // create menu items in a panel
        win.panels[pI] .menu = new Array()
        for (var mI = 0; mI < currPanel.panelMenu.length; mI++)
        {
            var currMenu = currPanel.panelMenu[mI]
           
            win.panels[pI].menu[mI]  = win.panels[pI].add('group', undefined) // '', {borderStyle:"none"})
            win.panels[pI].menu[mI].margins = [0,0,2,0]
            if (currMenu.itemType == 'slider')
                win.panels[pI].menu[mI].size = [currPanel.panelWidth, height1]
           else if (currMenu.itemType == 'checkbox')
                 win.panels[pI].menu[mI].size = [currPanel.panelWidth, height4]
           else if (currMenu.itemType == 'dropdownlist')
                 win.panels[pI].menu[mI].size = [currPanel.panelWidth, 32]
           else
                win.panels[pI].menu[mI].size = [currPanel.panelWidth, height3]
            win.panels[pI].menu[mI].orientation = 'stack'
            win.panels[pI].alignChildren = ['left','top'];  
            win.panels[pI].menu[mI].alignment='top';
            win.panels[pI].menu[mI].spacing=0;
            
            // display the menu item
			if (currMenu.itemType != 'checkbox')
			{
				// Use an extra 2 groups so that we can have it right aligned (I didn't figure out a better method for doing this)
				win.panels[pI].menu[mI].gr1 = win.panels[pI].menu[mI].add('group', undefined, '') 
				win.panels[pI].menu[mI].gr1.margins = [0,0,0,0]
				win.panels[pI].menu[mI].gr1.alignment = ['left','top'];
				if (currMenu.itemType == 'dropdownlist')
					win.panels[pI].menu[mI].gr1.size=[currPanel.panelWidth - currMenu.dropdownlistWidth - 30, 22];
				else
					win.panels[pI].menu[mI].gr1.size=[currPanel.leftColumnWidth, 22];
				win.panels[pI].menu[mI].gr1.alignChildren = ['left','top'];  
            
				win.panels[pI].menu[mI].gr1b = win.panels[pI].menu[mI].gr1.add('group', undefined) 
				if (currMenu.itemType == 'dropdownlist')
				{
					win.panels[pI].menu[mI].gr1b.size=[currPanel.panelWidth - currMenu.dropdownlistWidth - 30, height3];
					win.panels[pI].menu[mI].gr1b.alignment = ['left','top'];
					win.panels[pI].menu[mI].gr1b.margins = [0,0,0,0]
				}
				else if (currMenu.itemType == 'slider')
				{
					win.panels[pI].menu[mI].gr1b.size=[currPanel.leftColumnWidth, 22];
				}
				else
					win.panels[pI].menu[mI].gr1b.size=[currPanel.leftColumnWidth, height5];
				win.panels[pI].menu[mI].gr1b.alignChildren = ['right','center'];  
				
				if (currMenu.itemType == 'slider')
					win.panels[pI].menu[mI].gr1b.margins = [0,5,0,0]
			}
            win.panels[pI].menu[mI].itemType = currMenu.itemType
            if (currMenu.itemType != 'checkbox')
                win.panels[pI].menu[mI].st1 = win.panels[pI].menu[mI].gr1b.add('statictext', undefined, currMenu.itemName); 
      
            col2width = 30
            
             if (currMenu.itemType == 'checkbox')
            {
                win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].add(currMenu.itemType, [10, 0, currPanel.leftColumnWidth + 40, height4], '  ' + currMenu.itemName);
                win.panels[pI].menu[mI].var1.value = currMenu.itemValue
                win.panels[pI].menu[mI].var1.oldValue=win.panels[pI].menu[mI].var1.value
                win.panels[pI].menu[mI].var1.align = 'left'
                win.panels[pI].menu[mI].var1.disableItems = (typeof currMenu.disableItems != 'undefined') ? currMenu.disableItems : 0
                win.panels[pI].menu[mI].var1.onClick = function()
                {
                    if (!this.enabled)
                        return;
                     updateDisableElements (this, 0, true /* item itself is enabled */)
                     if (isPreview)
                    {
                        if (this.value != this.oldValue)
                        {
                            setPresetList (1)
                            doPreview (this.parent)
                         }
                   }
                 }
           }
            else if (currMenu.itemType == 'slider')
            {               
                //win.panels[pI].menu[mI].gr2.alignChildren = ['center','middle'];  
                //col2width = 0
                //win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].add(currMenu.itemType, undefined, currMenu.itemValue) //[10,20,currPanel.panelWidth-20, 5]
                //win.panels[pI].menu[mI].var1.margins = [0,0,0,0]
                //win.panels[pI].menu[mI].var1.alignment = 'bottom'
                //win.panels[pI].menu[mI].var1.size = [currPanel.panelWidth-20, 32] // higher Y size will move the slider closer to the edt text above it 
                //win.panels[pI].menu[mI].var1.preferredSize=[currPanel.panelWidth-10, 19];
             }
            else if (currMenu.itemType == 'edittext')
            {
                 // edit text
                win.panels[pI].menu[mI].valtgr = win.panels[pI].menu[mI].add('group', undefined)
                win.panels[pI].menu[mI].valtgr.size = [currPanel.panelWidth - (currPanel.leftColumnWidth) - 7 , height1+2]
                win.panels[pI].menu[mI].valtgr.alignChildren = ['left','top'];  
                win.panels[pI].menu[mI].valtgr.alignment = 'right' 
                
                win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].valtgr.add('edittext', undefined, currMenu.itemValue);
                var textWidth = typeof currMenu.editTextWidth != undefined ? currMenu.editTextWidth : currPanel.editTextWidth
                win.panels[pI].menu[mI].var1.size = [textWidth , height5]
                win.panels[pI].menu[mI].var1.alignment = 'top'
                win.panels[pI].menu[mI].var1.oldValue=win.panels[pI].menu[mI].var1.value
 
                //win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].add(currMenu.itemType, [currPanel.leftColumnWidth, 5, currPanel.leftColumnWidth + currPanel.editTextWidth, 25], currMenu.itemValue);
                win.panels[pI].menu[mI].var1.onChange = function()
                {
                    this.value= this.text
                    if (isPreview)
                        if (this.value != this.oldValue)
                        {
                            setPresetList (1)
                            doPreview (this.parent)
                        }
                }
            }
            else if (currMenu.itemType == 'colorpicker')
            {
                 // color picker
                win.panels[pI].menu[mI].valtgr = win.panels[pI].menu[mI].add('group', undefined)
                win.panels[pI].menu[mI].valtgr.size = [currPanel.panelWidth - (currPanel.leftColumnWidth) - 7 , height1+2]
                win.panels[pI].menu[mI].valtgr.alignChildren = ['left','top'];  
                win.panels[pI].menu[mI].valtgr.alignment = 'right' 
                
                win.panels[pI].menu[mI].valtgr2 = win.panels[pI].menu[mI].valtgr.add('group', undefined)  //, '', {borderStyle:"none"})
                win.panels[pI].menu[mI].valtgr2.size = [34 , height4+1]
                win.panels[pI].menu[mI].valtgr2.alignment = 'top' 

                win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].valtgr2.add('button', undefined, '', {borderStyle:"none"});
                win.panels[pI].menu[mI].valtgr2.var1 = win.panels[pI].menu[mI].var1 // because doPreview uses parent.var1
                win.panels[pI].menu[mI].var1.size = [30 , height5]
                win.panels[pI].menu[mI].var1.alignment = 'top'
                
                for (var j = 0 ; j < 3; j++)
                    if (currMenu.itemValue[j] < 0 )
                        currMenu.itemValue[j] = 0;
                    else if (currMenu.itemValue[j] > 1)
                        currMenu.itemValue[j] = 1;
                var hex = Number(Math.floor(currMenu.itemValue[0] * 255 + 0.5) * 0x10000 + 
                                             Math.floor(currMenu.itemValue[1] * 255 + 0.5) * 0x100 + 
                                             Math.floor(currMenu.itemValue[2] * 255 + 0.5) ).toString(16)
                hex = "0x000000".substr(0, 8 - hex.length) + hex
                win.panels[pI].menu[mI].var1.value = hex
                win.panels[pI].menu[mI].var1.oldValue=hex
 
                win.panels[pI].menu[mI].var1.onDraw = drawRGBSwatch;
                win.panels[pI].menu[mI].var1.onClick = clickRGBSwatch;
              
                initializeDrawingState (win.panels[pI].menu[mI].var1, currMenu.itemValue)
   
             }
            else if (currMenu.itemType == 'dropdownlist')
            {
                // dropdownlist
				win.panels[pI].menu[mI].margins = [0,0,15,0];
                win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].add(currMenu.itemType, undefined) //, currMenu.itemList);
                for (var ii = 0; ii < currMenu.itemList.length; ii++)
                {
                    if (typeof currMenu.itemList[ii] == 'object')
                    {
                        if (typeof currMenu.itemList[ii].item == 'string')
                        {
                            var item = win.panels[pI].menu[mI].var1.add ('item', currMenu.itemList[ii].item)
                            if (typeof currMenu.itemList[ii].image == 'string')
                                item.image = scriptPathLocal + '/' + currMenu.itemList[ii].image    // add icon image to the menu item
                        }
                    }
                    else
                    {
                        win.panels[pI].menu[mI].var1.add ('item', currMenu.itemList[ii])
                    }
                 }
                    
                win.panels[pI].menu[mI].var1.size = [currMenu.dropdownlistWidth, height5]
                win.panels[pI].menu[mI].var1.alignment = 'right'
                win.panels[pI].menu[mI].var1.disableItems = (typeof currMenu.disableItems != 'undefined') ? currMenu.disableItems : 0
           }
           else
                alert (localize( "$$$/DecoScripts/DecoMenu/UnregognizedType=Unrecognized menu item type ") + currMenu.itemType)
           // else print some error
           
            if (currMenu.itemType == 'dropdownlist')
            {
                win.panels[pI].menu[mI].var1.oldValue=currMenu.itemValue
                win.panels[pI].menu[mI].var1.selection=currMenu.itemValue
                win.panels[pI].menu[mI].var1.onChange = function()
                {
                     this.value= this.selection
                  
                    if (!this.enabled) // disable other elements only if this element is enable
                        return;
 
                    updateDisableElements (this, 0, true /* item itself is enabled */)
 
                    if (isPreview)
                    {
                        if (this.value != this.oldValue)
                        {
                            setPresetList (1)
                             doPreview (this.parent)
                         }
                    }
                }
             } 
 
             if (currMenu.itemUnit)
             {
                    win.panels[pI].menu[mI].utgr = win.panels[pI].menu[mI].add('group', undefined)
                    win.panels[pI].menu[mI].utgr.size = [currPanel.panelWidth - (currPanel.leftColumnWidth + currPanel.editTextWidth + 12) , 22]
                    win.panels[pI].menu[mI].utgr.alignChildren = ['left','top'];  
					win.panels[pI].menu[mI].utgr.margins = [0,3,0,0]; 
                    win.panels[pI].menu[mI].utgr.alignment = ['right','top'];  
                    //win.panels[pI].menu[mI].gr1b.orientation = 'stack' 
                    
                    win.panels[pI].menu[mI].ut = win.panels[pI].menu[mI].utgr.add('statictext', undefined, currMenu.itemUnit);
                    //win.panels[pI].menu[mI].ut.bounds = [currPanel.leftColumnWidth + col2width, 7, currPanel.leftColumnWidth + col2width + 55, 27]
                    win.panels[pI].menu[mI].ut.alignment = 'right'
					win.panels[pI].menu[mI].ut.size = [currPanel.panelWidth - (currPanel.leftColumnWidth + currPanel.editTextWidth + 12) , 20]
              }

             if (currMenu.itemType == 'slider')
             {
                    //win.panels[pI].menu[mI].st2 = win.panels[pI].menu[mI].add('statictext', [currPanel.leftColumnWidth, 15, currPanel.leftColumnWidth + 40, 35] , currMenu.itemMin);
                    //win.panels[pI].menu[mI].st2.alignment='center';
                    //win.panels[pI].menu[mI].st3 = win.panels[pI].menu[mI].add('statictext', [currPanel.leftColumnWidth + col2width - 10, 15, currPanel.leftColumnWidth + col2width + 30, 35], currMenu.itemMax);
                   
                    //col2width += 30
                   
                    // edit text
                    win.panels[pI].menu[mI].valtgr = win.panels[pI].menu[mI].add('group', undefined)
                    win.panels[pI].menu[mI].valtgr.size = [currPanel.panelWidth - (currPanel.leftColumnWidth) - 7 , 26]
                    win.panels[pI].menu[mI].valtgr.alignChildren = ['left', 'bottom'];  
                    win.panels[pI].menu[mI].valtgr.alignment = ['right', 'top']; 
					win.panels[pI].menu[mI].valtgr.margins = [0,0,0,0];
                    win.panels[pI].menu[mI].valt = win.panels[pI].menu[mI].valtgr.add('editnumber', undefined, currMenu.itemValue, currMenu.itemMin, currMenu.itemMax);
                    win.panels[pI].menu[mI].valt.size = [currPanel.editTextWidth , 22]
                    //win.panels[pI].menu[mI].valt.alignment = 'top';
                   
                    win.panels[pI].menu[mI].slidergr = win.panels[pI].menu[mI].add('group', undefined)
                    win.panels[pI].menu[mI].slidergr.size = [currPanel.panelWidth - 20 , 14]
                    win.panels[pI].menu[mI].slidergr.alignChildren = ['center','bottom'];  
                    win.panels[pI].menu[mI].slidergr.alignment = 'bottom';
					win.panels[pI].menu[mI].slidergr.margins = [0,0,0,10];
                    win.panels[pI].menu[mI].var1 = win.panels[pI].menu[mI].slidergr.add(currMenu.itemType, undefined, replaceDecimalPt(currMenu.itemValue, '.', '.')) // don't replace the decimal point for value in the slider
                    win.panels[pI].menu[mI].var1.margins = [0,0,0,0];
                    win.panels[pI].menu[mI].var1.size = [currPanel.panelWidth-20, 12]; // higher Y size will move the slider closer to the edt text above it 
                    win.panels[pI].menu[mI].var1.valt = win.panels[pI].menu[mI].valt;
                    
                    win.panels[pI].menu[mI].var1.minvalue = currMenu.itemMin;
                    win.panels[pI].menu[mI].var1.maxvalue = currMenu.itemMax;
                    win.panels[pI].menu[mI].var1.valStep = currMenu.itemStep ? currMenu.itemStep : 1;
                    win.panels[pI].menu[mI].var1.oldValue=win.panels[pI].menu[mI].var1.value;
               
                    win.panels[pI].menu[mI].valt.onChange = function()
                    {
                        var var1 = this.parent.parent.var1
                        win.paramsOK = true
                        var value = this.value // assign to value first becuase if you assign to var1.value it will get clamped automatically
                        var1.value = value
                        var1.value = Math.round (var1.value / var1.valStep) *var1.valStep;
 
                        // If our item should be less or equal to a linked item (if there is one), and the linked value is in fact lower, change it
                        if (var1.itemLEQitem >= 0 && var1.parent.parent.menu [var1.itemLEQitem].var1.value < var1.value)
                        {
                            var1.parent.parent.menu [var1.itemLEQitem].var1.value = var1.value
                            var1.parent.parent.menu [var1.itemLEQitem].var1.valt.value = var1.value
                        }
                        // If our item should be greater or equal to a linked item (if there is one), and the linked value is in fact greater, change it
                        if (var1.itemGEQitem >= 0 && var1.parent.parent.menu [var1.itemGEQitem].var1.value > var1.value)
                        {
                            var1.parent.parent.menu [var1.itemGEQitem].var1.value = var1.value
                            var1.parent.parent.menu [var1.itemGEQitem].var1.valt.value = var1.value
                        }
                       
                        if (var1.value != var1.oldValue)
                        {
                           setPresetList (1)
                            doPreview (this.parent.parent)
                        }
                    }

                   //col2width += 40
                   win.panels[pI].menu[mI].var1.onChanging = function()
                   {
                        this.value = Math.round (this.value / this.valStep) * this.valStep;
                        this.valt.value = this.value
                        if (dvar.presetsDbx.selection2 != 1)
                        {
                           setPresetList (1)
                        }
                    
                        //return;
                        // If our item should be less or equal to a linked item (if there is one), and the linked value is in fact lower, change it
                        if (this.parent.var1.itemLEQitem >= 0 && this.parent.parent.menu [this.parent.var1.itemLEQitem].var1.value < this.parent.var1.value)
                        {
                            this.parent.parent.menu [this.parent.var1.itemLEQitem].var1.value = this.parent.var1.value
                            this.parent.parent.menu [this.parent.var1.itemLEQitem].var1.valt.value = this.parent.var1.value
                        }
                        // If our item should be greater or equal to a linked item (if there is one), and the linked value is in fact greater, change it
                        if (this.parent.var1.itemGEQitem >= 0 && this.parent.parent.menu [this.parent.var1.itemGEQitem].var1.value > this.parent.var1.value)
                        {
                            this.parent.parent.menu [this.parent.var1.itemGEQitem].var1.value = this.parent.var1.value
                            this.parent.parent.menu [this.parent.var1.itemGEQitem].var1.valt.value = parent.var1.value
                        }
                        //alert ("slider changed, old = " + this.parent.var1.oldValue + ", new = " + this.parent.var1.value)
                        // Extendscript is not fast enough for true live preview while the slider is being modified
                        //if (isPreview)
                        //    if (typeof this.parent.var1.oldValue == 'undefined' || Math.abs(this.parent.var1.value - this.parent.var1.oldValue) >= this.parent.var1.valStep)
                        //    {
                                //alert('Slider changed, new value is ' + this.parent.var1.value)
                                //win.g100.bu1.onClick();  // this sets output text
                        //        doPreview (this.parent)
                        //    }
                        //alert ("done")
                        // update the mage
                    }
                   win.panels[pI].menu[mI].var1.onChange = function()
                   {
                        this.value = Math.round (this.value / this.valStep) * this.valStep;
                        this.valt.value = this.value
                        
                        var key = ScriptUI.environment.keyboardState.keyName;
                        if (key == "Right" || key == "Left")
                        {
                            // If we call onChanging() here, we don't know which is the last onChange call with a key press and when  to call doPreview
                            // Besically, we need to get key up event somehow
                            //this.onChanging()
                            this.value = this.oldValue // for now arrows cannot move slider left or right
                        }
                        else
                            if (isPreview && this.value != this.oldValue)
                                doPreview (this.parent.parent)
                   }
               
                    // set linked items if there are any
                    win.panels[pI].menu[mI].var1.itemLEQitem = typeof currMenu.itemLEQitem != 'undefined' ? currMenu.itemLEQitem : -1;
                    win.panels[pI].menu[mI].var1.itemGEQitem = typeof currMenu.itemGEQitem != 'undefined' ? currMenu.itemGEQitem : -1;                      
            }
        
             // name the variable so that it gets picked up by the xml parser
             win.panels[pI].menu[mI].var1.name = pI == 0 ? 'var' + mI : 'var' + pI + '_' + mI
             win.panels[pI].menu[mI].var1.fullname = currMenu.itemName
             
             win.panels[pI].menu[mI].var1.decoVarName = currMenu.varName
             //win.panels[pI].menu[mI].var1.oldValue = currMenu.itemValue
            win.panels[pI].menu[mI].var1.pI = pI      
            win.panels[pI].menu[mI].var1.mI = mI  
            win.panels[pI].menu[mI].var1.disabledElements = new Array(0) 
            
            if ((typeof currMenu.enabled != 'undefined') && !currMenu.enabled)
                enableMenuItem(pI, mI, false, 0)
        
           }
            
            if (0)
            {
                // get the real height
                var group = win.tabs[pI];
                group.layout.layout(false)
                var height = 60 + group.preferredSize[1]
                alert("height2 = " + height)
            }
   }
  

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
              
        //win.presetPn.setBtn = win.presetPn.add('button',undefined, localize( "$$$/DecoScripts/DecoMenu/ApplyPreset=Apply Preset"));
        //win.presetPn.saveBtn = win.presetPn.add('button',undefined,localize( "$$$/DecoScripts/DecoMenu/SavePreset=Save Preset"));
        //win.presetPn.deleteBtn = win.presetPn.add('button',undefined,localize( "$$$/DecoScripts/DecoMenu/DeletePreset=Delete Preset"));
        
 
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    presetsXML = new Array()
    allPresetsXML = new Array(0)
     
    if (shippedPresetsFolder)
    {
        readXMLFiles(shippedPresetsFolder, allPresetsXML) // read any shipped presets first
        numShippedPresets = allPresetsXML.length
    }
    readXMLFiles(decoPresetsFolder, allPresetsXML)  // see if any preset file(s) exists and load it/them.
     
    // Find presets for our menu
    for (var i = 0; i < allPresetsXML.length; i++)
       try{
            // compare the script name as a sanity check
            var xml = allPresetsXML[i] 
            if (xml != 'null' && xml != '') 
            {
                // check if there is a preset present
                var preset = xml.child ('preset')
                if (preset != 'null' && preset != '')
                {
                    if (preset.@presetName == 'Default') //XXX should we localize the strings inside the preset file?
                        defaultXML = xml
                    else if (preset.@presetName == 'Custom')
                        customXML = xml
                    else
                        presetsXML.push(xml) // all non-default and non-current presets
                }
            }
  
         }
        //catch assigns 'no_good' to current XMLVal so that if there is no value in the XML file, it will not try to assign a bad value to the UI controls.
        catch(e) { } //end catch
    
    if (!defaultXML)
    {
        // First time using this menu 
         defaultXML = new XML('<script><preset presetName ="Default"/></script>')
        // Store the default values
       setXML (defaultXML, win, 0)
    }
     if (!customXML)
    {
        // we store selected preset here as well
        customXML = new XML('<script><presets selection="1"/><preset presetName ="Custom"/></script>')
        // Store the default values
       setXML (customXML, win, 0)
    }
    lastUsedXML = clone(customXML)

    var selection = customXML.presets.@selection ? Number(customXML.presets.@selection) : 1
    if (selection < 0 || selection >= 2 + presetsXML.length)    //+ (numShippedPresets > 0 ? 1 :  0))  // 2 or 3 separators
        selection = 0

    if (forceUsingModelParameters)
        selection = 1
        
    setPresetList (selection) 

    if (!forceUsingModelParameters)
        setUIvar(selection == 0 ? defaultXML : (selection == 1 ? customXML : presetsXML[selection-2]), win)
    else
        setUIVarString(newModelParameters, win)
        
    if (previewZoom)
    {
       previewParameters.previewScaleFactor = customXML.presets.@previewscale ? Number(customXML.presets.@previewscale) : 1
        if (previewParameters.previewScaleFactor < 0.25)
            previewParameters.previewScaleFactor = 0.25
    }

    if (isPreview)
    {
        if (forceUsingModelParameters)
            previewParameters = newModelParameters
        else
            outputResult('previewParameters.') // update previewParameters
        update(previewParameters)
    }

    // see if the file was created, if not, disable preview
    var file = new File(previewImagePath)
    if (!file.exists)
        isPreview = false;

    if (isPreview)
    {
        //var file = new File(previewImagePath)
        win.previewImage = win.previewPanel.add ('image', [0, 0, previewSize, previewSize], previewImagePath); // it will not scale down the image but crop it
        var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 1, 1, 1]);
        win.previewImage.backgroundColor = myBrush;
        win.previewImage.enabled = true
        
        if (previewZoom)
        {
            win.previewPanel.var1 = win.previewPanelBase.add('slider',undefined, 1) // name it var1 becuase that is what we expect in doPreview
            //win.previewPanel.var1.name = 'zoom' 
            //win.previewPanel.var1.fullname = 'preview scale factor'
            
            win.previewPanel.var1.enabled = true
            win.previewPanel.var1.margins = [0,0,0,0]
            win.previewPanel.var1.alignment = 'bottom'
            win.previewPanel.var1.size = [200, 17] 
            
            win.previewPanel.var1.minvalue = 0.25
            win.previewPanel.var1.maxvalue = 2
            win.previewPanel.var1.valStep = 0.01
            win.previewPanel.var1.oldValue=win.previewPanel.var1.value

           win.previewPanel.var1.onChanging = function()
           {
                this.value = Math.round (this.value / this.valStep) * this.valStep;
           }
           win.previewPanel.var1.onChange = function()
           {
                var key = ScriptUI.environment.keyboardState.keyName;
                if (key == "Right" || key == "Left")
                {
                    // If we call onChanging() here, we don't know which is the last onChange call with a key press and when  to call doPreview
                    // Besically, we need to get key up event somehow
                    //this.onChanging()
                    this.value = this.oldValue // for now arrows cannot move slider left or right
                }
                else
                   if (isPreview)
                   {
                        if (this.value != this.oldValue)
                        {
                            //alert("zoom set to " + this.value)
                            previewParameters.previewScaleFactor = this.value
                            this.oldValue=this.value
                            doPreview (this.parent)
                       }
                   }
           }
     }
 
       if (!isLivePreview)
        {
            win.previewPanel.updateBtn = win.previewPanelBase.add('button',undefined, localize( "$$$/DecoScripts/DecoMenu/UpdatePreview=Update Preview"))
            win.previewPanel.updateBtn.enabled = false
            win.previewPanel.updateBtn.onClick = function() 
            {
                outputResult('previewParameters.') // update previewParameters
                update(previewParameters)
                win.previewImage.image = previewImagePath; // updates the image
                win.previewImage.enabled = true
                win.previewPanel.updateBtn.enabled = false
            }
        }
     }

    if (previewZoom)
    {
       previewParameters.previewScaleFactor = customXML.presets.@previewscale ? Number(customXML.presets.@previewscale) : 1
        if (previewParameters.previewScaleFactor < 0.25)
            previewParameters.previewScaleFactor = 0.25
        win.previewPanel.var1.value = previewParameters.previewScaleFactor
        win.previewPanel.var1.oldValue=win.previewPanel.var1.value
     }
   
    // call onClick for all checkboxes (to update disableItems, if there are any)
    for (var pI = 0; pI < menu.panels.length; pI++)
    {
        var currPanel = menu.panels[pI]
        for (var mI = 0; mI < currPanel.panelMenu.length; mI++)
             if (currPanel.panelMenu[mI].itemType == 'checkbox')
                    win.panels[pI].menu[mI].var1.onClick() 
            else if (currPanel.panelMenu[mI].itemType == 'dropdownlist')
                    win.panels[pI].menu[mI].var1.onChange() 
     }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////// Buttons
    win.g100 =win.p0.add('group');
    win.g100.orientation = "column";
    win.g100.alignment='top';
    
    var button3_width = 70;
    var textMargin = 25;
    var width = Math.max(  getTextWidth (win.g100,localize( "$$$/DecoScripts/DecoMenu/OK=OK")),
                           getTextWidth (win.g100,localize( "$$$/DecoScripts/DecoMenu/Reset=Reset")));
    width = Math.max(width,getTextWidth (win.g100,localize( "$$$/DecoScripts/DecoMenu/Cancel=Cancel")));
    
    if (width + textMargin > button3_width)
    {
        button3_width = width + textMargin
    }
    
    win.g100.bu1 = win.g100.add('button',undefined,localize( "$$$/DecoScripts/DecoMenu/OK=OK"));
    win.g100.bu1.preferredSize=[button3_width,22];
    
    //win.g100.bu2 = win.g100.add('button',undefined, localize( "$$$/DecoScripts/DecoMenu/Default=Default"));
    //win.g100.bu2.preferredSize=[70,30];
    
    win.g100.bu4 = win.g100.add('button',undefined, localize( "$$$/DecoScripts/DecoMenu/Reset=Reset"));
    win.g100.bu4.preferredSize=[button3_width,22];
 
    win.g100.bu3 = win.g100.add('button',undefined, localize( "$$$/DecoScripts/DecoMenu/Cancel=Cancel"));
    win.g100.bu3.preferredSize=[button3_width,22];
    
    //Control goes here if the pattern was chosen by name and the button to do it was clicked
    win.g100.bu1.onClick=function() 
    {                
        // output the variables 
        if (!win.paramsOK || !outputResult ('modelParameters.'))
        {
            win.paramsOK = true
            return;
        }
 
        setXML (customXML, win, 0)
        //alert ('writing xml' + customXML)
        writeXMLFile (customXML);
        
        skipRun = false
        win.close(0);
        
        RenderAPI.setParameter (kpsParameterString, modelParameters.toSource());
        //app.refresh()
    }       //end of onClick function

    //win.g100.bu2.onClick=function()
    //{
       ///setXML(prefXML,win,0);
       //skipRun = false
       //win.close(0);
    //}

    win.g100.bu4.onClick=function()
    {
        // Reset
        // Load default preset
        customXML = clone(lastUsedXML)

        setPresetList(1)
        menuApplyPreset(customXML)
    }

    win.g100.bu3.onClick=function()
    {
        // Cancel
        //setXML (customXML, win, 0)
        //writeXMLFile (customXML);
        
        skipRun = true
        win.close(0)
        Engine.setParameter(kCancelled, 1)
    }

     function onKeyDown (event)
    {
        //alert('onKeyDown')
    }
    //win.addEventListener('mouseover', onKeyDown)
    //win.addEventListener('mousedown', onKeyDown, false)
    win.addEventListener('enterKey', onKeyDown, false)

   win.center();
    win.show();
}


	/*
		The RGB swatch is implemented as a Group element with a Button that completely fills it. We use
		a Group because a Group's graphics.backgroundColor can be set, so we set it to the currently
		selected RGB color. We put a Button inside this Group to demonstrate making the swatch 'active',
		and to show a technique for making the Button be essentially transparent, by defining an onDraw
		handler for it that does not draw the 'background' of the button, but only its borders.
		We initialize the 'static' drawing state of the "rgb swatch" button as follows:
		<ul>
		<li>the 'border' paths are constant, so create them now rather than each time we draw the swatch
		<li>likewise, the pens for the borders are constant colors, so create them only once
		</ul>
	*/
     function toPrintObject(obj)
     {
        if (obj == null || typeof obj != "object")
        {
             return obj
        }
 
        var res = ''
        
         for (var name in obj)
        {
            res += toPrintObject (obj[name]) + ", "
        }

        return res
    }

	function initializeDrawingState (swatchBtn, color)
	{
		var gfx = swatchBtn.graphics;
		var btnW = swatchBtn.size.width;
		var btnH = swatchBtn.size.height;
 
		//	Define the top-left and bottom-right border paths
		var halfBorderW = kSwatchBorderWidth / 2;   
        
         gfx.newPath();
		gfx.rectPath(halfBorderW, halfBorderW, btnW - kSwatchBorderWidth, btnH - kSwatchBorderWidth);
         swatchBtn.brRectPath = gfx.currentPath;
               
         //gfx.new
		//	Define the border pens: use semi-transparent pens so the background color shows through
		swatchBtn.shadowPen = gfx.newPen (gfx.PenType.SOLID_COLOR, [.25, .25, .25, .4], kSwatchBorderWidth);
         if (swatchBtn.enabled)
            swatchBtn.backgroundColor = gfx.newBrush (gfx.PenType.SOLID_COLOR, [color[0], color[1], color[2], 1]);
         else
            swatchBtn.backgroundColor = gfx.newBrush (gfx.PenType.SOLID_COLOR, [0xe0 / 255, 0xe0 / 255, 0xe0 / 255, 1]);
 	}

	/*
		This is the "onDraw" event handler function for the rgb swatch Button. Because this function is
		called each time this Button is drawn, we want it to execute as fast as possible, so as
		much of the drawing state as possible is derived 'outside' this function.
	*/
	function drawRGBSwatch (drawingStateObj)
	{
 		var gfx = this.graphics;
		try {
			gfx.strokePath (this.shadowPen, this.brRectPath);
			if (this.enabled)
				gfx.fillPath (this.backgroundColor, this.brRectPath);
		}
		catch (e) {
			//	On any error, undefine the onDraw handler, so we don't get here again
			this.onDraw = undefined;
			//alert ("drawRGBSwatch handler failed.\n" + e);
		}
	}
  
	//	This is the "onClick" event handler for the rgb swatch button
	function clickRGBSwatch ()
	{
        var hex = Number(this.value).toString(16)
        hex = "0x000000".substr(0, 8 - hex.length) + hex
        var rgb = $.colorPicker(hex)
        if (rgb < 0)
            return;
        hex = Number(rgb).toString(16)
        hex = "0x000000".substr(0, 8 - hex.length) + hex
        this.value = hex
        
        setSwatchColor(this, hex)
        //alert ("Selected RGB color:  " + hex);
        
        //alert('old ' + this.oldValue + " new " + this.value)
        if (isPreview)
            if (this.value != this.oldValue)
            {
               setPresetList (1)
                doPreview (this.parent)
            }
 	}
    
      function setSwatchColor(button, hex)
      {
        var number = parseInt(hex, 16) // convert hex string to int
        var red = Math.floor(number / 0x10000)
        number -= red * 0x10000 
        var green = Math.floor( number / 0x100) 
        var blue = (number - green * 0x100 )
                
        var swatchGfx = button.graphics;
        //alert(this.backgroundColor.color)
        button.backgroundColor =
            swatchGfx.newBrush (swatchGfx.BrushType.SOLID_COLOR, [red/ 255, green/ 255, blue/ 255]);
        button.notify("onDraw")
      }


function removeSpaces(inputStr)
{
        var outputStr = ''
        for (var i = 0; i < inputStr.length; i++)
            if (inputStr[i] == ' ')
                outputStr += '_'
            else
                outputStr += inputStr[i]
        return outputStr
}

/////////////////////////////////////////////////////////////////////////
// The following code has been originaly written by Chuck Uebele
/////////////////////////////////////////////////////////////////////////
//functions for saving prefs



function removePercent20 (name)
{
    // remove %20 from the name
    var newName = ""
    var lastIndex = 0
    name = name.toString()
    for (var i = 0; i < name.length-2; i++)
    {
         if (name[i] == '%')
            if (name[i+1] == '2' && name[i+2] == '0')
            {
                newName += name.slice(lastIndex, i) + " "
                lastIndex = i+3
             }
     }

    if (lastIndex < name.length)
        newName += name.slice (lastIndex, name.length)
    return newName
}


function setPresetList(selection)
{
    // selection is 0 for default, 1 for custom, and 2 - 1+presets.length for user presets
        dvar.presetsDbx.insideSetPresetList = true
        presetList = new Array();
        dvar.presetsDbx.removeAll();
        
        // the first item always stores the active preset
        if (selection == 0) // Default
        {
            dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/Preset=Preset: ") + localize("$$$/DecoScripts/ScriptMenu/Default=Default"))
        }
        else if (selection == 1) // Custom
        {
            dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/Preset=Preset: ") + localize("$$$/DecoScripts/ScriptMenu/Custom=Custom"))
        }
        else if (selection >= 2 && selection < 2 + presetsXML.length)
        {
             dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/Preset=Preset: ") + File.decode (presetsXML[selection-2].preset.@presetName))
        }
        dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/Default=Default"))
        
        var i = 0;
        if (numShippedPresets > 0)
        {
            dvar.presetsDbx.add('separator', "-")
            for(/* i set above */; i<numShippedPresets; i++) { dvar.presetsDbx.add('item', File.decode (presetsXML[i].preset.@presetName)) }
            // i continues to the next 'for'
      }
     
        dvar.presetsDbx.add('separator', "-")
        if (presetsXML.length - i > 0)
        {
           for(/* i set above */; i<presetsXML.length; i++) { dvar.presetsDbx.add('item', File.decode (presetsXML[i].preset.@presetName)) }
           dvar.presetsDbx.add('separator', "-")
        }
        dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/LoadPreset=Load Preset..."))
        dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/SavePreset=Save Preset..."))
        dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/DeletePreset=Delete Preset..."))
        dvar.presetsDbx.items[dvar.presetsDbx.items.length - 1].enabled = selection >= 2;
        dvar.presetsDbx.add('separator', "-")
        dvar.presetsDbx.add('item', localize("$$$/DecoScripts/ScriptMenu/Custom=Custom"))
        
        dvar.presetsDbx.selection = 0 //selection == 1 ? 3 + presetsXML.length + 5 :  selection == 0 ? 0 : 2 + selection; // will call onChanging for selection
        dvar.presetsDbx.selection2 = selection
        dvar.presetsDbx.insideSetPresetList = false
} //end function setPresetList


 function setXML(x,d,level)
 {//x = xml file, d = dialog.
     //alert ("setXML called, d.children.length = " + d.children.length);
    //if (x.presets.child(n))
    //   x.presets.child(n).setChildren('')
    
    // Delete the preset
    if (level == 0 && x.preset.children().length() > 0)
    {
        //var children =  x.presets.children();
        //var tempXML = new XML('<preset presetName ="' + saveName + '"/>');
        for (var i = x.preset.children().length() - 1; i >= 0; i--)
            delete x.preset.children()[0];
    }
        
    for (var i = 0; i<d.children.length; i++)
    {
       if(d.children[i].type == 'panel' || d.children[i].type == 'group' || d.children[i].type == 'tabbedpanel' || d.children[i].type == 'tab')
       {
           //loops though UI and restarts function if it comes to a container that might have more children
           setXML(x,d.children[i],level+1)
        }
        else{
            if(d.children[i].name){//check to make sure the control has a name assigned so that it only records those with name.
                switch(d.children[i].type){
                    case 'radiobutton':
                        x.preset.appendChild(XML('<' + d.children[i].name + ' name="' + d.children[i].fullname + '" type="' + d.children[i].type + '">' + d.children[i].value + '</' + d.children[i].name + '>'));                        
                        break;
                    case 'checkbox':
                        x.preset.appendChild(XML('<' + d.children[i].name +' name="' + d.children[i].fullname + '" type="' + d.children[i].type + '">' + d.children[i].value + '</' + d.children[i].name + '>'));                        
                        break;
                    case 'slider':
                        x.preset.appendChild(XML('<' + d.children[i].name +' name="' + d.children[i].fullname + '" type="' + d.children[i].type + '">' + Math.round (d.children[i].value / d.children[i].valStep) * d.children[i].valStep + '</' + d.children[i].name + '>'));                        
                        break;
                    case 'edittext':
                        x.preset.appendChild(XML('<' + d.children[i].name +' name="' + d.children[i].fullname + '" type="' + d.children[i].type + '"><![CDATA[' + d.children[i].text + ']]\></' + d.children[i].name + '>'));                        
                        break;
                    case 'button':
                        x.preset.appendChild(XML('<' + d.children[i].name +' name="' + d.children[i].fullname + '" type="colorpicker"><![CDATA[' + d.children[i].value + ']]\></' + d.children[i].name + '>'));                        
                        break;
                   case 'dropdownlist':
                        if(d.children[i].selection){varHold = d.children[i].selection.text}
                        else{varHold = 'null'};
                        x.preset.appendChild(XML('<' + d.children[i].name +' name="' + d.children[i].fullname + '" selecIndex="' + d.children[i].selection + '" type="' + d.children[i].type + '"><![CDATA[' + varHold + ']]\></' + d.children[i].name + '>'));                        
                        break;
                  }//end switch
               }//end if for child having name
            }//end else
        }//end for loop
}//end function setXML


function setUIVarString (x, d)
{
    //x=object with parameters; d = UI dialog
    var currentVal;//used to store values from XML file.  When this value is assigned, it checks to see if value from XML exist
    var noMatch = false
    
    if (d == win)
        inSetUIvar = true
    
    //if (d == win)
    //    alert('Setting values to ' + x)

    for(var i = 0; i<d.children.length; i++)
    {
        noMatch = false;
        if(d.children[i].type == 'panel' || d.children[i].type == 'group' || d.children[i].type == 'tab' || d.children[i].type == 'tabbedpanel') { setUIVarString(x,d.children[i]) };//reruns function if child is container and not control item.	
        else
        {
            if(d.children[i].decoVarName)
            {
                currentVal = x[d.children[i].decoVarName]
                switch(d.children[i].type)
                {
                    case 'radiobutton':
                    case 'checkbox':
                        d.children[i].value = currentVal
                        d.children[i].oldValue = d.children[i].value
                        break;
                    case 'edittext':
                        d.children[i].text = currentVal
                        break;
                        
                    case 'button':
                        //fix for watson 4127957
                        //if (currentXMLVal.@type == 'colorpicker') // colorpicker
                        {
                            var hex = Number(Math.floor(currentVal[0] * 255 + 0.5) * 0x10000 + 
                                                         Math.floor(currentVal[1] * 255 + 0.5) * 0x100 + 
                                                         Math.floor(currentVal[2] * 255 + 0.5) ).toString(16)
                            hex = "0x000000".substr(0, 8 - hex.length) + hex
                            d.children[i].value = hex
                            d.children[i].oldValue = d.children[i].value
                            setSwatchColor (d.children[i], d.children[i].enabled ? d.children[i].value : '0xe0e0e0')                              
                    }
                    break;
                    
               case 'slider':
                    d.children[i].value = currentVal
                    if (typeof d.children[i].valt != 'undefined')
                        d.children[i].valt.value = d.children[i].value;
                    d.children[i].oldValue = d.children[i].value
                   break;
                   
                case 'dropdownlist':
                    d.children[i].selection = currentVal
                    d.children[i].oldValue = d.children[i].selection
                    break;
                  };//end switch else             
                };//end if for UI control having name
            };//end else for if child is container or control
        };//end for loop
    
    if (d == win)
        inSetUIvar = false
}


function setUIvar(x,d)
{  //x= xml file; d = UI dialog

    var currentXMLVal;//used to store values from XML file.  When this value is assigned, it checks to see if value from XML exist
    var noMatch = false
    
    if (d == win)
        inSetUIvar = true
    
    //if (d == win)
    //    alert('Setting values to ' + x)

    for(var i = 0; i<d.children.length; i++)
    {
        noMatch = false;
        if(d.children[i].type == 'panel' || d.children[i].type == 'group' || d.children[i].type == 'tab' || d.children[i].type == 'tabbedpanel') { setUIvar(x,d.children[i]) };//reruns function if child is container and not control item.	
        else
        {
            if(d.children[i].name)
            {
                //Checks to see if child has a name assigned so only will reset those control items that have a name will be stored.
                var goodXML = false;
                try
                {
                        currentXMLVal = x.preset.child(d.children[i].name);
                        if (currentXMLVal.@name != d.children[i].fullname)
                        {
                            // find among other children
                            for (var j = 0; j < x.preset.children().length(); j++)
                            {
                                //alert('child['+ j + '] = ' + x.preset.children()[j].@name)
                                if (x.preset.children()[j].@name == d.children[i].fullname)
                                    currentXMLVal = x.preset.children()[j]
                             }
                        }
                        if (currentXMLVal.@name == d.children[i].fullname) // test again in case we searched for it in the loop above
                        {
                            //alert('value for ' + d.children[i].name + ' is:' + currentXMLVal)
                            goodXML = true
                         }
                     }//end try
                //catch assigns 'no_good' to current XMLVal so that if there is no value in the XML file, it will not try to assign a bad value to the UI controls.
                catch(e) {}; //end catch
                //switch makes sure proper type of value is reassigned back to UI controls.
                if (goodXML && (currentXMLVal.length() > 0 || d.children[i].type == 'button' ))
                {
                     switch(d.children[i].type)
                    {
                        case 'radiobutton':
                            d.children[i].value = returnBoolean(currentXMLVal);
                            d.children[i].oldValue = d.children[i].value
                            break;
                         case 'checkbox':
                            d.children[i].value = returnBoolean(currentXMLVal);
                            d.children[i].oldValue = d.children[i].value
                            break;
                        case 'edittext':
                            d.children[i].text = new String(currentXMLVal); // clone the string since we are deleting the xml in setUIvars
                            break;
                        case 'button':
                             if (currentXMLVal.@type == 'colorpicker') // colorpicker
                            {
                                 //alert('xml value:' + currentXMLVal)
                                d.children[i].value = currentXMLVal != '' ? new String(currentXMLVal) : '0x000000'; // clone the string since we are deleting the xml in setUIvars
                                d.children[i].oldValue = d.children[i].value
                                setSwatchColor (d.children[i], d.children[i].enabled ? d.children[i].value : '0xe0e0e0')                              
                          }
                            break;
                       case 'slider':
                            d.children[i].value = parseFloat(currentXMLVal);
                            if (typeof d.children[i].valt != 'undefined')
                                d.children[i].valt.value = d.children[i].value;
                            d.children[i].oldValue = d.children[i].value
                           break;
                        case 'dropdownlist':
                            varHold = false;
 
                            if(currentXMLVal.@selecIndex.toString() == 'null'){d.children[i].selection = null}
                            else{d.children[i].selection = parseInt(currentXMLVal.@selecIndex)};
                            d.children[i].oldValue = d.children[i].selection
                            break;
                          };//end switch else

                   };//end if to see if there is a good value from the XML
               
                };//end if for UI control having name
            };//end else for if child is container or control
        };//end for loop
    
    if (d == win)
        inSetUIvar = false

 };//end function setUIvar
 
 //function returns a boolean value
function returnBoolean(b){
	if(b == 'true'){return true}
	else{return false}
	};

//===============READ/WRITE XML functions========================================
//=========================================================================
 function readXMLFiles(directory, parsedXMLs) 
 {
     // read all xml files in the decoPresetsFolder directory into one string
     folder = new Folder (directory)
     xmlFiles = folder.getFiles('*.xml')
     
     for (var i = 0; i < xmlFiles.length; i++)
     {
         //alert('Opening file: ' + xmlFiles[i])
         file = new File(xmlFiles[i] )
         parsedXMLs.push (readFile(file))
    }

    return parsedXMLs
}


function readFile(file)
{
    if (!file.exists)
    {
        alert( localize("$$$/DecoScripts/ScriptMenu/CannotFindFile=Cannot find file: ") + decodeURI(file.absoluteURI));
    }
    else
    {
        file.encoding = "UTF8";
        file.lineFeed = "unix";
        file.open("r", "TEXT", "????");
        var str = file.read();
        file.close();
        return new XML(str)
    }
}

function writeXMLFile (xml)
{
	if (!(xml instanceof XML))
    {
		alert( localize("$$$/DecoScripts/ScriptMenu/BadXMLonWrite=Cannot save preset file - bad XML"))
	}
	else
    {
         // add selection
         if (xml == customXML)
         {
            xml.presets.@selection = "" + dvar.presetsDbx.selection2
         
            // add preview scale
            if (previewZoom)
                xml.presets.@previewscale = "" + previewParameters.previewScaleFactor
        }
         
         var fileName = decoPresetsFolder + '/' + xml.preset.@presetName + '.xml'
         var file = new File(fileName)
         //alert ('Saving file ' + fileName);
         
         writeFile(file,xml)
         
         // workaround for mac, remove file without extension
         var extraFile = decoPresetsFolder + '/' + xml.preset.@presetName
         file = new File(extraFile)
         if (file.exists)
            file.remove()
	}
}

function deleteXMLFile (xml)
{
	if (!(xml instanceof XML))
    {
		alert( localize("$$$/DecoScripts/ScriptMenu/BadXMLonDelete=Cannot delete preset file - bad XML"))
	}
	else
    {         
         var fileName = decoPresetsFolder + '/' + xml.preset.@presetName + '.xml'
         var file = new File(fileName)
         //alert ('Deleting file ' + fileName);
         file.remove()
	}
}

function writeFile(file, xml)
{
    file.encoding = "UTF8";
    file.open("w", "????TEXT", "MPS_");  // MPS_ is for XML on Mac, ignored on windows
    //unicode signature, this is UTF16 but will convert to UTF8 "EF BB BF"
    file.write("\uFEFF");
    file.lineFeed = "unix";
    file.write(xml.toXMLString());
    file.close();
}


previewAPI.command (kpsThawProgress) // thaw the progress bar

if (typeof skipRun == 'undefined' || !skipRun)  // satrt watch cursor unles we exited the preview window without pressing a button
    previewAPI.command (kpsStartWatchCursor)