/*
Copyright (C) 2015 Runtime Revolution Ltd.

This file is part of LiveCode.

LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.

LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with LiveCode.  If not see <http://www.gnu.org/licenses/>.  */

/*
This widget is a header bar.
*/

-- declaring the extension as a widget, followed by identifier
widget com.livecode.widget.headerBar

-- dependency declarations
use com.livecode.canvas
use com.livecode.widget
use com.livecode.engine
use com.livecode.library.iconSVG

-- metadata
metadata title is "Header"
metadata author is "LiveCode"
metadata version is "1.0.0"
metadata preferredSize is "320,64"
metadata svgicon is "M0,0v29.5h80.2V0H0z M13.2,20.6l-1.8,1.8l-7.5-7.5l0.1-0.1l-0.1-0.1l7.5-7.5l1.8,1.8l-5.8,5.8L13.2,20.6z M31.2,10.9h-3.8v10.3h-2.6V10.9H21V8.7h10.2V10.9z M34.7,21.2h-2.5v-9.3h2.5V21.2z M34.7,10.8h-2.5V8.6h2.5V10.8z M41.3,13.7h-1.5v4.9c0,0.4,0,0.6,0.1,0.7c0.1,0.1,0.4,0.1,0.9,0.1c0.1,0,0.2,0,0.2,0s0.2,0,0.2,0v1.8l-1.1,0c-1.1,0-1.9-0.2-2.3-0.6c-0.3-0.3-0.4-0.7-0.4-1.3v-5.7h-1.3V12h1.3V9.4h2.4V12h1.5V13.7z M45.4,21.2h-2.4V8.7h2.4V21.2z M56.2,17.3h-6.8c0,0.9,0.4,1.6,1,2c0.4,0.2,0.8,0.3,1.3,0.3c0.6,0,1-0.1,1.3-0.4c0.2-0.2,0.4-0.4,0.5-0.6h2.5c-0.1,0.6-0.4,1.1-0.9,1.7c-0.8,0.9-2,1.4-3.5,1.4c-1.2,0-2.3-0.4-3.3-1.1c-0.9-0.8-1.4-2-1.4-3.7c0-1.6,0.4-2.8,1.3-3.7c0.9-0.9,2-1.3,3.3-1.3c0.8,0,1.5,0.1,2.2,0.4c0.6,0.3,1.2,0.7,1.6,1.4c0.4,0.6,0.6,1.2,0.7,1.9c0,0,0,0.1,0,0.1h-2.5c-0.1-0.6-0.3-1-0.6-1.4c-0.4-0.3-0.9-0.5-1.4-0.5c-0.6,0-1.1,0.2-1.4,0.5c-0.3,0.4-0.6,0.8-0.6,1.4h3.1h1.1h2.5C56.2,16.1,56.2,16.6,56.2,17.3z M76.1,16.2h-4.6v4.6h-3v-4.6h-4.6v-3h4.6V8.7h3v4.6h4.6V16.2z"
   
-- property declarations
/*
Summary: Sets the title of the header bar

Syntax: set the headerTitle of <widget> to <pTitle>
Syntax: get the headerTitle of <widget>

Parameters:
pTitle (string): The text of the title of the header bar.

Description:
Sets the title of the header bar to any string.
*/
property headerTitle          get mHeaderTitle           set setHeaderTitle
metadata headerTitle.default is "Title"
metadata headerTitle.label is "Title"

/*
Summary: Sets the subtitle of the header bar

Syntax: set the headerSubtitle of <widget> to <pSubtitle>
Syntax: get the headerSubtitle of <widget>

Parameters:
pSubtitle (string): The text of the subtitle of the header bar.

Description:
Sets the subtitle of the header bar to any string.
*/
property headerSubtitle       get mHeaderSubtitle        set setHeaderSubtitle
metadata headerSubtitle.default is "Subtitle"
metadata headerSubtitle.label is "Subtitle"

/*
Summary: Sets the label at the left hand side of the header

Syntax: set the leftLabel of <widget> to <pLabel>
Syntax: get the leftLabel of <widget>

Parameters:
pLabel (string): the label at the left hand side of the header

Description:
Sets the label at the left hand side of the header.
*/
property leftLabel            get mLeftLabel             set setLeftLabel
metadata leftLabel.default is "Back"
metadata leftLabel.label is "Left Label"

/*
Summary: Sets the actions of the header bar

Syntax: set the headerActions of <widget> to <pActions>
Syntax: get the headerActions of <widget>

Parameters:
pActions (list): The list of actions.

Description:
Sets the actions of the header bar from pActions, where pActions is a line delimited list of the actions, where each line is a comma delimited list
of the different components of the action: name, label, icon
*/
property headerActions        get getHeaderActions       set setHeaderActions
metadata headerActions.editor is "com.livecode.pi.navbar"
metadata headerActions.label is "Header Actions"

/*
Summary: Sets the action names of the header bar

Syntax: set the actionNames of <widget> to <pNames>
Syntax: get the actionNames of <widget>

Parameters:
pNames (list): The list of names.

Description:
Sets the names of the header bar actions, where <pNames> is a comma delimited list of the names.
*/
property actionNames          get getActionNames         set setActionNames
metadata actionNames.user_visible is "false"

/*
Summary: Sets the action labels of the header bar

Syntax: set the actionLabels of <widget> to <pLabels>
Syntax: get the actionLabels of <widget>

Parameters:
pLabels (list): The list of labels.

Description:
Sets the labels of the header bar actions, where <pLabels> is a comma delimited list of the labels.
*/
property actionLabels         get getActionLabels        set setActionLabels
metadata actionLabels.user_visible is "false"

/*
Summary: Sets the action icons of the header bar

Syntax: set the actionIcons of <widget> to <pIcons>
Syntax: get the actionIcons of <widget>

Parameters:
pIcons (list): The list of icons.

Description:
Sets the icons of the header bar actions, where <pIcons> is a comma delimited list of the icon names.
*/
property actionIcons          get getActionIcons         set setActionIcons
metadata actionIcons.user_visible is "false"

/*
Summary: Sets the header actions display style.

Syntax: set the actionStyle of <widget> to <pActionStyle>
Syntax: get the actionStyle of <widget>

Parameters:
pActionStyle (enum): The style of the header actions display.
-"icons": Show the icons
-"names": Show the names

Description:
Sets the header actions display style of the control.
*/
property actionStyle          get mActionStyle           set setActionStyle
metadata actionStyle.editor is "com.livecode.pi.enum"
metadata actionStyle.options is "icons,names"
metadata actionStyle.default is "icons"
metadata actionStyle.label is "Action Display Style"

/*
Summary: Controls whether the back icon is displayed.

Syntax: set the showBackIcon of <widget> to <pShow>
Syntax: get the showBackIcon of <widget>

Parameters:
pShow (boolean): True or false

Description:
Sets whether the back icon is showing or not.
*/
property showBackIcon         get mShowBackIcon          set setShowBackIcon
metadata showBackIcon.editor is "com.livecode.pi.boolean"
metadata showBackIcon.default is "true"
metadata showBackIcon.label is "Show Back Icon"

/*
Summary: Sets whether there is a search icon in the header bar.

Syntax: set the showSearchIcon of <widget> to <pShow>
Syntax: get the showSearchIcon of <widget>

Parameters:
pShow (boolean): True or false

Description:
Controls whether the search icon is displayed in the header bar.
*/
property showSearchIcon       get mShowSearchIcon        set setShowSearchIcon
metadata showSearchIcon.editor is "com.livecode.pi.boolean"
metadata showSearchIcon.default is "false"
metadata showSearchIcon.label is "Show Search Icon"

/*
Summary: Sets the color of the actions and the back button.

Syntax: set the actionColor of <widget> to <pColor>
Syntax: get the actionColor of <widget>

Parameters:
pColor (color): The color of the actions and back button

Description:
Sets the color of the actions and the back button.
*/
property actionColor          get getActionColor         set setActionColor
metadata actionColor.editor is "com.livecode.pi.color"
metadata actionColor.default is "0,121,255"
metadata actionColor.label is "Action Color"


/*
Syntax: set the <opaqueBackground> of <widget> to {true | false}
Syntax: get the <opaqueBackground> of <widget>

Summary: Whether the background of the header bar is drawn opaque or not

Description:
Use the <opaqueBackground> property to determine whether objects underneath the header bar are visible or not.

*/
property opaqueBackground get mOpaque set setOpaque
metadata opaqueBackground.default is "true"
metadata opaqueBackground.label is "Opaque Background"

/*
Syntax: set the <showDivide> of <widget> to {true | false}
Syntax: get the <showDivide> of <widget>

Summary: Whether the dividing line at the top of the header bar is drawn or not

Description:
Use the <showDivide> property to determine whether the dividing line at the top of the header bar is drawn or not.

*/
property showDivide get mShowDivide set setShowDivide
metadata showDivide.default is "true"
metadata showDivide.label is "Show Divide"

-- private instance variables
private variable mHeaderTitle as String
private variable mHeaderSubtitle as String
private variable mHeaderActions as List
private variable mActionStyle as String
private variable mShowBackIcon as Boolean
private variable mLeftLabel as String
private variable mActionColor as Color
private variable mShowSearchIcon as Boolean

private variable mShowLeftLabel as Boolean
private variable mShowSubtitle as Boolean

private variable mIconColor as Color
private variable mTitleRatio as Real
private variable mTitleVAdjust as Real
private variable mIconTop as Real
private variable mIconBottom as Real
private variable mIconWidth as Real
private variable mLabelTop as Real
private variable mLabelBottom as Real
private variable mSubtitleCenter as Real
private variable mSelectedAction as Integer
private variable mRight as Real
private variable mLetterCount as Integer
private variable mActionRects as List

private variable mOpaque as Boolean
private variable mShowDivide as Boolean

-- constants (some defined as varibales)
private variable kBackgroundColor as List
private variable kBlackColor as List

constant kMargin is 8
constant kActionRatio is 0.265625
constant kSubtitleRatio is 0.171875

public handler OnSave(out rProperties as Array)
   put the empty array into rProperties

   put mHeaderTitle into rProperties["headerTitle"]
   put mHeaderSubtitle into rProperties["headerSubtitle"]
   put getHeaderActions() into rProperties["headerActions"]
   put mActionStyle into rProperties["actionStyle"]
   put mShowBackIcon into rProperties["showBackIcon"]
   put mLeftLabel into rProperties["leftLabel"]
   put getActionColor() into rProperties["actionColor"]
   put mShowSearchIcon into rProperties["showSearchIcon"]

   return rProperties
end handler

public handler OnLoad(in pProperties as Array)
   put pProperties["headerTitle"] into mHeaderTitle
   put pProperties["headerSubtitle"] into mHeaderSubtitle
   setHeaderActions(pProperties["headerActions"])
   put pProperties["actionStyle"] into mActionStyle
   put pProperties["showBackIcon"] into mShowBackIcon
   put pProperties["leftLabel"] into mLeftLabel
   setActionColor(pProperties["actionColor"])
   put pProperties["showSearchIcon"] into mShowSearchIcon
end handler

public handler OnCreate()

   -- creating a list of actions
   variable tHeaderActions as List
   put the empty list into tHeaderActions

   variable tArray as Array
   put the empty array into tArray

   put "add" into tArray["name"]
   put "Add" into tArray["label"]
   put "plus" into tArray["icon_name"]
   put "" into tArray["selected_icon_name"]
   put "" into tArray["icon"]
   push tArray onto tHeaderActions

   put tHeaderActions into mHeaderActions

   -- properties
   put "Yesterday" into mHeaderTitle
   put "" into mHeaderSubtitle
   put "icons" into mActionStyle
   put true into mShowBackIcon
   put "Back" into mLeftLabel
   put color [0, 121/255, 1] into mActionColor
   put true into mShowSearchIcon

   -- variables
   put false into mShowLeftLabel
   put false into mShowSubtitle
   put 0 into mSelectedAction
   put 0 into mRight
   put 0 into mLetterCount

   -- constants (defined as variables)
   put [247/255,247/255,247/255] into kBackgroundColor
   put [0,0,0] into kBlackColor

   put true into mOpaque
	put true into mShowDivide
end handler

public handler OnPaint()

   updateVariables()

   -- draw the background rectangle
   if mOpaque then
      set the paint of this canvas to fetchPaint("background")
      fill fetchPath("background") on this canvas
   end if

   -- draw the bottom line
   if mShowDivide then
      set the paint of this canvas to fetchPaint("line")
      fill fetchPath("line") on this canvas
   end if

   -- filling in the text of the title
   set the font of this canvas to fetchFont("title")
   set the paint of this canvas to fetchPaint("title")
   fill text mHeaderTitle at center of fetchRect("title") on this canvas

   -- fill in the text of the subtitle
   if mShowSubtitle is true then
      set the font of this canvas to fetchFont("subtitle")
      set the paint of this canvas to fetchPaint("title")
      fill text mHeaderSubtitle at center of fetchRect("subtitle") on this canvas
   end if

   -- drawing the back button
   set the paint of this canvas to fetchPaint("backButton")

   if mShowBackIcon is true then
      fill fetchPath("backIcon") on this canvas
   end if

   if mShowLeftLabel is true then
      set the font of this canvas to fetchFont("label")
      fill text mLeftLabel at left of fetchRect("leftLabel") on this canvas
   end if

   -- draw the actions
   if mHeaderActions is not the empty list then

      variable tX as Integer
      variable tActionsCount as Integer

      put the number of elements in mHeaderActions into tActionsCount

      repeat with tX from 1 up to tActionsCount

         if tX+2 is mSelectedAction then
            set the paint of this canvas to solid paint with color [the red of mActionColor, the green of mActionColor, the blue of mActionColor, 0.5]
         else
            set the paint of this canvas to solid paint with mActionColor
         end if

         if mActionStyle is "icons" then
            drawActionIcon(mHeaderActions[tX]["icon_name"])
         else if mActionStyle is "names" then
            drawActionName(mHeaderActions[tX]["label"])
         end if

      end repeat
   end if

   -- draw the search icon
   if mShowSearchIcon is true then
      set the paint of this canvas to fetchPaint("searchIcon")
      fill fetchPath("searchIcon") on this canvas
   end if

end handler

public handler OnMouseDown()
   -- the icons change color when they are pressed down
   variable tSelectedDown as Integer
   put fetchSelectedItem() into tSelectedDown

   if tSelectedDown > 0 then
      put tSelectedDown into mSelectedAction
      redraw all
   end if

end handler

public handler OnMouseUp()

   -- the icons change back to their original colors on mouse up
   put 0 into mSelectedAction
   redraw all

   -- the relevant commands are dispatched to the script object
   variable tSelectedUp as Integer
   put fetchSelectedItem() into tSelectedUp

   if tSelectedUp > 2 then

      variable tActionCount as Integer
      variable tNumActions as Integer

      put the number of elements in element 1 of mHeaderActions into tNumActions

      repeat with tActionCount from 1 up to tNumActions

         if tActionCount is tSelectedUp-2 then
            post "headerAction" with [mHeaderActions[tActionCount]["name"]]
         end if

      end repeat

   else if tSelectedUp = 1 then
      post "leftAction"

   else if tSelectedUp = 2 then
      post "searchAction"
   end if

end handler

private handler updateVariables() returns nothing

   if mHeaderSubtitle is empty then
      put false into mShowSubtitle
   else
      put true into mShowSubtitle
   end if

   if mShowSubtitle is false then
      put 17/64 into mTitleRatio
   else
      put 15/64 into mTitleRatio
   end if

   if mLeftLabel is empty then
      put false into mShowLeftLabel
   else
      put true into mShowLeftLabel
   end if

   variable tH
   put my height into tH

   put 2*tH/64 into mTitleVAdjust
   put tH*(30.5/64) into mIconTop
   put tH*(51.5/64) into mIconBottom
   put tH*(13/64) into mIconWidth
   put tH*(9/64) into mLabelTop
   put tH*(73/64) into mLabelBottom
   put tH*(43/64) into mSubtitleCenter

   put (my width - kMargin) into mRight
   put 0 into mLetterCount

   put the empty list into mActionRects
end handler

private handler drawActionIcon(in pIconName as String) returns nothing

   variable tIconPath as Path
   variable tIconRect as Rectangle

   put path iconSVGPathFromName(pIconName) into tIconPath
   put fetchRect("actionIcon") into tIconRect

   setIconPath(tIconRect, tIconPath)
   fill tIconPath on this canvas

   push tIconRect onto back of mActionRects

end handler

private handler drawActionName(in pActionLabel as String) returns nothing
   put the number of chars in pActionLabel into mLetterCount

   variable tActionRect as Rectangle
   put fetchRect("actionLabel") into tActionRect

   set the font of this canvas to fetchFont("label")
   fill text pActionLabel at right of tActionRect on this canvas

   push tActionRect onto back of mActionRects

end handler

private handler setIconPath(in pTargetRect as Rectangle, inout xPath as Path)

	-- Scale the icon
	variable tBounds
	put the bounding box of xPath into tBounds

	-- Scale appropriately
	variable tXScale as Real
	variable tYScale as Real

	put the width of pTargetRect / the width of tBounds into tXScale
	put the height of pTargetRect / the height of tBounds into tYScale

	if tXScale > tYScale then
		put tYScale into tXScale
	else
		put tXScale into tYScale
	end if

   scale xPath by [tXScale, tYScale]

	variable tXTranslate as Real
	variable tYTranslate as Real

   put the bounding box of xPath into tBounds

	put the left of pTargetRect - the left of tBounds into tXTranslate
	put the top of pTargetRect - the top of tBounds into tYTranslate

    variable tXDiff as Real
    variable tYDiff as Real

    put the width of pTargetRect - the width of tBounds into tXDiff
    put the height of pTargetRect - the height of tBounds into tYDiff

	-- align center
	divide tXDiff by 2
	divide tYDiff by 2

	translate xPath by [tXTranslate + tXDiff, tYTranslate + tYDiff]
end handler

private handler fetchPath(in pObject as String) returns Path
   variable tIconPath as Path
   variable tIconRect as Rectangle

   variable tH as Real
   variable tW as Real
   put my width into tW
   put my height into tH

   if pObject is "background" then
      return rectangle path of rectangle [0, 0, tW, tH-1]

   else if pObject is "line" then
      return rectangle path of rectangle [0, tH-1, tW, tH]

   else if pObject is "backIcon" then
      put path iconSVGPathFromName("angle left") into tIconPath
      put rectangle [kMargin, mLabelTop, kMargin + mIconWidth, mLabelBottom] into tIconRect

      setIconPath(tIconRect, tIconPath)
      return tIconPath

   else if pObject is "searchIcon" then
      variable tLeft as Real
      variable tRight as Real

      if mHeaderActions is empty then
         put tW - kMargin into tRight
      else
         if mActionStyle is "icons" then
            put mRight into tRight
         else
            put mRight - kMargin into tRight
         end if
      end if
      put tRight - mIconWidth into tLeft

      put path iconSVGPathFromName("search") into tIconPath
      put rectangle [tLeft-mIconWidth/4, mLabelTop-mIconWidth/4, tRight+mIconWidth/4, mLabelBottom+mIconWidth/4] into tIconRect

      setIconPath(tIconRect, tIconPath)
      return tIconPath

   end if
end handler

private handler fetchRect(in pObject as String) returns Rectangle

   variable tRect as Rectangle
   variable tLeft as Real
   variable tTop as Real
   variable tRight as Real
   variable tBottom as Real
   variable tH as Real
   variable tSize as Real

   put my height into tH
   put tH*kActionRatio/2 into tSize

   if pObject is "title" then
      if mShowSubtitle is false then
         return rectangle [0, mLabelTop-mTitleVAdjust, my width, mLabelBottom-mTitleVAdjust]
      else
         put mSubtitleCenter-tH*mTitleRatio-kMargin/2 into tTop
         put mSubtitleCenter-kMargin/2 into tBottom
         return rectangle [0, tTop, my width, tBottom]
      end if

   else if pObject is "subtitle" then
      put kMargin/2 + mSubtitleCenter into tTop
      put kMargin/2 + mSubtitleCenter + tH*kSubtitleRatio into tBottom
      return rectangle [0, tTop, my width, tBottom]

   else if pObject is "leftLabel" then
      if mShowBackIcon is true then
         put 3*kMargin/2 + mIconWidth into tLeft
      else
         put kMargin into tLeft
      end if
      return rectangle [tLeft, mLabelTop, my width, mLabelBottom]

   else if pObject is "actionIcon" then
      put rectangle [mRight-5*mIconWidth/4, mLabelTop-mIconWidth/4, mRight+mIconWidth/4, mLabelBottom+mIconWidth/4] into tRect
      put (mRight - 5*mIconWidth/4 - 2*kMargin) into mRight
      return tRect

   else if pObject is "actionLabel" then

      put rectangle [mRight - mLetterCount*tSize, mLabelTop, mRight, mLabelBottom] into tRect
      put (mRight - mLetterCount*tSize - kMargin) into mRight
      return tRect

   end if
end handler

private handler fetchPaint(in pObject as String) returns Paint
   variable tPaint as Paint

   if pObject is "background" then
      put solid paint with color kBackgroundColor into tPaint

   else if pObject is "line" then
      put solid paint with color [0, 0, 0, 0.2] into tPaint

   else if pObject is "title" then
      put solid paint with color kBlackColor into tPaint

   else if pObject is "backButton" then
      if mSelectedAction is 1 then
         put solid paint with color [the red of mActionColor, the green of mActionColor, the blue of mActionColor, 0.5] into tPaint
      else
         put solid paint with mActionColor into tPaint
      end if

   else if pObject is "searchIcon" then
      if mSelectedAction is 2 then
         put solid paint with color [the red of mActionColor, the green of mActionColor, the blue of mActionColor, 0.5] into tPaint
      else
         put solid paint with mActionColor into tPaint
      end if

   end if

   return tPaint
end handler

private handler fetchFont(in pType as String) returns Font

   variable tSize as Number
   variable tFont as String

   if pType is "title" then
      put (my height)*mTitleRatio into tSize
      put "Helvetica Neue Medium" into tFont

   else if pType is "subtitle" then
      put (my height)*kSubtitleRatio into tSize
      put "Helvetica Neue Regular" into tFont

   else if pType is "label" then
      put (my height)*kActionRatio into tSize
      put "Helvetica Neue Regular" into tFont

   end if

   return font tFont at size tSize
end handler

private handler fetchSelectedItem() returns Integer

   variable tSelected as Integer
   variable tBackRect as Rectangle
   variable tSearchRect as Rectangle

   put fetchBackRect() into tBackRect
   put fetchSearchRect() into tSearchRect

   variable tClick as Point
   put the click position into tClick

   if tClick is within tBackRect then
      put 1 into tSelected
   else if tClick is within tSearchRect then
      put 2 into tSelected
   else
      put locToAction(tClick) into tSelected
   end if

   return tSelected

end handler

private handler fetchBackRect() returns Rectangle
   variable tBackRect as Rectangle

   if mShowLeftLabel is true then

      variable tLetterCount as Integer
      variable tSize as Real
      variable tWidth as Real

      put the number of chars in mLeftLabel into tLetterCount
      put (my height)*kActionRatio/2 into tSize
      put tLetterCount*tSize into tWidth

   end if

   if mShowBackIcon is true and mShowLeftLabel is false then
      put rectangle [kMargin, mLabelTop, kMargin + mIconWidth, mLabelBottom] into tBackRect
   else if mShowBackIcon is false and mShowLeftLabel is true then
      put rectangle [kMargin, mLabelTop, kMargin + tWidth, mLabelBottom] into tBackRect
   else if mShowBackIcon is true and mShowLeftLabel is true then
      put rectangle [kMargin, mLabelTop, kMargin + mIconWidth + tWidth, mLabelBottom] into tBackRect
   else
      put rectangle [0,0,0,0] into tBackRect
   end if

   return tBackRect
end handler

private handler fetchSearchRect() returns Rectangle
   variable tSearchRect as Rectangle

   if mShowSearchIcon is true then
      put rectangle [mRight - mIconWidth - kMargin, mLabelTop, mRight - kMargin, mLabelBottom] into tSearchRect
   else
      put rectangle [0,0,0,0] into tSearchRect
   end if

   return tSearchRect
end handler

private handler locToAction(in pLoc as Point) returns Integer
   variable tX as Integer

   repeat with tX from 1 up to the number of elements in mActionRects
      if pLoc is within (element tX of mActionRects) then
         return (tX + 2)
      end if
   end repeat

   return 0
end handler

-- this handler converts an RGBA color to a string
private handler colorToString(in pColor as Color) returns String

   variable tRed as Integer
   variable tGreen as Integer
   variable tBlue as Integer
   variable tAlpha as Integer

   put the rounded of ((the red of pColor)*255) into tRed
   put the rounded of ((the green of pColor)*255) into tGreen
   put the rounded of ((the blue of pColor)*255) into tBlue
   put the rounded of ((the alpha of pColor)*255) into tAlpha

   return stripZeros(tRed formatted as string) & "," & stripZeros(tGreen formatted as string) & \
      "," & stripZeros(tBlue formatted as string) & "," & stripZeros(tAlpha formatted as string)

end handler
--

-- this handler converts a string to an RGBA color
private handler stringToColor(in pString as String) returns Color

   variable tStringList as List
   variable tColorList as List
   variable tComponent

   split pString by "," into tStringList
   put the empty list into tColorList

   variable tComponentCount
	put the number of elements in tStringList into tComponentCount
	if tComponentCount is not 3 and tComponentCount is not 4 then
		-- Invalid number of components detected
		throw "Invalid color"
	end if

   repeat for each element tComponent in tStringList
      push (tComponent parsed as number)/255 onto back of tColorList
   end repeat

   return color tColorList

end handler
--

-- this handler strips the zeros when an integer is formatted as a string
private handler stripZeros(in pString as String) returns String
   if pString contains "." then
      repeat while ((the last char of pString) is in ".0")
         if the last char of pString is "." then
            delete the last char of pString
            exit repeat
         else
            delete the last char of pString
         end if
      end repeat
   end if
   return pString
end handler
--

-- this handler returns an array of the default action
private handler defaultActionsArray() returns Array
   variable tArray as Array
   put the empty array into tArray
   put "action" into tArray["name"]
   put "Action" into tArray["label"]
   put "circle"into tArray["icon_name"]
   return tArray
end handler
--

--------------------------------------------------------------------------------
--
-- Get Handlers
--
--------------------------------------------------------------------------------

-- this handler converts mHeaderActions into a form for output in the PI
private handler getHeaderActions() returns Array
   return listToArray(mHeaderActions)
end handler

private handler getActionNames() returns String
   return getDataElement("name", mHeaderActions)
end handler

private handler getActionLabels() returns String
   return getDataElement("label", mHeaderActions)
end handler

private handler getActionIcons() returns String
   return getDataElement("icon_name", mHeaderActions)
end handler

-- these handlers convert the color properties for output in the PI
private handler getActionColor() returns String
   return colorToString(mActionColor)
end handler

--------------------------------------------------------------------------------
--
-- Set Handlers
--
--------------------------------------------------------------------------------

private handler setHeaderTitle(in pTitle as String) returns nothing
   put pTitle into mHeaderTitle
   redraw all
end handler

private handler setHeaderSubtitle(in pSubtitle as String) returns nothing
   put pSubtitle into mHeaderSubtitle
   redraw all
end handler

-- this handler sets the actions, converting from a string to the correct format
private handler setHeaderActions(in pActions as Array) returns nothing
   setData(pActions, the keys of defaultActionsArray(), mHeaderActions)
   redraw all
end handler

private handler setActionNames(in pNames as String) returns nothing
   setPrimaryDataElement("name", pNames, defaultActionsArray(), mHeaderActions)
   redraw all
end handler

private handler setActionLabels(in pLabels as String) returns nothing
   setDataElement("label", pLabels, defaultActionsArray(), mHeaderActions)
   redraw all
end handler

private handler setActionIcons(in pIcons as String) returns nothing
   setDataElement("icon_name", pIcons, defaultActionsArray(), mHeaderActions)
   redraw all
end handler

private handler setActionSelectedIcons(in pSelectedIcons as String) returns nothing
   setDataElement("selected_icon_name", pSelectedIcons, defaultActionsArray(), mHeaderActions)
   redraw all
end handler

private handler setActionStyle(in pActionStyle as String) returns nothing
   put pActionStyle into mActionStyle
   redraw all
end handler

private handler setShowBackIcon(in pShow as Boolean) returns nothing
   put pShow into mShowBackIcon
   redraw all
end handler

private handler setLeftLabel(in pLabel as String) returns nothing
   put pLabel into mLeftLabel
   redraw all
end handler

private handler setActionColor(in pColor as String) returns nothing
   put stringToColor(pColor) into mActionColor
   redraw all
end handler

private handler setShowSearchIcon(in pShow as Boolean) returns nothing
   put pShow into mShowSearchIcon
   redraw all
end handler

private handler setOpaque(in pOpaque as Boolean) returns nothing
	if pOpaque is mOpaque then
		return
	end if

	put pOpaque into mOpaque
	redraw all
end handler

private handler setShowDivide(in pShowDivide as Boolean) returns nothing
	if pShowDivide is mShowDivide then
		return
	end if

	put pShowDivide into mShowDivide
	redraw all
end handler

--------------------------------------------------------------------------------
--
-- Data utilities
--
--------------------------------------------------------------------------------

private handler listToArray(in pList as List) returns Array
	variable tCount
	variable tArray
	put the empty array into tArray

	variable tKey as String
	repeat with tCount from 1 up to the number of elements in pList
		put tCount formatted as string into tKey
		put defaultActionsArray() into tArray[tKey]

		variable tAttribute as String
		repeat for each key tAttribute in tArray[tKey]
			put pList[tCount][tAttribute] into tArray[tKey][tAttribute]
		end repeat
	end repeat
	return tArray
end handler

private handler setData(in pArray as Array, in pKeys as List, out rList as List)
	-- Excessive hoop-jumping to work around the fact that keys of numerically keyed LCS array
	-- comes into LCB as strings, and therefore must be parsed as number to sort, then formatted back
	-- to be used as keys again. Because parse/format of integer doesn't currently roundtrip, we need to
	-- create a lookup from the original strings.
	variable tKey
	variable tOrder as List
	put the empty list into tOrder
	variable tLookup as Array
	put the empty array into tLookup
	variable tNumericKey as Number
	repeat for each key tKey in pArray
		put tKey parsed as number into tNumericKey
		push tNumericKey onto tOrder
		put tKey into tLookup[tNumericKey formatted as string]
	end repeat
	sort tOrder in ascending numeric order

	variable tOrderKey
	variable tElement as Array
	variable tList as List
	put the empty list into tList

	variable tOriginalKey as String
	repeat for each element tOrderKey in tOrder
		put the empty array into tElement
		repeat for each element tKey in pKeys
			put tLookup[tOrderKey formatted as string] into tOriginalKey
			put pArray[tOriginalKey][tKey] into tElement[tKey]
		end repeat
		if tElement["label"] is "" then
			put "New Item" into tElement["label"]
		end if

		push tElement onto tList
	end repeat

	put tList into rList

	redraw all
end handler

private handler getDataElement(in pElementName as String, in pList as List) returns String
	variable tList
	put the empty list into tList

	variable tElement
	repeat for each element tElement in pList
		push tElement[pElementName] onto tList
	end repeat

	variable tElements
	combine tList with "," into tElements
	return tElements
end handler

private handler setDataElement(in pElementName as String, in pElements as String, in pDefaultArray as Array, inout xList as List)
	variable tElements
	split pElements by "," into tElements

	variable tCount
	put 0 into tCount

	variable tElement
	repeat for each element tElement in xList
		add 1 to tCount
		if tCount > the number of elements in tElements then
			put pDefaultArray[pElementName] into xList[tCount][pElementName]
		else
			put tElements[tCount] into xList[tCount][pElementName]
		end if
	end repeat

	-- If we there are more of the given element than there are items, generate more
	add 1 to tCount
	repeat with tCount from tCount up to the number of elements in tElements
		push pDefaultArray onto xList
		put tElements[tCount] into xList[tCount][pElementName]
	end repeat

	redraw all
end handler

-- Only the primary data element ("name") can *reduce* the number of elements in the data list.
private handler setPrimaryDataElement(in pElementName as String, in pElements as String, in pDefaultArray as Array, inout xList as List)
	variable tElements
	split pElements by "," into tElements

	variable tCount
	put 0 into tCount

	variable tElement
	repeat for each element tElement in tElements
		add 1 to tCount
		if tCount > the number of elements in xList then
			push pDefaultArray onto xList
		end if

		put tElement into xList[tCount][pElementName]
	end repeat

	-- If we there are more items than there are primary elements, pop them off.
	add 1 to tCount
	repeat with tCount from tCount up to the number of elements in xList
		pop xList
	end repeat

	redraw all
end handler

end widget
