---------------------------------------------------------------------------------------
-- Carbonite UI code
-- Copyright 2007-2012 Carbon Based Creations, LLC
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
-- Carbonite - Addon for World of Warcraft(tm)
-- Copyright 2007-2012 Carbon Based Creations, LLC
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
---------------------------------------------------------------------------------------

local NotInitializedWins = {}
local L = LibStub("AceLocale-3.0"):GetLocale("Carbonite")

local HideFramesOnEsc = {}

function Nx:UIInit()

	local qc = {}
	self.QualityColors = qc

	for n = -1, 10 do		-- Blizz max is currently 7
		local r, g, b, hex = GetItemQualityColor (n)
		qc[n] = hex
	end

	qc[1] = "|cffe7e7e7"		-- Dim the white

	Nx.Font:Init()
	Nx.Skin:Init()

	Nx.Menu:Init()
	Nx.Window:Init()

	Nx.Button:Init()
	Nx.List:Init()
	Nx.DropDown:Init()
	Nx.ToolBar:Init()

--	Nx.GList:Init()
end

---------------------------------------------------------------------------------------

function Nx.strpos (haystack, needle, offset) 
  --local pattern = string.format("(%s)", needle)
  local i       = string.find (haystack, needle, (offset or 0), true)
  
  return (i ~= nil and i or false)
end

function Nx.prtStack (str)
	local s = debugstack (2, 3, 2)
	s = gsub (s, "Interface\\AddOns\\", "")
	Nx.prtD ("%s: %s", str, s)
end

---------------------------------------------------------------------------------------

function Nx.ArrayConcat(...) 
	local t = {}
	for n = 1,select("#",...) do
		local arg = select(n,...)
		if type(arg)=="table" then
			for _,v in ipairs(arg) do
				v._type = n
				t[#t+1] = v
			end
		else
			t[#t+1] = arg
		end
	end
	return t
end

---------------------------------------------------------------------------------------

function Nx.IsFriend(name)
	for i = 1, C_FriendList.GetNumFriends() do
		local finfo = C_FriendList.GetFriendInfoByIndex (i)
		if finfo.name == name then
		   return true
		end
   end
end

---------------------------------------------------------------------------------------
-- Chat printing
---------------------------------------------------------------------------------------

function Nx:prtGetChatFrames()

	local t = {}

	for n = 1, 10 do
		local cfrm = _G["ChatFrame" .. n]
		if cfrm and cfrm["name"] then
			tinsert (t, cfrm["name"])
--			Nx.prt ("cfrm %s %s", n, cfrm["name"] or "nil")
		end
	end

	sort (t)

--	Nx.prtVar ("", t)

	return t
end

function Nx:prtSetChatFrame()
	Nx.prtChatFrm = DEFAULT_CHAT_FRAME
	for n = 1, 10 do
		local cfrm = _G["ChatFrame" .. n]
		if cfrm then
			if cfrm["name"] == Nx.db.profile.General.ChatMsgFrm then
				Nx.prtChatFrm = cfrm
			end
		end
	end
end

function Nx.prt (...)
	local args = {...}
	local i = 1
	-- replace missing/erroneous placeholders
	function replace_placeholders(placeholder, item)
		i = i + 1
		--if (args[i] == nil) then
		--	return '[missing argument]'
		--end
		if item ~= 's' and item ~= 'q' then
			if type(args[i]) == 'string' and string.match(args[i], '^[.0-9]+$') then
				return placeholder
			elseif type(args[i]) ~= 'number' then
				return '[not a number]'
			end
		end
		return placeholder
	end
	args[1] = string.gsub((args[1] or 'nil'), '(%%[0-9.]*([cdeEfgGioqsuxX]))', replace_placeholders)
	
	-- convert boolean and nil to string
	for n = 1, #args do
		if n > 1 then
			if type(args[n]) == 'boolean' or type(args[n]) == 'nil' then
				args[n] = tostring(args[n])
			end
		end
	end
	
	local f = Nx.prtChatFrm or DEFAULT_CHAT_FRAME
	f:AddMessage ("|cffff0000[" .. Nx.TXTBLUE..L["Carbonite"].."|cffff0000] |cffffffff".. format (unpack(args)), 1, 1, 1)
end

function Nx.prtraw (msg)
	local f = Nx.prtChatFrm or DEFAULT_CHAT_FRAME
--	msg = debugstack(2,3,2)
	f:AddMessage (Nx.TXTBLUE..L["Carbonite"].." |cffffffff".. msg, 1, 1, 1)
end

function Nx.prtError (msg, ...)
	UIErrorsFrame:AddMessage (format (msg, ...), 1, 1, 0)
end

-- Debug print
function Nx.prtD (...)
	if Nx.DebugOn then
		Nx.prt (...)
	end
end

function Nx.prtCtrl (msg, ...)
	if Nx.DebugOn and IsControlKeyDown() then
		Nx.prt (msg, ...)
	end
end

function Nx.prtTable (msg, s)

	Nx.prt (msg.." Table: "..type (s))

	if type (s) == "table" then
		for k, v in pairs (s) do
			if type (v) ~= "table" then
				Nx.prtVar (" "..k, v)
			else
				Nx.prt (" "..k.." table")
			end
		end
	end
end

function Nx.prtVar (msg, v)

	local prt = Nx.prt

	if v == nil then
		prt (msg.." nil")
	elseif type (v) == "boolean" then
		prt (msg.." "..tostring (v))
	elseif type (v) == "number" then
		prt (format ("%s #%d (0x%x)", msg, v, v))
	elseif type (v) == "string" then
		local s = gsub (v, "%%", "%%%%")
		prt (msg.. " '" .. s .."'")
	elseif type (v) == "table" then
		Nx.prtTable (msg, v)
	else
		prt (msg.." ? "..tostring (v))
	end
end

function Nx.prtStrHex (msg, str)

	local prt = Nx.prt
	prt (msg..":")

	for n = 1, #str, 4 do

		local s = ""

		for n2 = n, min (#str, n + 3) do
			s = s .. format (" %x", strbyte (str, n2))
		end

		prt (s)
	end
end

function Nx.prtFrame (msg, frm)

	local prt = Nx.prt
	local parent = frm:GetParent()

--	prt (msg.." Frame: %s", frm:GetName() or "nil")

	prt (msg..L[" Frame: %s Shown%d Vis%d P>%s"], frm:GetName() or "nil",
			frm:IsShown() or 0, frm:IsVisible() or 0, parent and parent:GetName() or "nil")
	prt (L[" EScale %f, Lvl %f"], frm:GetEffectiveScale(), frm:GetFrameLevel())
	prt (L[" LR %f, %f"], frm:GetLeft() or -999, frm:GetRight() or -999)
	prt (L[" BT %f, %f"], frm:GetBottom() or -999, frm:GetTop() or -999)

	local reg = { frm:GetRegions() }
	for n, o in ipairs (reg) do

		local str = ""
		if o:IsObjectType ("Texture") then
			str = o:GetTexture()
		end
		prt ("  %d %s: %s", n, o:GetObjectType(), str)
	end
end

function Nx.prtFrameChildren (msg, frm, lvl)

	local prt = Nx.prt

	lvl = lvl or 1

	if msg then
		prt (format ("FrameChildren (%s)", msg))
	end

	local pad = ""

	for n = 1, lvl do
		pad = pad.." "
	end

	local ch = { frm:GetChildren() }

	for n = 1, #ch do

		local c = ch[n]

		if c:IsObjectType ("Frame") then

			prt (L["%s#%d %s ID%s (%s) show%d l%d x%d y%d"], pad, n, c:GetName() or "nil",
				c:GetID() or "nil", c:GetObjectType(),
				c:IsShown() or 0, frm:GetFrameLevel(),
				c:GetLeft() or -99999, c:GetTop() or -99999
				)


			Nx.prtFrameChildren (nil, c, lvl + 1)
		end
	end
end

---------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------
-- Make the first letter a cap and the rest lower case
---------------------------------------------------------------------------------------

function Nx.Util_CapStr (str)
	return strupper (strsub (str, 1, 1)) .. strlower (strsub (str, 2))
end

function Nx.Util_CleanName (name)
	name = Nx.Util_CapStr (name)
	name = gsub (name, "[~%^]", "")
	return name
end

---------------------------------------------------------------------------------------
-- Count table entries
---------------------------------------------------------------------------------------

function Nx.Util_tcount (t)

	local n = 0
	if t then
		for k, v in pairs (t) do
			n = n + 1
		end
	end

	return n
end

function Nx.Util_tcountrecurse (t)

	local n = 0
	if t then
		for k, v in pairs (t) do
			n = n + 1
			if type (v) == "table" then
				n = n + Nx.Util_tcountrecurse (v)
			end
		end
	end

	return n
end

---------------------------------------------------------------------------------------
-- Copy table entries recursively
---------------------------------------------------------------------------------------

function Nx.Util_TCopyRecurse (t)

	local tc = {}

	for k, v in pairs (t) do
		if type (v) == "table" then
			tc[k] = Nx.Util_TCopyRecurse (v)
		else
			tc[k] = v
		end
	end

	return tc
end

---------------------------------------------------------------------------------------
-- Find item index in table
-- (table, search item)
---------------------------------------------------------------------------------------

function Nx.Util_TFindItemI (t, item)

	for i, v in ipairs (t) do
		if v == item then
			return i
		end
	end
end

---------------------------------------------------------------------------------------
-- Move a indexed table index lower or higher
-- (table, index, not nil to move to lower index)
---------------------------------------------------------------------------------------

function Nx.Util_TMoveI (t, i, low)

	if low then
		if i > 1 then
			t[i-1], t[i] = t[i], t[i-1]
			return i - 1
		end
	else
		if i < #t then
			t[i+1], t[i] = t[i], t[i+1]
			return i + 1
		end
	end
end

---------------------------------------------------------------------------------------
-- Move a indexed table item lower or higher
-- (table, match item, not nil to move to lower index)
---------------------------------------------------------------------------------------

function Nx.Util_TMoveItem (t, item, low)

	for i, v in ipairs (t) do
		if v == item then
			if low then
				if i > 1 then
					t[i-1], t[i] = t[i], t[i-1]
					return i - 1
				end
			else
				if i < #t then
					t[i+1], t[i] = t[i], t[i+1]
					return i + 1
				end
			end
			return
		end
	end
end

---------------------------------------------------------------------------------------
-- Serialize a table to a string
---------------------------------------------------------------------------------------

function Nx.Util_t2strRecurse (t)

	local str = ""

	if t then

		str = "{"

		for k, v in pairs (t) do

			local kStr = k

			if type (k) == "string" then
				kStr = format ("\"%s\"", k)
			end

			if type (v) == "table" then
				str = str .. format ("[%s]=%s,", kStr, Nx.Util_t2strRecurse (v))

			elseif type (v) == "string" then
				str = str .. format ("[%s]=\"%s\",", kStr, v)

			else
				str = str .. format ("[%s]=%s,", kStr, v)
			end
		end

		str = str .. "}"
	end

	return str
end

---------------------------------------------------------------------------------------
-- Convert hex color number to R G B A floats (0-1)
-- (RRGGBBAA number)
---------------------------------------------------------------------------------------

function Nx.Util_dec2hex(input)
	local base,key,output,remainder=16,"0123456789ABCDEF","",0
	while input>0 do			
		input,remainder=floor(input/base), mod(input,base)+1
		output=strsub(key,remainder,remainder)..output
	end
	
	return output..strrep("0", 2 - strlen(output))
end

function Nx.Util_str2rgba (colors)
	local arr = { Nx.Split("|",colors) }
	return arr[1], arr[2], arr[3], arr[4]
end

function Nx.Util_str2rgb (colors)
	local arr = { Nx.Split("|",colors) }
	return arr[1], arr[2], arr[3]
end

---------------------------------------------------------------------------------------
-- Convert hex color number to alpha float (0-1)
-- (RRGGBBAA number)
---------------------------------------------------------------------------------------

function Nx.Util_str2a (colors)
	local arr = { Nx.Split("|",colors) }
	return arr[4]
end

---------------------------------------------------------------------------------------
-- Convert hex color number to color string
-- (RGBA number)
---------------------------------------------------------------------------------------

function Nx.Util_str2colstr (colors)
	local arr = { Nx.Split("|",colors) }
	return format ("|c%02x%02x%02x%02x",arr[4]*255,arr[1]*255,arr[2]*255,arr[3]*255)
end

---------------------------------------------------------------------------------------
-- Convert color table
---------------------------------------------------------------------------------------

function Nx.Util_coltrgb2colstr (colors)

	local t = {}

	for k, v in pairs (colors) do
		t[k] = format ("|cff%02x%02x%02x", v.r * 255, v.g * 255, v.b * 255)
	end

	return t
end

---------------------------------------------------------------------------------------
-- Step a value to the target value by a step
-- ret new value
---------------------------------------------------------------------------------------

function Nx.Util_StepValue (value, target, step)

	if value < target then
		value = value + step
		if value > target then
			value = target
		end

	elseif value > target then
		value = value - step
		if value < target then
			value = target
		end
	end

	return value
end

---------------------------------------------------------------------------------------
-- Check if mouse is over a frame
-- (frame)
-- ret XY offsets from bottom left corner or nil if not over
---------------------------------------------------------------------------------------

function Nx.Util_IsMouseOver (frm)

	local x, y = GetCursorPosition()
	x = x / frm:GetEffectiveScale()

	local left = frm:GetLeft()
	local right = frm:GetRight()

	if x >= left and x <= right then

		y = y / frm:GetEffectiveScale()

		local top = frm:GetTop()
		local bottom = frm:GetBottom()

		if y >= bottom and y <= top then
			return x - left, y - bottom
		end
	end
end

---------------------------------------------------------------------------------------
-- Get mouse position relative to a frame
-- (frame)
-- ret XY offsets from bottom left corner
---------------------------------------------------------------------------------------

function Nx.Util_GetMouseClampedXY (frm)

	local x, y = GetCursorPosition()
	x = x / frm:GetEffectiveScale()

	local left = frm:GetLeft()
	local right = frm:GetRight()

	x = max (x, left)
	x = min (x, right)

	y = y / frm:GetEffectiveScale()

	local top = frm:GetTop()
	local bottom = frm:GetBottom()

	y = max (y, bottom)
	y = min (y, top)

	return x - left, y - bottom
end

---------------------------------------------------------------------------------------
-- Clamp frame to screen
-- (frame)
-- ret XY offsets from bottom left corner
---------------------------------------------------------------------------------------

function Nx.Util_SnapToScreen (frm)

	local sw = GetScreenWidth()
	local sh = GetScreenHeight()

	local atPt, relTo, relPt, x, y = frm:GetPoint()

	local sc = frm:GetScale()
	local l = frm:GetLeft() * sc		-- Scale does not matter for left or bottom
	local r = frm:GetRight() * sc
	local t = frm:GetTop() * sc
	local b = frm:GetBottom() * sc

	local dist = 4

	if abs (l - 0) < dist then
		x = x - l / sc
	end

	if abs (r - sw) < dist then
		x = x - (r - sw) / sc
	end

	if MultiBarLeft:IsVisible() then

		local ml = MultiBarLeft:GetLeft()

		if abs (r - ml) < dist then
			x = x - (r - ml) / sc
		end
	end

	if MultiBarRight:IsVisible() then

		local ml = MultiBarRight:GetLeft()

		if abs (r - ml) < dist then
			x = x - (r - ml) / sc
		end
	end

	if abs (b - 0) < dist then
		y = y - b / sc
	end

	if abs (t - sh) < dist then
		y = y - (t - sh) / sc
	end

	frm:SetPoint (atPt, x, y)

	return nil
end

---------------------------------------------------------------------------------------
-- Recursively set child levels
---------------------------------------------------------------------------------------

function Nx.Util_SetChildLevels (frm, lvl)

	if frm:GetNumChildren() > 0 then

		local ch = { frm:GetChildren() }

		for n, chf in pairs (ch) do

			chf:SetFrameLevel (lvl)

			if chf:GetNumChildren() > 0 then
				Nx.Util_SetChildLevels (chf, lvl + 1)
			end
		end
	end
end

---------------------------------------------------------------------------------------
-- Get a string from a money value
---------------------------------------------------------------------------------------

function Nx.Util_GetMoneyStr (money)

	if not money then
		return "|cffff4040?"
	end

	if money == 0 then
		return "0"
	end

	local pre = money > 0 and "" or "-"

	money = abs (money)

	local str = ""

	local g = floor (money / 10000)
	if g > 0 then
		str = format ("|cffffff00%dg", g)
	end

	local s = mod (floor (money / 100), 100)
	if s > 0 then
		str = format ("%s |cffbfbfbf%ds", str, s)
	end

	local c = mod (money, 100)
	if c > 0 then
		str = format ("%s |cff7f7f00%dc", str, c)
	end

	return pre .. strtrim (str)
end

---------------------------------------------------------------------------------------
-- Get a string from a seconds
---------------------------------------------------------------------------------------

function Nx.Util_GetTimeElapsedStr (seconds)

	local secs = seconds
	local mins = secs / 60 % 60
	local hours = secs / 3600

	if hours > 24 then
		return format (L["%.1f days"], hours / 24)

	elseif hours >= 1 then
		return format (L["%.1f hours"], hours)
	end

	return format (L["%d mins"], mins)
end

function Nx.Util_SecondsToDays (seconds)
	fdays = math.floor(seconds/86400)
	fhours = math.floor((bit.mod(seconds,86400))/3600)
	fminutes = math.floor(bit.mod((bit.mod(seconds,86400)),3600)/60)
	fseconds = math.floor(bit.mod(bit.mod((bit.mod(seconds,86400)),3600),60))
	return fdays.." days, "..fhours.." hours, "..fminutes.." minutes, "..fseconds.." seconds"
end


---------------------------------------------------------------------------------------
-- Get a string from a seconds in 00:00 minute:second format
---------------------------------------------------------------------------------------

function Nx.Util_GetTimeElapsedMinSecStr (seconds)
	return format ("%d:%02d", seconds / 60 % 60, seconds % 60)
end

---------------------------------------------------------------------------------------
-- Nan to Zero conversion
---------------------------------------------------------------------------------------

function Nx.Util_NanToZero (somevar)
	if value == math.huge or somevar == -math.huge then return 0 end
	
	return (somevar ~= somevar and 0 or somevar)
end

---------------------------------------------------------------------------------------
-- Parse text and set for tooltip
---------------------------------------------------------------------------------------

function Nx:SetTooltipText (str)

	if strbyte (str) == 33 then		-- ! (item)

--		Nx.prt ("Item %s", str)

		local link, s = Nx.Split ("^", str)

		if not s or #s < 1 or IsAltKeyDown() then
			str = strsub (link, 2)
			Nx.Item:ShowTooltip (str, true)
			return
		end

		str = s

	elseif strbyte (str) == 64 then		-- @ (quest)

		str = "quest:" .. strsub (str, 2)
		Nx.Item:ShowTooltip (str, true)
		return

	elseif strbyte (str) == 35 then		-- # (enchant)

		str = strsub (str, 2)
--		Nx.prt (str)
		GameTooltip:SetHyperlink (str)
		GameTooltip_ShowCompareItem()
		return
	end

	local s1, s2 = strfind (str, "\n")
	if s1 then

		local t = { Nx.Split ("\n", str) }

		GameTooltip:SetText (t[1], 1, 1, 1, 1, true)			-- Wrap text
		tremove (t, 1)

		for _, line in ipairs (t) do

			local s1, s2 = Nx.Split ("\t", line)
			if s2 then
				GameTooltip:AddDoubleLine (s1, s2, 1, 1, 1, 1, 1, 1)
			else
				GameTooltip:AddLine (line, 1, 1, 1, true)	-- Wrap text
			end
		end

--[[
		local s = strsub (str, 1, s1 - 1)
--		Nx.prt ("Tool %s", s)
		GameTooltip:SetText (s, 1, 1, 1, 1, true)

		s = strsub (str, s2 + 1)
		GameTooltip:AddLine (s, 1, 1, 1, true)
--]]
		GameTooltip:Show()

	else
		GameTooltip:SetText (str, 1, 1, 1, 1, true)			-- Wrap text
	end
end

---------------------------------------------------------------------------------------
-- Play a sound file if sounds enabled
---------------------------------------------------------------------------------------

function Nx:PlaySoundFile (file)

	if GetCVar ("Sound_EnableSFX") ~= "0" then
		PlaySoundFile (file)
	end
end


---------------------------------------------------------------------------------------
-- Font stuff
---------------------------------------------------------------------------------------

function Nx.Font:Init()

	self.Inited = true

	-- Some code uses the "Nx" named font directly

	self.Fonts = {
		["Font.Small"] = { "NxFontS", "GameFontNormalSmall", "db" },
		["Font.Medium"] = { "NxFontM", "GameFontNormal", "db" },
		["Font.Map"] = { "NxFontMap", "GameFontNormalSmall", "db" },
		["Font.MapLoc"] = { "NxFontMapLoc", "GameFontNormalSmall", "db" },
		["Font.Menu"] = { "NxFontMenu", "GameFontNormalSmall", "db" },
	}

	self.Faces = {
		{ "Arial", "Fonts\\ARIALN.TTF", },
		{ "Friz", "Fonts\\FRIZQT__.TTF", },
		{ "Morpheus", "Fonts\\MORPHEUS.TTF", },
		{ "Skurri", "Fonts\\SKURRI.TTF", }
	}

	-- Predefine so we dont get dups
	self.AddonFonts = {
		["Arial Narrow"] = true,
		["Friz Quadrata TT"] = true,
		["Morpheus"] = true,
		["Skurri"] = true,
	}

	for name, v in pairs (self.Fonts) do

		local font = CreateFont (v[1])
		v.Font = font
		font:SetFontObject(v[2])
		v.Font.db = v[3]
	end

	self:Update()
end

function Nx.Font:ModuleAdd (key, value)
  self.Fonts[key] = value
  local font = CreateFont (value[1])
  value.Font = font
  font:SetFontObject(value[2])
  value.Font.db = value[3]
  self:Update()
end

function Nx.Font:AddonLoaded()

	if not self.Inited then
		return
	end

	local found

	found = self:FontScan ("LibSharedMedia-3.0")

	if found then
		self:Update()
	end
end

function Nx.Font:FontScan (libName)

	local sm

	sm = LibStub(libName)

	if sm then

		local found

		local fonts = sm.List(sm, "font")

--		Nx.prtVar ("SM", fonts)

		for k, name in ipairs (fonts) do

			if not self.AddonFonts[name] then
				found = true
--				Nx.prtVar ("Font", name)
--				Nx.prtVar ("Fetch", sm["Fetch"] (sm, "font", name))
				self.AddonFonts[name] = sm.Fetch(sm, "font", name)
				tinsert (self.Faces, { name, self.AddonFonts[name] })
			end
		end

		return found
	end
end

function Nx.Font:GetObj (name)
	return self.Fonts[name].Font
end

function Nx.Font:GetH (name)
--	Nx.prt ("Font %s", name or "nil")
	return self.Fonts[name].H
end

function Nx.Font:GetIndex (name)
	for k, v in ipairs (self.Faces) do
		if v[1] == name then
			return k
		end
	end
	return 1
end

function Nx.Font:GetName (index)
	local t = self.Faces[index]
	return t and t[1]
end

function Nx.Font:GetFile (name)

	for k, v in ipairs (self.Faces) do
		if v[1] == name then
			return v[2]
		end
	end

	return self.Faces[2][2]		-- Default to friz
end

function Nx.Font:Update()

	for name, v in pairs (self.Fonts) do

		local font = v.Font
		local dbloc = v.Font.db
		local fname, size, flags = font:GetFont()
		local optname = "Nx." .. dbloc .. ".profile." .. name
--		Nx.prt ("Font %s %s %s", fname, size, flags)
		local parts1, parts2 = Nx.Split(".",name)
		local file = self:GetFile (Nx[dbloc].profile[parts1][parts2])
		local size = Nx[dbloc].profile[parts1][parts2 .. "Size"]
		font:SetFont (file, size, flags)
		v.H = max (size + (Nx[dbloc].profile[parts1][parts2 .. "Spacing"] or 0), 6)
	end

	Nx.List:NextUpdateFull()
	Nx.Window:AdjustAll()
end

---------------------------------------------------------------------------------------
-- Skin stuff
---------------------------------------------------------------------------------------

function Nx.Skin:Init()

	Nx.Skins = {
		["Blackout"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\Addons\\Carbonite\\Gfx\\Skin\\EdgeSquare",
				["tile"] = true,
				["tileSize"] = 8,
				["edgeSize"] = 8,
				["insets"] = { ["left"] = 0, ["right"] = 0, ["top"] = 0, ["bottom"] = 0 }
			},
			["BdCol"] = "0|0|0|1",
			["BgCol"] = "0|0|0|1",
		},
		["Blackout Blues"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\Tooltips\\UI-Tooltip-Border",
				["tile"] = true,
				["tileSize"] = 9,
				["edgeSize"] = 9,
				["insets"] = { ["left"] = 1, ["right"] = 1, ["top"] = 1, ["bottom"] = 1 }
			},
			["BdCol"] = ".8|.8|1|1",
			["BgCol"] = "0|0|0|1",
		},
		["Dialog Blue"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\DialogFrame\\UI-DialogBox-Border",
				["tile"] = true,
				["tileSize"] = 16,
				["edgeSize"] = 16,
				["insets"] = { ["left"] = 2, ["right"] = 2, ["top"] = 2, ["bottom"] = 2 }
			},
			["BdCol"] = ".8|.8|1|1",
			["BgCol"] = ".125|.125|.125|.88",
		},
		["Dialog Gold"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\DialogFrame\\UI-DialogBox-Gold-Border",
				["tile"] = true,
				["tileSize"] = 16,
				["edgeSize"] = 16,
				["insets"] = { ["left"] = 2, ["right"] = 2, ["top"] = 2, ["bottom"] = 2 }
			},
			["BdCol"] = "1|1|1|1",
			["BgCol"] = ".15|.15|0|.88",			-- { .15, .15, 0, .88 },
		},
		["Simple Blue"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\Addons\\Carbonite\\Gfx\\Skin\\EdgeSquare",
--				["edgeFile"] = "Interface\\PVPFrame\\UI-Character-PVP-Highlight",
				["tile"] = true,
				["tileSize"] = 8,
				["edgeSize"] = 8,
				["insets"] = { ["left"] = 0, ["right"] = 0, ["top"] = 0, ["bottom"] = 0 }
			},
			["BdCol"] = "0.7|0.7|1|0.8",
			["BgCol"] = ".125|.125|.125|.88",		-- { .125, .125, .125, .88 },
		},
		["Stone"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\Glues\\Common\\TextPanel-Border",
--				["tile"] = true,
				["tileSize"] = 256,
				["edgeSize"] = 16,
				["insets"] = { ["left"] = 3, ["right"] = 2, ["top"] = 2, ["bottom"] = 2 }
			},
			["BdCol"] = "1|1|1|1",
			["BgCol"] = "0.06|0.06|0.06|.9",
		},
		["Tool Blue"] = {
			["Folder"] = "",
			["WinBrH"] = "WinBrH",
			["WinBrV"] = "WinBrV",
			["TabOff"] = "TabOff",
			["TabOn"] = "TabOn",
			["Backdrop"] = {
				["bgFile"] = "Interface\\Buttons\\White8x8",
				["edgeFile"] = "Interface\\Tooltips\\UI-Tooltip-Border",
--				["edgeFile"] = "Interface\\Minimap\\TooltipBackdrop",
				["tile"] = true,
				["tileSize"] = 9,
				["edgeSize"] = 9,
				["insets"] = { ["left"] = 1, ["right"] = 1, ["top"] = 1, ["bottom"] = 1 }
			},
			["BdCol"] = ".8|1|1|.8",
			["BgCol"] = ".125|.125|.125|.88",		-- { .125, .125, .125, .88 },
		},
	}
	self:Set (Nx.db.profile.Skin["Name"], true)
end

function Nx.Skin:Set (skinName, init)

	self.Data = Nx.Skins[skinName or ""]

	if not self.Data then
		skinName = "Tool Blue"
		self.Data = Nx.Skins[skinName]
	end

	Nx.db.profile.Skin.Name = skinName

	local data = self.Data

	self.Path = "Interface\\Addons\\Carbonite\\Gfx\\Skin\\" .. data["Folder"]

	if not init then
		Nx.db.profile.Skin.WinBdColor = data["BdCol"]
		Nx.db.profile.Skin.WinFixedBgColor = ".5|.5|.5|.5"
		Nx.db.profile.Skin.WinSizedBgColor = data["BgCol"]
	end

	self:Update()
end

function Nx.Skin:Update()
	self.BdCol = { Nx.Util_str2rgba (Nx.db.profile.Skin.WinBdColor) }
	self.BgCol = { Nx.Util_str2rgba (Nx.db.profile.Skin.WinSizedBgColor) }
	self.FixedBgCol = { Nx.Util_str2rgba (Nx.db.profile.Skin.WinFixedBgColor) }

	Nx.Window:ResetBackdrops()
	Nx.Menu:ResetSkins()
end

function Nx.Skin:GetBackdrop()
	return self.Data["Backdrop"]
end

function Nx.Skin:GetBorderCol()
	return self.BdCol
end

function Nx.Skin:GetBGCol()
	return self.BgCol
end

function Nx.Skin:GetFixedSizeBGCol()
	return self.FixedBgCol
end

function Nx.Skin:GetTex (txName)
	return self.Path .. txName
end

---------------------------------------------------------------------------------------
-- Window - A frame with a title and borders that can be moved and resized
---------------------------------------------------------------------------------------

function Nx.Window:Init()

	local wd = Nx:GetData ("Win")

	if not wd.Version or wd.Version < Nx.VERSIONWin then

		if wd.Version then
			Nx.prt (L["Reset old layout data"])
		end
		wd.Version = Nx.VERSIONWin

		for k, win in pairs (wd) do
			if type (win) == "table" then
--				Nx.prt (" Reset %s", k)
				wd[k] = nil
			end
		end
	end

	self.Wins = {}		-- All created windows

	self.BORDERW = 7
	self.BORDERH = 7

	self.Borders = {
		"TOPLEFT", "TOPRIGHT", 1, self.BORDERH, "WinBrH",
		"BOTTOMLEFT", "BOTTOMRIGHT", 1, self.BORDERH, "WinBrH",
		"TOPLEFT", "BOTTOMLEFT", self.BORDERW, 1, "WinBrV",
		"TOPRIGHT", "BOTTOMRIGHT", self.BORDERW, 1, "WinBrV",
	}

	self.SideNames = {
		"LEFT", "RIGHT", "", "TOP", "TOPLEFT", "TOPRIGHT", "",
		"BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT"
	}

	self.StrataNames = {
		"LOW", "MEDIUM", "HIGH", "DIALOG",
		["LOW"] = 1, ["MEDIUM"] = 2, ["HIGH"] = 3, ["DIALOG"] = 4,
	}

	local menu = Nx.Menu:Create (UIParent)
	self.Menu = menu

	self.MenuIHideInCombat = menu:AddItem (0, L["Hide In Combat"], self.Menu_OnHideInCombat, self)
	self.MenuILock = menu:AddItem (0, L["Lock"], self.Menu_OnLock, self)
	self.MenuIFadeIn = menu:AddItem (0, L["Fade In"], self.Menu_OnFadeIn, self)
	self.MenuIFadeOut = menu:AddItem (0, L["Fade Out"], self.Menu_OnFadeOut, self)
	self.MenuILayer = menu:AddItem (0, L["Layer"], self.Menu_OnLayer, self)
	self.MenuIScale = menu:AddItem (0, L["Scale"], self.Menu_OnScale, self)
	self.MenuITrans = menu:AddItem (0, L["Transparency"], self.Menu_OnTrans, self)

	local function func (item)
		self.MenuWin:ResetLayout()
	end

	menu:AddItem (0, L["Reset Layout"], func, self)
end

---------------------------------------------------------------------------------------

function Nx.Window:Menu_OnHideInCombat (item)
	self.MenuWin.SaveData["HideC"] = item:GetChecked()
end

function Nx.Window:Menu_OnLock (item)
	self.MenuWin:Lock (item:GetChecked())
end

function Nx.Window:Menu_OnFadeIn (item)

	local v = item:GetSlider()
	local svdata = self.MenuWin.SaveData

	svdata["FI"] = v
	self.MenuWin.BackgndFadeIn = v
end

function Nx.Window:Menu_OnFadeOut (item)

	local v = item:GetSlider()
	local svdata = self.MenuWin.SaveData

	svdata["FO"] = v
	self.MenuWin.BackgndFadeOut = v
end

function Nx.Window:Menu_OnLayer (item)

	local layer = item:GetSlider()
	self.MenuWin:SetFrmStrata (layer)
end

function Nx.Window:Menu_OnScale (item)

	local scale = item:GetSlider()
	local svdata = self.MenuWin.SaveData

	svdata[self.MenuWin.LayoutMode.."S"] = scale

	local f = self.MenuWin.Frm
	local s = f:GetScale()
	local x = f:GetLeft() * s
	local y = GetScreenHeight() - f:GetTop() * s

	f:ClearAllPoints()
	f:SetPoint ("TOPLEFT", x / scale, -y / scale)

	f:SetScale (scale)
end

function Nx.Window:Menu_OnTrans (item)

	local trans = item:GetSlider()
	local svdata = self.MenuWin.SaveData
	svdata[self.MenuWin.LayoutMode.."T"] = trans < 1 and trans or nil

	local f = self.MenuWin.Frm
	f:SetAlpha (trans)
end

---------------------------------------------------------------------------------------
-- Reset layouts of all created windows
---------------------------------------------------------------------------------------

function Nx.Window:ResetLayouts()

	for win, v in pairs (self.Wins) do
		win:ResetLayout()
	end
end

---------------------------------------------------------------------------------------
-- Check if ok to copy all window saved data from one character to another
---------------------------------------------------------------------------------------

function Nx.Window:CopyLayoutsCheck (swd, dwd)

	if dwd.Version and (not swd.Version or swd.Version < dwd.Version) then
		Nx.prt (L["Window version mismatch!"])
		return
	end

	self.SaveDisabled = true

	return true
end

---------------------------------------------------------------------------------------
-- Clear a window's saved data
---------------------------------------------------------------------------------------

function Nx.Window:ClrSaveData (name)

	local wd = Nx:GetData ("Win")
	wd[name] = nil
end

---------------------------------------------------------------------------------------
-- Force adjust of all created windows
---------------------------------------------------------------------------------------

function Nx.Window:AdjustAll()

	if self.Wins then
		for win in pairs (self.Wins) do
			win:Adjust()
		end
	end
end

---------------------------------------------------------------------------------------
-- Update hide status for combat
---------------------------------------------------------------------------------------

function Nx.Window:UpdateCombat()

	local combat = UnitAffectingCombat ("player")

	if self.Wins then

		for win in pairs (self.Wins) do
			if win.SaveData["HideC"] then

--				Nx.prt ("UCombat %s", win.Name)

				if combat then
					win.Frm:Hide()
				else
					if not win.SaveData["Hide"] and not win.RaidHid then
						win.Frm:Show()
					end
				end
			end
		end
	end
end

---------------------------------------------------------------------------------------
-- Position
---------------------------------------------------------------------------------------

function Nx.Window:ConsolePos (str)

	local name, x, y = self:ParseConsole (str)
	if not (x and y) then
		Nx.prt (L["XY missing (%s)"], str)
		return
	end

	local win = self:FindNoCase (name)
	if win then
		win:SetPos (x, -y)
		return
	end

	Nx.prt (L["Window not found (%s)"], str)
end

---------------------------------------------------------------------------------------
-- Show
---------------------------------------------------------------------------------------

function Nx.Window:ConsoleShow (str)

	local name, mode = self:ParseConsole (str)

	local win = self:FindNoCase (name)
	if win then
		if not mode then
			win:Show (not win:IsShown())
		elseif mode == 0 then
			win:Show (false)
		else
			win:Show()
		end
		return
	end

	Nx.prt (L["Window not found (%s)"], str)
end

---------------------------------------------------------------------------------------
-- Size
---------------------------------------------------------------------------------------

function Nx.Window:ConsoleSize (str)

	local name, x, y = self:ParseConsole (str)
	if not (x and y) then
		Nx.prt (L["XY missing (%s)"], str)
		return
	end

	local win = self:FindNoCase (name)
	if win then
		win:SetTotalSize (x, y)
		return
	end

	Nx.prt (L["Window not found (%s)"], str)
end

---------------------------------------------------------------------------------------
-- Parse
---------------------------------------------------------------------------------------

function Nx.Window:ParseConsole (str)

	local str = gsub (strlower (str), ",", " ")

	local name
	local x, y

	for s in gmatch (str, "%S+") do
		local i = tonumber (s)
		if i then
			if x then
				y = y or i
			else
				x = i
			end
		else
			if name then
				name = name .. " " .. s
			else
				name = s
			end
		end
	end

	local names = {
		["map"] = "NxMap1",
--		["watch"] = "NxQuestWatch",
	}

	return names[name] or name, x, y
end

---------------------------------------------------------------------------------------
-- Find a window by name. Case insensitive
---------------------------------------------------------------------------------------

function Nx.Window:FindNoCase (name)

	if self.Wins and name then
		name = strlower (name)
		for win in pairs (self.Wins) do
			if strlower (win.Name) == name then
				return win
			end
		end
	end
end

---------------------------------------------------------------------------------------
-- Find a window by name
---------------------------------------------------------------------------------------

function Nx.Window:Find (name)

	-- Use a name table?

	if self.Wins then
		for win in pairs (self.Wins) do
			if win.Name == name then
				return win
			end
		end
	end
end

---------------------------------------------------------------------------------------
-- Set fade values used for next window create
---------------------------------------------------------------------------------------

function Nx.Window:SetCreateFade (fadein, fadeout)

	self.CFadeIn = fadein
	self.CFadeOut = fadeout
end

---------------------------------------------------------------------------------------
-- Create a Window
-- ()
-- ret: window table
---------------------------------------------------------------------------------------

function Nx.Window:Create (name, minResizeW, minResizeH, secure, titleLines, borderType, hide, noButs)

	local c2rgba = Nx.Util_str2rgba

	local wd = Nx:GetData ("Win")
	local svdata = name and wd[name]

	if not svdata then		-- No data for our name?

		svdata = {}		-- New
		if name then
			wd[name] = svdata
		end

		svdata["Hide"] = hide
		svdata["FI"] = self.CFadeIn or 1
		svdata["FO"] = self.CFadeOut or .75
	end

	-- Debug
--	svdata["FI"] = self.CFadeIn or 1
--	svdata["FO"] = self.CFadeOut or .75

	-- New

	local win = {}

	setmetatable (win, self)
	self.__index = self

	win.SaveData = svdata

	if name then
		assert (self.Wins[win] == nil)
		self.Wins[win] = true

		win.Name = name
	end

	win.Secure = secure		-- Have secure children. Don't move in combat

	win.BorderW = self.BORDERW
	win.BorderH = self.BORDERH

	win.TitleLineH = 10
	win.TitleLines = titleLines or 1
	win.TitleH = win.TitleLines * win.TitleLineH + 2
	win.TopH = win.TitleH + win.BorderH

	win.ButW = 0			-- Top left corner button width

	win.Sizeable = true
	win.Border = true
	if borderType == false then
		win.Sizeable = false
		win.Border = false
	elseif borderType == 1 then
		win.Sizeable = false
	end

	win.MovSizing = false

	win.BackgndAlphaMin = .65
	win.BackgndAlphaDiff = .35	-- Max - min
	win.BackgndFade = .01		-- Current
	win.BackgndFadeTarget = 0	-- Target value
	win.BackgndFadeIn = svdata["FI"]
	win.BackgndFadeOut = svdata["FO"]

	win.ChildFrms = {}

	-- Create window frame

--	local f = CreateFrame ("Frame", name, UIParent, "SecureStateHeaderTemplate")
	local f = CreateFrame ("Frame", name, UIParent)
	win.Frm = f
	f.NxWin = win

--	f:SetAttribute ("showstates", "1")

	f:SetMinResize (minResizeW or 100, minResizeH or 40)

	f:SetWidth (10)
	f:SetHeight (win.TitleH + 50)

	f:SetPoint ("TOPLEFT", 100, -100)
	f:SetMovable (true)
	f:SetResizable (true)
	tinsert(NotInitializedWins,f)
	f:SetScript ("OnEvent", self.OnEvent)
--	f:RegisterEvent ("PLAYER_LOGIN")

	f:SetScript ("OnMouseDown", self.OnMouseDown)
	f:SetScript ("OnMouseUp", self.OnMouseUp)
	f:SetScript ("OnMouseWheel", self.OnMouseWheel)

	f:SetScript ("OnUpdate", self.OnUpdate)

	if not win.Border then

		local t = f:CreateTexture()
		t:SetColorTexture (c2rgba (".125|.125|.125|.75"))
		t:SetAllPoints (f)
		f.texture = t
	end

	-- Create title text

	win.TitleFStr = {}

	for n = 1, win.TitleLines do

		local fstr = f:CreateFontString()
		win.TitleFStr[n] = fstr
		fstr:SetFontObject ("NxFontS")
		fstr:SetJustifyH ("LEFT")
		fstr:SetJustifyV ("MIDDLE")
		fstr:SetHeight (win.TitleLineH)
	end

	win:SetTitleXOff (0)

	-- Create GUI elements

	if win.Border then
		win:CreateBorders()
	end

	if not noButs then

		local y = win.Sizeable and -win.BorderH or -3
--		local but = Nx.Button:Create (win.Frm, "Close", nil, nil, -win.BorderW, y, "TOPRIGHT", 12, 12, win.OnCloseBut, win, "SecureAnchorButtonTemplate")
		local but = Nx.Button:Create (win.Frm, "Close", nil, nil, -win.BorderW, y, "TOPRIGHT", 12, 12, win.OnCloseBut, win)
		win.ButClose = but
		but.Frm:Hide()
--[[
		local bf = but.Frm
		bf:SetAttribute ("anchorchild", "$parent")
		f:SetAttribute ("addchild", bf)
		bf:SetAttribute ("addchild", bf)
--]]
		win.ButW = 15
	else
		win.NoButs = true
	end

	-- Init layout (start false)

	win.LayoutMode = false		-- "" = normal, "Max", "Min", "User"

	--

	win:Lock (svdata["Lk"])
	win:Show (not svdata["Hide"])

	-- Reset creation stuff

	self:SetCreateFade()

	return win
end

---------------------------------------------------------------------------------------
-- Create buttons
---------------------------------------------------------------------------------------

function Nx.InitWins()
	for a,b in pairs(NotInitializedWins) do
		Nx.FixWin(b)
	end
end

function Nx.Window:CreateButtons (closer, maxer, miner)

--	assert (not self.NoButs)

	self.Closer = closer
	self.Maxer = maxer
	self.Miner = miner		-- No buts can be set while min is on

	local x = self.BorderW -26.5

	if self.Closer then
		self.ButClose.Frm:Show()
	end
	
	local y = self.Sizeable and -self.BorderH
	
	if self.Sizeable and self.Maxer then	
		self.ButMaxer = Nx.Button:Create (self.Frm, "Max", nil, nil, x, y, "TOPRIGHT", 12, 12, self.OnMaxBut, self)
	end

	if self.Miner then		
		self.ButMiner = Nx.Button:Create (self.Frm, "Min", nil, nil, x, y, "TOPRIGHT", 12, 12, self.OnMinBut, self)
	end

	self.ButW = -x - self.BorderW

	self:Lock (self:IsLocked())	-- Force button update
end

---------------------------------------------------------------------------------------
-- Create border frames
---------------------------------------------------------------------------------------

function Nx.Window:CreateBorders()

	local c2rgba = Nx.Util_str2rgba
	local Skin = Nx.Skin
--[[
	local winBorders = self.Borders

	for n = 1, 4 do

		local index = n * 5 - 4

		local f = CreateFrame ("Frame", nil, self.Frm)
		self["BorderFrm"..n] = f

		f:SetPoint (winBorders[index], 0, 0)
		f:SetPoint (winBorders[index + 1], 0, 0)

		f:SetWidth (winBorders[index + 2])
		f:SetHeight (winBorders[index + 3])

		local t = f:CreateTexture()
		local txName = Skin:GetTex (winBorders[index + 4])
		t:SetTexture (txName)
--		t:SetTexture ("Interface\\Buttons\\Bluegrad64")
--		t:SetTexture (c2rgba ("507050ff"))
		t:SetAllPoints (f)
		f.texture = t

		f:Show()
	end
--]]

	local bk = Nx.Skin:GetBackdrop()
	self.Frm:SetBackdrop (bk)
end

function Nx.Window:SetBordersFade (fade)

	if self.Border then
--[[
		local f

		for n = 1, 4 do

			f = self["BorderFrm"..n]
			f:SetAlpha (fade * .7)
		end
--]]

		local col = Nx.Skin:GetBorderCol()
		self.Frm:SetBackdropBorderColor (col[1], col[2], col[3], col[4] * fade)
	end
end

---------------------------------------------------------------------------------------
-- Set backdrops of all created windows
---------------------------------------------------------------------------------------

function Nx.Window:ResetBackdrops()

	if self.Wins then

		local bk = Nx.Skin:GetBackdrop()

		for win, v in pairs (self.Wins) do

			if win.Border then
				win.Frm:SetBackdrop (bk)

				win.BackgndFade = win.BackgndFadeTarget + .0001		-- Cause refresh
			end
		end
	end
end

---------------------------------------------------------------------------------------
-- Attach a child frame
---------------------------------------------------------------------------------------

function Nx.Window:Attach (childFrm, posX1, posX2, posY1, posY2, width, height)

--	Nx.prt ("AttachA #%s", #self.ChildFrms)

	local f = self.Frm

	if not posX1 then
		posX1 = 0
		posX2 = 1
		posY1 = 0
		posY2 = 1
	end

	local child

	for i, ch in ipairs (self.ChildFrms) do
		if ch.Frm == childFrm then
			child = ch
			break
		end
	end

	if not child then

		child = {}
		tinsert (self.ChildFrms, child)
		child.Frm = childFrm
		childFrm:SetParent (f)		-- Triggers frame updates which can call attach again!
	end

	child.PosX1 = posX1
	child.PosX2 = posX2
	child.PosY1 = posY1
	child.PosY2 = posY2

	if width then
		child.ScaleW = width
		child.ScaleH = height
	end

--	Nx.prtVar ("Attach", child)

	self:Adjust()
end

---------------------------------------------------------------------------------------
-- Detach a child frame
---------------------------------------------------------------------------------------

function Nx.Window:Detach (childFrm)

	Nx.prt (L["Detach %s"], #self.ChildFrms)

	for i, ch in ipairs (self.ChildFrms) do
		if ch.Frm == childFrm then
			tremove (self.ChildFrms, i)
			Nx.prt (L["Detach found %s"], #self.ChildFrms)
			break
		end
	end
end

---------------------------------------------------------------------------------------
-- Adjust title width and child frames to fit our client area
---------------------------------------------------------------------------------------

function Nx.Window:Adjust (skipChildren)
	if InCombatLockdown() and Nx.db.profile.Map.Compatibility then
		return
	end
	local f = self.Frm

	local w = f:GetWidth() - self.BorderW * 2
	local h = f:GetHeight() - self.TitleH - self.BorderH * 2

	for _, fstr in ipairs (self.TitleFStr) do
		fstr:SetWidth (w - self.ButW)
	end

	if not skipChildren then

		local x, y

		for n = 1, #self.ChildFrms do
			
			local child = self.ChildFrms[n]
			local cf = child.Frm
			
			if cf and not cf:IsProtected() then			
				x = child.PosX1
				if x < 0 then
					x = w + x	-- Offset from right edge
				elseif x <= 1 then
					x = w * x	-- Percent
				end

				local x2 = child.PosX2
				if x2 < 0 then
					x2 = w + x2	-- Offset from right edge
				elseif x2 <= 1 then
					x2 = w * x2	-- Percent
				end

				y = child.PosY1
				if y <= -10000 then
					y = y + 10000
				elseif y < 0 then
					y = h + y
				elseif y <= 1 then
					y = h * y
				end

				local y2 = child.PosY2
				if y2 <= -10000 then
					y2 = y2 + 10000
				elseif y2 < 0 then
					y2 = h + y2
				elseif y2 <= 1 then
					y2 = h * y2
				end

				cf:SetPoint ("TOPLEFT", f, "TOPLEFT", x + self.BorderW, -y - self.TopH)

				local childW = x2 - x
				local childH = y2 - y

				if child.ScaleW then

					local sw = childW / child.ScaleW
					local sh = childH / child.ScaleH
					local scale = max (min (sw, sh), .001)
					cf:SetScale (scale)

					cf:SetPoint ("TOPLEFT", f, "TOPLEFT", (self.BorderW + w * child.PosX1) / scale, (-self.TopH - h * child.PosY1) / scale)

				else

					local inst = cf.NxInst

					if inst and inst.SetSize then
						inst:SetSize (childW, childH)
					else
						cf:SetWidth (childW)
						cf:SetHeight (childH)
					end
				end

				if cf.NxSetSize then
					cf:NxSetSize (childW, childH)
				end
			end
			--			prtFrame ("Adj"..n, cf)
		end
	end
end

---------------------------------------------------------------------------------------
-- Get a window attribute
---------------------------------------------------------------------------------------

function Nx.Window:GetAttribute (winName, atName)

	local win = self:Find (winName)

	if win then
		if atName == "L" then		-- Locked
			return "B", win:IsLocked()

		elseif atName == "H" then	-- Hide
			return "B", not win:IsShown()
		end
	end
end

---------------------------------------------------------------------------------------
-- Set a window attribute
---------------------------------------------------------------------------------------

function Nx.Window:SetAttribute (winName, atName, value)

	local win = self:Find (winName)

	if win then
		if atName == "L" then		-- Locked
			win:Lock (value)

		elseif atName == "H" then	-- Hide
			win:Show (not value)
		end
	end
end

---------------------------------------------------------------------------------------
-- Show or hide window
---------------------------------------------------------------------------------------

function Nx.Window:Show (show)

	local svdata = self.SaveData

	if show ~= false then
		self.Frm:Show()
		self.Frm:Raise()
		self.Frm:Raise()
		svdata["Hide"] = nil
	else
		if self.Frm:IsShown() then		-- Check first to avoid taint errors
			self.Frm:Hide()
		end
		svdata["Hide"] = true
	end
end

---------------------------------------------------------------------------------------
-- Check if shown
---------------------------------------------------------------------------------------

function Nx.Window:IsShown()

	local svdata = self.SaveData

	local vis = self.Frm:IsShown()
	if vis == nil then
		vis = false
	end

	return vis, not svdata["Hide"]
end

---------------------------------------------------------------------------------------
-- Restore saved show or hide state
---------------------------------------------------------------------------------------

--[[
function Nx.Window:ShowRestore()

	if self.SaveData["Hide"] then
		self.Frm:Hide()
	else
		self.Frm:Show()
	end
end
--]]

---------------------------------------------------------------------------------------
-- Check if visible (parent must also be visible)
---------------------------------------------------------------------------------------

function Nx.Window:IsVisible()
	return self.Frm:IsVisible()
end

---------------------------------------------------------------------------------------
-- Check if hidden from combat
---------------------------------------------------------------------------------------

function Nx.Window:IsCombatHidden()

	if self.SaveData["HideC"] then
		return UnitAffectingCombat ("player")
	end
end

---------------------------------------------------------------------------------------
-- Lock or unlock window. Cannot move, resize or scale if locked
---------------------------------------------------------------------------------------
function Nx.Window:Lock (lock, fullLockout)

--	Nx.prtVar ("Win:Lock", lock)

	self.Locked = lock
	self.Frm:EnableMouse (not lock)
	self.Frm:EnableMouseWheel (not lock)

	local svdata = self.SaveData

	svdata["Lk"] = lock or nil

	self:SetBordersFade (lock and 0 or self.BackgndFade)

	if self.ButClose then
		if lock then

			if self.Closer then
				self.ButClose:SetType ("CloseLock")
			else
				self.ButClose.Frm:Show()
				self.ButClose:SetType ("Lock")
			end
		else

			if self.Closer then
				self.ButClose:SetType ("Close")
			else
				self.ButClose.Frm:Hide()
			end
		end

		self.ButClose:Update()
	end

	if fullLockout then
		self.FullLock = lock
	end
end

---------------------------------------------------------------------------------------
-- Get lock status of window
---------------------------------------------------------------------------------------

function Nx.Window:IsLocked()
	return self.Locked
end

---------------------------------------------------------------------------------------
-- Set if mouse enabled for window
---------------------------------------------------------------------------------------

function Nx.Window:EnableMouse (on)

	self.FullLock = not on

	if self.MovSizing then
		self.OnMouseUp (self.Frm, "")
	end

	if self.ButClose then
		if on then
			self.ButClose.Frm:Show()
		else
			self.ButClose.Frm:Hide()
		end
	end

	if on then
		self:Lock (self.Locked)		-- Restore
	else
		self.Frm:EnableMouse (on)
		self.Frm:EnableMouseWheel (on)
	end

end

---------------------------------------------------------------------------------------
-- Set if we have a window menu
---------------------------------------------------------------------------------------

function Nx.Window:EnableMenu (on)
	self.MenuDisable = not on
end

---------------------------------------------------------------------------------------
-- Get title text width
---------------------------------------------------------------------------------------

function Nx.Window:GetTitleTextWidth()

	local w = 40

	for n = 1, self.TitleLines do

		local fstr = self.TitleFStr[n]
		fstr:SetWidth (0)

		w = max (self.TitleFStr[n]:GetStringWidth(), w)
	end

	return w
end

---------------------------------------------------------------------------------------
-- Set title text line height
---------------------------------------------------------------------------------------

function Nx.Window:SetTitleLineH (height)

	self.TitleLineH = height
	self.TitleH = self.TitleLines * self.TitleLineH + 2
	self.TopH = self.TitleH + self.BorderH

	local fname = height <= 10 and "NxFontS" or "NxFontM"

	for n = 1, self.TitleLines do

		local fstr = self.TitleFStr[n]
		fstr:SetFontObject (fname)
		fstr:SetHeight (height)
	end
end

---------------------------------------------------------------------------------------
-- Set the title text x offset (also y. rename!)
---------------------------------------------------------------------------------------

function Nx.Window:SetTitleXOff (x, yo)

	yo = yo or 0

	for n = 1, self.TitleLines do

		local fstr = self.TitleFStr[n]
		local y = -self.BorderH - (n - 1) * self.TitleLineH - .4		-- Fudge it so it looks better
		fstr:SetPoint ("TOPLEFT", self.BorderW + x, y - yo)
		fstr:SetPoint ("TOPRIGHT", self.Frm, "TOPRIGHT", -self.BorderW, y)
	end
end

---------------------------------------------------------------------------------------
-- Set our title text
---------------------------------------------------------------------------------------

function Nx.Window:SetTitle (text, line)

	line = line or 1
	if self.TitleFStr[line] then
		self.TitleFStr[line]:SetText (text)
	end
end

---------------------------------------------------------------------------------------
-- Set all title colors
---------------------------------------------------------------------------------------

function Nx.Window:SetTitleColors (r, g, b, a)

	for n = 1, self.TitleLines do

		local fstr = self.TitleFStr[n]
		fstr:SetTextColor (r, g, b, a)
	end
end

---------------------------------------------------------------------------------------
-- Set our title text
---------------------------------------------------------------------------------------

function Nx.Window:SetTitleJustify (mode, line)

	line = line or 1
	self.TitleFStr[line]:SetJustifyH (mode)
end

---------------------------------------------------------------------------------------
-- Get our background min max alpha
---------------------------------------------------------------------------------------

function Nx.Window:GetBGAlpha()

	local m = self.BackgndAlphaMin
	return m, m + self.BackgndAlphaDiff
end

---------------------------------------------------------------------------------------
-- Set our background min max alpha
---------------------------------------------------------------------------------------

function Nx.Window:SetBGAlpha (min, max)

	self.BackgndAlphaMin = min
	self.BackgndAlphaDiff = max - min

	self.BackgndFade = self.BackgndFadeTarget + .0001	-- Cause refresh
end

---------------------------------------------------------------------------------------
-- Get our current fade
---------------------------------------------------------------------------------------

function Nx.Window:GetFade()
	return self.BackgndFade
end

---------------------------------------------------------------------------------------
-- Set our background color
---------------------------------------------------------------------------------------

function Nx.Window:SetBGColor (r, g, b, a)
	if self.Frm.texture then
		self.Frm.texture:SetColorTexture (r, g, b, a or 1)
	end
end

---------------------------------------------------------------------------------------
-- Get win width and height
---------------------------------------------------------------------------------------

function Nx.Window:GetClientOffset()
	return self.BorderW, self.TitleH + self.BorderH
end

---------------------------------------------------------------------------------------
-- Set if win is sizeable
---------------------------------------------------------------------------------------

function Nx.Window:SetSizeable (on)
	self.Sizeable = on
end

---------------------------------------------------------------------------------------
-- Get win width and height (client size)
---------------------------------------------------------------------------------------

function Nx.Window:GetSize()

	return self.Frm:GetWidth() - self.BorderW * 2,
			 self.Frm:GetHeight() - self.TitleH + self.BorderH * 2
end

---------------------------------------------------------------------------------------
-- Set win width and height (client size)
---------------------------------------------------------------------------------------

function Nx.Window:SetSize (width, height, skipChildren)
	if InCombatLockdown() then	
		return
	end
	self.Frm:SetWidth (width + self.BorderW * 2)
	self.Frm:SetHeight (height + self.TitleH + self.BorderH * 2)

	self:Adjust (skipChildren)
end

---------------------------------------------------------------------------------------
-- Set win width and height (win size)
---------------------------------------------------------------------------------------

function Nx.Window:SetTotalSize (width, height, skipChildren)

	self.Frm:SetWidth (width)
	self.Frm:SetHeight (height)

	self:Adjust (skipChildren)
	self:RecordLayoutData()
end

---------------------------------------------------------------------------------------
-- Set win pos
---------------------------------------------------------------------------------------

function Nx.Window:SetPos (x, y)

	local f = self.Frm
	f:ClearAllPoints()
	f:SetPoint ("TOPLEFT", x, y)

	self:RecordLayoutData()
end

---------------------------------------------------------------------------------------
-- Offset win pos
---------------------------------------------------------------------------------------

function Nx.Window:OffsetPos (xo, yo)

	local f = self.Frm
	local atPt, relTo, relPt, x, y = f:GetPoint()

	f:SetPoint (atPt, relTo, relPt, x + xo, y + yo)

	self:RecordLayoutData()
end

---------------------------------------------------------------------------------------
-- Get size of borders
---------------------------------------------------------------------------------------

function Nx.Window:GetBorderSize()
	return self.BorderW, self.BorderH
end

---------------------------------------------------------------------------------------
-- Set size of borders
---------------------------------------------------------------------------------------

function Nx.Window:SetBorderSize (w, h)

	self.BorderW = w
	self.BorderH = h
	self.TopH = self.TitleH + h
end

---------------------------------------------------------------------------------------
-- Reset layout to default
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:ResetLayout()

	local data = self.SaveData

	if data["_X"] then

		for k, v in pairs (data) do

			if k ~= "_X" then
				if strsub (k, -1) == "X" then
					local mode = strsub (k, 1, #k - 1)

--					Nx.prt ("Reset %s '%s' %f %f %f %f, %s, %s",
--							self.Name, mode, data["_X"], data["_Y"], data["_W"], data["_H"], data["_A"] or "nil", data["_S"] or "nil")

					self:SetLayoutData (mode, data["_X"], data["_Y"], data["_W"], data["_H"], data["_L"], data["_A"], data["_S"])

					self:SetMaxSizeDefault()
				end
			end
		end

		self.LayoutMode = false
		self:SetLayoutMode()
--		self.Frm:SetScale (1)
	end

	self:Lock (false)

	if self.Name == "NxMap1" or self.Name == "NxQuestWatch" then
		self.Frm:Show()
		data["Hide"] = nil
	end
end

---------------------------------------------------------------------------------------
-- Set max size to default
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:SetMaxSizeDefault()

	local sw = GetScreenWidth()
	local sh = GetScreenHeight()
	self:SetLayoutData ("Max", sw * .1, sh * .1, sw * .8, sh * .8, 2, "TOPLEFT")
end

---------------------------------------------------------------------------------------
-- Get current layout mode name
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:GetLayoutMode()
	return self.LayoutMode
end

---------------------------------------------------------------------------------------
-- Switch our layout mode if different and not maximized
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:SwitchLayoutMode (mode)

	mode = mode or ""

	if self.LayoutMode ~= mode then

		if self.LayoutMode == "Max" then
			self.LayoutModeNormal = mode		-- Remember for later
		else
			self:SetLayoutMode (mode)
		end
	end
end

---------------------------------------------------------------------------------------
-- Set our layout mode and change to size/position
-- (mode or 1 if first time (login))
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:SetLayoutMode (mode)
	if InCombatLockdown() and Nx.db.profile.Map.Compatibility then
		return
	end
	local data = self.SaveData

	if mode == 1 then

		mode = data["Mode"]		-- nil, "Min", "Max"

		if mode == "Min" then
			self:SetLayoutMode()
			self:SetMinimize (true)
			return
		end
	end

	if mode == "" then
		mode = nil
	end

	data["Mode"] = mode

	mode = mode or ""

--	Nx.prt ("SetLayoutMode '%s' to '%s'", self.LayoutMode or "nil", mode)

	local f = self.Frm

	local oldMode = self.LayoutMode
	if oldMode then
		self:RecordLayoutData()
	end

	if self.ButMiner then

		if mode == "Min" then
--			mode = ""
			data["Min"] = true
			self.ButMiner:SetPressed (true)

		else
			data["Min"] = nil
			self.ButMiner:SetPressed (false)
		end
	end

	if self.ButMaxer then

		if mode == "Max" then
			self.ButMaxer:SetType ("MaxOn")
		else
			self.ButMaxer:SetType ("Max")
		end
		self.ButMaxer:Update()
	end

	self.LayoutMode = mode

	local sw = GetScreenWidth()
	local sh = GetScreenHeight()

	if mode == "Max" and not data["MaxX"] then
		self:SetMaxSizeDefault()
--		Nx.prt ("Setting win max")
	end

	local x = data[mode.."X"]

	if not x then

		if mode == "Min" then			
			self:SetLayoutData (mode, sw * .9, sh * .4, 1, 1)	-- Hardcoded for quest watch
		else
--			Nx.prt ("SetLayoutMode %s '%s' missing!", self.Name, mode)
			self:SetLayoutData (mode, sw * .4, sh * .4, sw * .2, sh * .2)
		end

	else
		local w = data[mode.."W"]
		if w < 0 then
			w = sw * -w
		end

		local h = data[mode.."H"]
		if h < 0 then
			h = sh * -h
		end

		if x >= 999999 then				-- Center
			x = (sw - w) * .5

		elseif x >= 300000 then				-- Offset + from screen center
			local s = data[mode.."S"] or 1
			x = (sw * .5 + (x - 300000)) / s

		elseif x >= 200000 then				-- Offset - from screen center
			local s = data[mode.."S"] or 1
			x = (sw * -.5 - (x - 200000)) / s
--			x = sw * .5 - (x - 200000) - w

		elseif x > 100000 then				-- Offset from screen right
			x = sw - x + 100000 - self.BorderW

		elseif x < 0 and x > -1 then			-- % of width
			x = sw * -x
		end

		local y = data[mode.."Y"]

		if y >= 999999 then				-- Center
			y = (sh - h) * .5
		elseif y < 0 and y > -1 then			-- % of width
			y = sh * -y
		end

		self:SetLayoutData (mode, x, y, w, h, false, data[mode.."A"], data[mode.."S"])
	end

	local aPt = data[mode.."A"] or "TOPLEFT"

	if aPt == "TOPLEFT" then
		if data[mode.."X"] > sw - 20 then
			data[mode.."X"] = sw - 20
--			Nx.prt ("Fix %s x", self.Name)
		end
	end
	if aPt == "TOPRIGHT" or aPt == "RIGHT" or aPt == "BOTTOMRIGHT" then
		if data[mode.."X"] > 20 then
			data[mode.."X"] = 20
--			Nx.prt ("Fix %s x", self.Name)
		end
	end

--	prt ("Y "..data[mode.."Y"])
--	prt ("%s H %f", self.Name, data[mode.."H"])

	self:SetFrmStrata (data[mode.."L"])

	f:ClearAllPoints()
	f:SetPoint (aPt, data[mode.."X"], -data[mode.."Y"])
	f:SetWidth (data[mode.."W"])
	f:SetHeight (data[mode.."H"])
	f:SetScale (data[mode.."S"] or 1)
	f:SetAlpha (data[mode.."T"] or 1)

--	prt ("SetLayoutMode %s WH %f %f", data[mode.."A"] or "nil", data[mode.."W"], data[mode.."H"])

	if mode == "Max" then

		f:Raise()
		f:Raise()
--		prt ("Y "..sh)
	end

	if mode == "Min" then
		f:SetWidth (125)
		f:SetHeight (28)
	end

	self:Adjust()
end

---------------------------------------------------------------------------------------
-- Set strata
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:SetFrmStrata (layer)

	local svdata = self.SaveData
	svdata[self.LayoutMode.."L"] = layer

	self.Frm:SetFrameStrata (self.StrataNames[layer] or "MEDIUM")
end

---------------------------------------------------------------------------------------
-- Init default data for a layout mode
-- self = Win

function Nx.Window:InitLayoutData (mode, x, y, w, h, layer, scale)

	local data = self.SaveData

	-- w and h are client size
	if w > 0 then
		w = w + self.BorderW
	end
	if h > 0 then
		h = h + self.BorderH + self.TitleH
	end

--	Nx.prt ("InitLayout %s '%s' %f %f %f %f, %s",
--			self.Name, mode or "_", x, y, w, h, scale or "")

	local attach

	if scale then

		if x >= 300000 then			-- Offset + from screen center
		elseif x >= 200000 then			-- Offset - from screen center
			attach = "TOPRIGHT"
--			x = (x - 200000) * scale + 200000
		end
	end

	if not mode then
		mode = ""
		self:SetLayoutData ("_", x, y, w, h, layer, attach, scale)	-- Original position for reset
	end

	if not data[mode.."X"] then
		self:SetLayoutData (mode, x, y, w, h, layer, attach, scale)
	end

	if self.LoginDone then				-- Already logged in?
		self:SetLayoutMode (1)
	end
end

---------------------------------------------------------------------------------------
-- Set data for a layout mode
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:SetLayoutData (mode, x, y, w, h, layer, attachPt, scale)

--	Nx.prt ("SetLayoutData %s %s", self.Name, mode)

	if not Nx.Window.SaveDisabled then

		local data = self.SaveData

--		if self.Name == "NxHUD" and mode == "_" then			
--			Nx.prt ("SetLayout %s '%s' %f %f %f %f, %s",
--					self.Name, mode, x, y, w, h, attachPt or "")
--		end

		if attachPt == "TOPLEFT" then
			attachPt = nil			-- Don't save default
		end

		data[mode.."A"] = attachPt
		data[mode.."X"] = x
		data[mode.."Y"] = y
		data[mode.."W"] = w
		data[mode.."H"] = h < 0 and h or max (h, 40)
		if layer ~= false then
			data[mode.."L"] = layer
		end
		data[mode.."S"] = scale ~= 1 and scale or nil
	end
end

---------------------------------------------------------------------------------------
-- Record current layout
-- self = Win
---------------------------------------------------------------------------------------

function Nx.Window:RecordLayoutData()

	if self.LayoutMode then

		local f = self.Frm
		local atPt, relTo, relPt, x, y = f:GetPoint()
		local scale = f:GetScale()

--		Nx.prt ("Record %s(%s): %s %s %s %s %s %d", self.Name, self.LayoutMode, atPt, (relTo and relTo:GetName()) or "nil", relPt, x, y, scale)

		assert (atPt == relPt)

		if x < 0 and x >= -1 then	-- Neg small numbers used for % of screen W
			x = 0
		end

		y = -y

		if y < 0 and y >= -1 then	-- Neg small numbers used for % of screen H
			y = 0
		end

		local w = f:GetWidth()
		local data = self.SaveData

		if self.LayoutMode == "" then

			if self.Name == "NxMap1" and data["MaxW"] and w >= data["MaxW"] then
--				Nx.prt ("Window %s Normal >= Max layout. Not saving", self.Name)
				return
			end

		elseif self.LayoutMode == "Max" then

--			Nx.prt ("Window %s %s", w, data["W"])

			if self.Name == "NxMap1" and data["W"] and w <= data["W"] then
--				Nx.prt ("Window %s Max <= Normal layout. Not saving", self.Name)
				return
			end
		end

		self:SetLayoutData (self.LayoutMode, x, y, f:GetWidth(), f:GetHeight(), false, atPt, scale)
	end
end

---------------------------------------------------------------------------------------
-- Set user of the window (for event handlers) and generic callback function
-- (user table, function)
---------------------------------------------------------------------------------------

function Nx.Window:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Register for hide notify
---------------------------------------------------------------------------------------

function Nx.Window:RegisterHide()

	local function func (self)
		self.NxWin:Notify ("Hide")
	end

	self.Frm:SetScript ("OnHide", func)
end

---------------------------------------------------------------------------------------
-- Notfiy user of the window
-- (name)
---------------------------------------------------------------------------------------

function Nx.Window:Notify (name, ...)

	if self.UserFunc then
		self.UserFunc (self.User, name, ...)
	end
end

---------------------------------------------------------------------------------------
-- Toggle window size
---------------------------------------------------------------------------------------

function Nx.Window:ToggleSize()

	if self.Sizeable then

		if self.LayoutMode ~= "Max" then

			self.LayoutModeNormal = self.LayoutMode
			self:SetLayoutMode ("Max")
			self:Notify ("SizeMax")

		else
			self:SetLayoutMode (self.LayoutModeNormal)
			self:Notify ("SizeNorm")
		end
	end
end

function Nx.Window:OnMinBut (but, id, click)
	self:SetMinimize (but:GetPressed())
	self.SaveData["Minimized"] = but:GetPressed()
end

function Nx.Window:ToggleMinimize()
	self:SetMinimize (not self.ButMiner:GetPressed())
end

---------------------------------------------------------------------------------------
-- Toggle window minimize
---------------------------------------------------------------------------------------

function Nx.Window:SetMinimize (minOn)

	if self.ButMiner then

		if minOn then

			self.LayoutModeNormal = self.LayoutMode
			self:SetLayoutMode ("Min")
			self:Notify ("SizeMin")

		else
			self:SetLayoutMode (self.LayoutModeNormal)
			self:Notify ("SizeNorm")
		end
	end
end

function Nx.Window:IsSizeMax()
	return self.Sizeable and self.LayoutMode == "Max"
end

function Nx.Window:IsSizeMin()
	return self.ButMiner and self.ButMiner:GetPressed()
end

---------------------------------------------------------------------------------------
-- Check for moving or sizing
---------------------------------------------------------------------------------------

function Nx.Window:IsMovingOrSizing()
	return self.MovSizing
end

---------------------------------------------------------------------------------------
-- Handle events
-- self is frame
---------------------------------------------------------------------------------------

function Nx.FixWin (frm)
	local win = frm.NxWin
--	Nx.prt ("Win Event %s", win.Name)
	Nx.Window.LoginDone = true
	win.LayoutMode = false
	win:SetLayoutMode(1)
end

function Nx.Window:OnEvent (event, ...)

	-- V4 this
	local win = self.NxWin
--	local win = self

--	Nx.prt ("Win Event %s %s", win.Name, event)

	if win.Events and win.Events[event] then
		win.Events[event] (win.User, event, ...)
	end
end

function Nx.Window:OnMouseDown (button)

--	Nx.prt ("WinMouseDown "..tostring (button))
--	Nx.prtFrame ("Win", this)

	local this = self			-- V4
	local win = this.NxWin

	local x, y = GetCursorPosition()
	x = x / this:GetEffectiveScale()
	y = y / this:GetEffectiveScale()

	ResetCursor()

	if win.Secure and InCombatLockdown() then
		return
	end

	if button == "LeftButton" then

--		Nx.prt (" MouseDown "..x.." "..y.." "..rgt.." "..bot)

		local side = win:IsOnWinUI (x, y)

		if win.Sizeable then
			if side > 0 then
				this:StartSizing (win.SideNames[side])
				win.MovSizing = true
			end
		end

		if not win.MovSizing and side == 0 then
			this:StartMoving()
			win.MovSizing = true
		end

		if win.MovSizing then

			SetCursor ("INSPECT_CURSOR")
			this:SetFrameStrata ("HIGH")
		end

	elseif button == "MiddleButton" then

		win:ToggleSize()

	elseif button == "RightButton" then

		if IsShiftKeyDown() and IsControlKeyDown() then
			win:ResetLayout()
		else
			win:OpenMenu (win.NoButs)
		end

	end
end

---------------------------------------------------------------------------------------
-- Check if xy on window UI elements
-- (x, y)
---------------------------------------------------------------------------------------

function Nx.Window:IsOnWinUI (x, y)

	local f = self.Frm
	local top = f:GetTop()
	local bot = f:GetBottom()

	-- Bits set: 1=Left, 2=Right, 3=Top, 4=Bottom

	if self.Sizeable then

		local left = f:GetLeft()
		local rgt = f:GetRight()
		local bw = self.BorderW
		local bh = self.BorderH

		if x >= rgt - bw then

			if y >= top - bh then
				return 6		-- TOPRIGHT
			elseif y <= bot + bh then
				return 10		-- BOTTOMRIGHT
			end

			return 2			-- RIGHT

		elseif x < left + bw then

			if y >= top - bh then
				return 5		-- TOPLEFT
			elseif y <= bot + bh then
				return 9		-- BOTTOMLEFT
			end

			return 1			-- LEFT

		elseif y <= bot + bh then

			return 8			-- BOTTOM

		elseif y >= top - bh then

			return 4			-- TOP
		end
	else
		if y <= bot + self.BorderH then
			return 0			-- Header
		end
	end

	if y >= top - self.TopH then
		return 0				-- Header
	end

	return -1					-- None
end

---------------------------------------------------------------------------------------
-- Mouse up message. Enable mouse also calls!
-- self = frame
---------------------------------------------------------------------------------------

function Nx.Window:OnMouseUp (button)

--	prt ("WinMouseUp "..tostring (button))

	local this = self				-- V4
	local win = this.NxWin

	if win.MovSizing then

		this:StopMovingOrSizing()
		win.MovSizing = false

		if win.Secure and InCombatLockdown() then
			win.DeferredMouseUp = true
		else
			win:SetFrmStrata (win.SaveData[win.LayoutMode.."L"])
			this:Raise()
		end

		win:RecordLayoutData()
	end

	ResetCursor()

	win:Adjust()
end

---------------------------------------------------------------------------------------

function Nx.Window:OnMouseWheel (value)

	if not IsShiftKeyDown() then
		return
	end

	if not (IsControlKeyDown() or IsAltKeyDown()) then
		return
	end

	local this = self				-- V4
	local win = this.NxWin
	local f = win.Frm

	value = value > 0 and 1 or -1

	local cx, cy = GetCursorPosition()
	cx = cx / UIParent:GetEffectiveScale()
	cy = GetScreenHeight() - cy / UIParent:GetEffectiveScale()

	local s = f:GetScale()
	local top = GetScreenHeight() - f:GetTop() * s
	local left = f:GetLeft() * s

--	prtFrame ("Win", this)
--	prt ("XY "..left.." "..top)
--	prt ("CXY "..cx.." "..cy)

	news = max (s + value * .025, .5)

	if IsAltKeyDown() then
		news = 1
	end

	local x = ((left - cx) * news / s + cx) / news
	local y = ((top - cy) * news / s + cy) / news

	f:SetScale (news)
	f:ClearAllPoints()
	f:SetPoint ("TOPLEFT", x, -y)

	win:Adjust()
	win:RecordLayoutData()
end

---------------------------------------------------------------------------------------
-- Win update
-- self = frame
---------------------------------------------------------------------------------------

function Nx.Window:OnUpdate (elapsed)

	local this = self				-- V4
	local win = this.NxWin

	local secureOk = not (win.Secure and InCombatLockdown())

	if win.DeferredMouseUp and secureOk then
		win.DeferredMouseUp = nil
		win:SetFrmStrata (win.SaveData[win.LayoutMode.."L"])
		this:Raise()
	end

	if win.MovSizing and secureOk then
		if IsAltKeyDown() then
			Nx.Util_SnapToScreen (this)
		end
	end

	if win.CursorIsSet then
		win.CursorIsSet = false
		ResetCursor()
	end

	local x = not win.FullLock and Nx.Util_IsMouseOver (this)

	if x then
		if GetMouseFocus() == this then

			local x, y = GetCursorPosition()
			x = x / this:GetEffectiveScale()
			y = y / this:GetEffectiveScale()

			local side = win:IsOnWinUI (x, y)

			if side == 0 then
				SetCursor ("ITEM_CURSOR")
				win.CursorIsSet = true

			elseif side > 0 then
				SetCursor ("INTERACT_CURSOR")
				win.CursorIsSet = true
			end
		end
	end

	if (x or win.Sizing) and secureOk then

		win:Adjust()
		win.BackgndFadeTarget = win.BackgndFadeIn
	else

		win.BackgndFadeTarget = win.BackgndFadeOut
	end

	local fade = Nx.Util_StepValue (win.BackgndFade, win.BackgndFadeTarget, elapsed * 2)

	if fade ~= win.BackgndFade then

		if win.UserUpdateFade then
			win.UserUpdateFade (win.User, fade)
		end

		win.BackgndFade = fade

		local a = fade * win.BackgndAlphaDiff + win.BackgndAlphaMin
		if this.texture then
			this.texture:SetVertexColor (1, 1, 1, a)
		else

			local col = Nx.Skin:GetBGCol()
			if not win.Sizeable and win.Border then
				col = Nx.Skin:GetFixedSizeBGCol()
			end

			this:SetBackdropColor (col[1], col[2], col[3], col[4] * a)
		end

		if not win.Locked then
			win:SetBordersFade (fade)
		end

		if win.ButClose then
			win.ButClose.Frm:SetAlpha (fade * .9 + .1)
		end
		if win.ButMaxer then
			win.ButMaxer.Frm:SetAlpha (fade * .9 + .1)
		end
		if win.ButMiner then
			win.ButMiner.Frm:SetAlpha (fade * .9 + .1)
		end

		for n = 1, #win.ChildFrms do

			local child = win.ChildFrms[n]
			local cf = child.Frm

			local inst = cf.NxInst

			if inst and inst.SetFade then
				inst:SetFade (fade)
			else
				if cf.texture then
					cf.texture:SetVertexColor (1, 1, 1, fade * .7 + .3)
				end
			end
		end
	end
end

function Nx.Window:OnCloseBut (but, id, click)

	if click == "LeftButton" and self.Closer then

		self:Show (false)
		self:RecordLayoutData()
		GameTooltip:Hide()
		self:Notify ("Close")

	else
		if self.Locked then
			self:Lock (false)
		else
			self:OpenMenu()
		end
	end
end

function Nx.Window:OnMaxBut (but, id, click)

	if click == "LeftButton" then
		self:ToggleSize()
	else
		self:OpenMenu()
	end
end

function Nx.Window:OpenMenu (noLock)

--	Nx.prtVar ("LockWinOM", self)

	if not self.MenuDisable then

		local w = Nx.Window

		w.MenuWin = self
		w.MenuIHideInCombat:SetChecked (self.SaveData["HideC"])
		w.MenuILock:SetChecked (self.Locked)
		w.MenuILock:Show (not noLock)
		w.MenuIFadeIn:SetSlider (self.BackgndFadeIn, .25, 1)
		w.MenuIFadeOut:SetSlider (self.BackgndFadeOut, 0, 1)

		local svdata = self.SaveData

		w.MenuILayer:SetSlider (svdata[self.LayoutMode.."L"] or 2, 1, 3, 1)

		w.MenuIScale:SetSlider (svdata[self.LayoutMode.."S"] or 1, .5, 2)

		w.MenuITrans:SetSlider (svdata[self.LayoutMode.."T"] or 1, .01, 1)

		local m = Nx.Window.Menu
		m:Open()
	end
end

---------------------------------------------------------------------------------------
-- Button - A frame that acts like a button
---------------------------------------------------------------------------------------

Nx.Button.TypeData = {
	["AAItem"] = {
		Up = "$INV_Misc_QuestionMark",
		Dn = "$INV_Misc_QuestionMark",
		SizeUp = 16,
		SizeDn = 16,
	},
	["Chk"] = {
		Skin = true,
		Bool = true,
		Up = "But",
		Dn = "ButChk",
	},
	["Close"] = {
		Skin = true,
		Up = "ButClose",
		Dn = "ButClose",
		Tip = L["Close/Menu"]
	},
	["CloseLock"] = {
		Skin = true,
		Up = "ButLock",
		Dn = "ButLock",
		Tip = L["Close/Unlock"]
	},
	["Color"] = {
		Tip = L["Pick Color"],
		SizeUp = 22,			-- Temp. Make opts version?
		SizeDn = 22,
	},
	["Lock"] = {
		Skin = true,
		Up = "ButLock",
		Dn = "ButLock",
		Tip = L["Unlock"]
	},
	["Guide"] = {
		Bool = true,
		Up = "$INV_Misc_QuestionMark",
		Dn = "$INV_Misc_QuestionMark",
		SizeUp = 24,
		SizeDn = 28,
		AlphaUp = .7,
		AlphaDn = 1,
	},
	["Max"] = {
		Tip = L["Maximize"],
		Skin = true,
		Up = "ButMax",
		Dn = "ButMax",
		VRGBAUp = "1|1|1|1",
	},
	["MaxOn"] = {
		Tip = L["Restore"],
		Skin = true,
		Up = "ButMax",
		Dn = "ButMax",
		VRGBAUp = ".5|.5|1|1",
	},
	["Min"] = {
		Tip = L["Minimize"],
		Bool = true,
		Skin = true,
		Up = "ButWatchShow",
		Dn = "ButWatchMini",
		VRGBAUp = "1|1|1|.5",
		VRGBADn = ".62|.62|1|1",
	},
	["MapAutoScale"] = {
		Tip = L["Auto Scale"],
		Bool = true,
		Skin = true,
		Up = "But",
		Dn = "ButChk",
	},
	["MapCombat"] = {
		Up = "$Ability_DualWield",
		SizeUp = 22,
		SizeDn = 22,
	},
	["MapEvents"] = {
		Up = "$INV_Misc_Note_03",
		SizeUp = 22,
		SizeDn = 22,
	},
	["MapGuide"] = {
		Up = "$INV_Misc_QuestionMark",
		SizeUp = 22,
		SizeDn = 22,
	},
	["MapQGivers"] = {
		Up = "$INV_Misc_Note_02",
		SizeUp = 22,
		SizeDn = 22,
	},
	["MapZIn"] = {
		Up = "$Spell_ChargePositive",
		SizeUp = 22,
		SizeDn = 22,
	},
	["MapZOut"] = {
		Up = "$Spell_ChargeNegative",
		SizeUp = 22,
		SizeDn = 22,
	},
	["Opts"] = {
		Skin = true,
		Bool = true,
		Up = "But",
		Dn = "ButChk",
		SizeUp = 22,
		SizeDn = 22,
	},
	["Scroll"] = {
		Scroll = true,
		Up = "Interface\\Addons\\Carbonite\\Gfx\\Buttons\\ScrollUp",
		Dn = "Interface\\Addons\\Carbonite\\Gfx\\Buttons\\ScrollUp",
		SizeUp = 14,
		SizeDn = 12,
	},
	["Tab"] = {
		Bool = true,
		Skin = true,
		Up = "TabOff",
		Dn = "TabOn",
	},
	["Toggle"] = {
		Bool = true,
		Skin = true,
		Up = "But",
		Dn = "ButChk",
		SizeUp = 14,
		SizeDn = 14,
	},
	["Txt"] = {
		RGBUp = "604040",
		RGBDn = "503030",
	},
	["Txt64"] = {
		Skin = true,
		Up = "ButEmpty64",
		Dn = "ButEmpty64",
		RGBUp = "604040",
		RGBDn = "503030",
	},
	["Txt64B"] = {
		Bool = true,
		Skin = true,
		Up = "ButEmpty64",
		Dn = "ButEmpty64",
		VRGBAUp = "1|1|1|1",
		VRGBADn = "1|.373|.373|1",
	},
}

---------------------------------------------------------------------------------------
--
---------------------------------------------------------------------------------------

function Nx.Button:Init()

	local f = CreateFrame ("Frame", nil, UIParent)
	self.OverFrm = f

	f:SetFrameStrata ("MEDIUM")
	f:Hide()

	local t = f:CreateTexture()
	t:SetColorTexture (Nx.Util_str2rgba ("0.06|0.06|0.250|1"))
	t:SetAllPoints (f)
	t:SetBlendMode ("ADD")
	f.texture = t
end

---------------------------------------------------------------------------------------
-- Create a Button
-- ()
-- ret: button table
---------------------------------------------------------------------------------------

function Nx.Button:Create (parentFrm, typ, text, tip, bx, by, side, width, height, func, user, template)

	parentFrm = parentFrm or UIParent

	local but = {}		-- New

	setmetatable (but, self)
	self.__index = self

	but:SetUser (user, func)

	but.Type = self.TypeData[typ]

	assert (not typ or but.Type)

	-- Create frame

	local fType = template and "Button" or "Frame"
	local fname = text and ("NxBut" .. text)

	local f = CreateFrame (fType, fname, parentFrm, template)
	but.Frm = f
	f.NxBut = but

	but.Tip = tip
	f.NxTip = tip or (typ and self.TypeData[typ].Tip)

	side = side or "TOPLEFT"
	f:SetPoint (side, bx, by)

	f:SetWidth (width)
	f:SetHeight (height)

--	f:SetFrameStrata ("MEDIUM")

	f:SetScript ("OnMouseDown", self.OnMouseDown)
	f:SetScript ("OnMouseUp", self.OnMouseUp)
	f:SetScript ("OnEnter", self.OnEnter)
	f:SetScript ("OnLeave", self.OnLeave)
	f:EnableMouse (true)

	f:SetScript ("OnUpdate", self.OnUpdate)

	local t = f:CreateTexture()
	f.texture = t
	t:SetAllPoints (f)

	f:Show()

	-- Create text

	if text then

		local fstr = f:CreateFontString()
		but.FStr = fstr
		fstr:SetFontObject ("NxFontS")
		fstr:SetJustifyH ("CENTER")
		fstr:SetHeight (height)
		but:SetText (text, 0, 0)
		fstr:Show()
	end

	---------------------------------------------------------------------------------------

	but:Update()

	---------------------------------------------------------------------------------------

	if template then	-- Social frame template?

--		Nx.prt ("But %s", template)
--		Nx.prtFrame ("But", f)

		local reg = { f:GetRegions() }
		for n, o in ipairs (reg) do
			if o:IsObjectType ("Texture") and o ~= f.texture then
				o:Hide()		-- Hide
			end
		end
	end


	return but
end

---------------------------------------------------------------------------------------
-- Set handler to notify user
---------------------------------------------------------------------------------------

function Nx.Button:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Get type
---------------------------------------------------------------------------------------

function Nx.Button:GetType()
	return self.Type
end

---------------------------------------------------------------------------------------
-- Set type
-- (Type string or nil)
---------------------------------------------------------------------------------------

function Nx.Button:SetType (typ)

	self.Frm.NxTip = self.Tip or (typ and self.TypeData[typ].Tip)
	self.Type = self.TypeData[typ]
end

---------------------------------------------------------------------------------------
-- Set Id
---------------------------------------------------------------------------------------

function Nx.Button:SetId (id)
	self.Id = id
end

---------------------------------------------------------------------------------------
-- Get pressed state
---------------------------------------------------------------------------------------

function Nx.Button:GetPressed()

	return self.Pressed
end

---------------------------------------------------------------------------------------
-- Set pressed state
---------------------------------------------------------------------------------------

function Nx.Button:SetPressed (down)

	self.Pressed = down
	self:Update()
end

---------------------------------------------------------------------------------------
-- Get state
---------------------------------------------------------------------------------------

function Nx.Button:GetState()

	return self.State
end

---------------------------------------------------------------------------------------
-- Set state
---------------------------------------------------------------------------------------

function Nx.Button:SetState (state)

	self.State = state
	self:Update()
end

---------------------------------------------------------------------------------------
-- Set button text and text position
---------------------------------------------------------------------------------------

function Nx.Button:SetText (text, x, y)

	local fstr = self.FStr

	if strbyte (text) ~= 124 then		-- |
		text = "|cffffbfaf" .. text
	end

	fstr:SetText (text)

	if x then
		fstr:SetPoint ("CENTER", x, y + 1)
	end
end

---------------------------------------------------------------------------------------
-- Set button texture
---------------------------------------------------------------------------------------

function Nx.Button:SetAlpha (a)
	self.Frm:SetAlpha (a)
end

---------------------------------------------------------------------------------------
-- Set button texture
---------------------------------------------------------------------------------------

function Nx.Button:SetTexture (tex)

	self.Tx = tex
end

---------------------------------------------------------------------------------------
-- Set button position
---------------------------------------------------------------------------------------

function Nx.Button:SetPos (side, x, y)

	self.Frm:SetPoint (side, x, y)
end

---------------------------------------------------------------------------------------
-- Set button size
---------------------------------------------------------------------------------------

function Nx.Button:SetSize (w, h)

	self.Frm:SetWidth (w)
	self.Frm:SetHeight (h)
end

---------------------------------------------------------------------------------------

function Nx.Button:OnMouseDown (button)

--	prt ("ButMouseDown "..tostring (button))

	local this = self				-- V4
	local but = this.NxBut

	if button == "LeftButton" or button == "MiddleButton" then

		if but.Type.Bool then

			but.Pressed = not but.Pressed

--			if IsShiftKeyDown() then	-- Force on. Keep?
--				but.Pressed = true
--			end

			if but.UserFunc then
				but.UserFunc (but.User, but, but.Id, button)
			end

		elseif but.Type.States then

			but.State = but.State % but.Type.States + 1

			if but.UserFunc then
				but.UserFunc (but.User, but, but.Id, button)
			end

		else
			but.Pressed = true

		end
	end

	if but.Type.Scroll then

		local x, y = GetCursorPosition()
		but.ScrollingX = x / this:GetEffectiveScale()
		but.ScrollingY = y / this:GetEffectiveScale()
		but.Scrolling = true

		return

	elseif button == "RightButton" then

		if but.UserFunc then
			but.UserFunc (but.User, but, but.Id, button)
		end
	end

	but:Update()
end

---------------------------------------------------------------------------------------

function Nx.Button:OnMouseUp (button)

--	Nx.prt ("ButMouseUp "..tostring (button))

	local this = self			-- V4
	local but = this.NxBut

	if button == "LeftButton" then

		if not (but.Type.Bool or but.Type.States or but.Type.Scroll) then

			but.Pressed = false

			if Nx.Util_IsMouseOver (but.Frm) then

				if but.UserFunc then
					but.UserFunc (but.User, but, but.Id, button)
				end
			end

		elseif but.Type.Scroll then

			but.Pressed = false
		end
	end

	but.Scrolling = false

	but:Update()
end

---------------------------------------------------------------------------------------
-- Handle mouse on button
---------------------------------------------------------------------------------------

function Nx.Button:OnEnter (motion)

	local this = self			-- V4
	local but = this.NxBut

	but.Over = true
	but:Update()

--	Nx.prt ("Enter %s", this.NxTip or "nil")

	local owner = this.NXTipFrm or this

	if GameTooltip:IsOwned (owner) then
		return
	end

	local tip = this.NxTip

	if tip then

		Nx.TooltipOwner = owner

		if this.NXTipFrm then
			GameTooltip:SetOwner (owner, "ANCHOR_TOPLEFT", 0, 0)
		else
			GameTooltip:SetOwner (owner, "ANCHOR_LEFT", 0, 5)
		end

		Nx:SetTooltipText (tip)
	end
end

---------------------------------------------------------------------------------------
-- Handle mouse leaving icon
---------------------------------------------------------------------------------------

function Nx.Button:OnLeave (motion)

	local this = self			-- V4
	local but = this.NxBut

	but.Over = nil
	but:Update()

	if not this:IsVisible() then
		return
	end

	local owner = this.NXTipFrm or this

	if GameTooltip:IsOwned (owner) then
		GameTooltip:Hide()
	end
end

---------------------------------------------------------------------------------------

function Nx.Button:OnUpdate (elapsed)

	local this = self			-- V4
	local but = this.NxBut

	if but.Scrolling then

		local cx, cy = GetCursorPosition()
		cx = cx / this:GetEffectiveScale()
		cy = cy / this:GetEffectiveScale()

		local x = cx - but.ScrollingX
		local y = but.ScrollingY - cy

		if x ~= 0 or y ~= 0 then

			but.ScrollingX = cx
			but.ScrollingY = cy

			if IsShiftKeyDown() then
				x = x * .1
				y = y * .1
			end

			if but.UserFunc then
				but.UserFunc (but.User, but, but.Id, "scroll", x, y)
			end
		end
	end

end

---------------------------------------------------------------------------------------

function Nx.Button:Update()

--	Nx.prt ("But Update: %s", debugstack (2, 3, 0))

	local typ = self.Type
	if not typ then
		return
	end

	local Skin = Nx.Skin

	local f = self.Frm
	local tx = f.texture

	if self.State then

		local stateT = typ[self.State] or typ[1]

		local txName = self.Tx or stateT.Tx or typ.Tx

		if typ.Skin then
			txName = Skin:GetTex (txName)
		else
			if txName then
				if type (txName) == "string" then
					txName = gsub (txName, "%$", "Interface\\Icons\\")
				else
					if (strmatch(txName,"|")) then
						tx:SetColorTexture (Nx.Util_str2rgba (txName))
					else
						tx:SetTexture (txName)
					end
					txName = nil
				end
			end
		end

		if txName then
			tx:SetTexture (txName)
		else
			local rgb = stateT.RGB
			if rgb then
				tx:SetColorTexture (Nx.Util_str2rgb (rgb))
			end
		end

		if stateT.Alpha then
			tx:SetVertexColor (1, 1, 1, stateT.Alpha)
		elseif stateT.VRGBA then
			tx:SetVertexColor (Nx.Util_str2rgba (stateT.VRGBA))
		end

		local sz = stateT.Size
		if sz then
			f:SetWidth (sz)
			f:SetHeight (sz)
		end

	else

		if self.Pressed then

			local txName = self.Tx or typ.Dn

			if typ.Skin then
				txName = Skin:GetTex (txName)
			else
				if txName then
					if type (txName) == "string" then
						txName = gsub (txName, "%$", "Interface\\Icons\\")
					else
						if (strmatch(txName,"|")) then
							tx:SetColorTexture (Nx.Util_str2rgba (txName))
						else
							tx:SetTexture (txName)
						end
						txName = nil
					end
				end
			end

			if txName then
				tx:SetTexture (txName)
			else
				local rgb = typ.RGBDn
				if rgb then
					tx:SetColorTexture (Nx.Util_str2rgb (rgb))
				end
			end

			if typ.AlphaDn then
				tx:SetVertexColor (1, 1, 1, typ.AlphaDn)
			elseif typ.VRGBADn then
				tx:SetVertexColor (Nx.Util_str2rgba (typ.VRGBADn))
			end

			local sz = typ.SizeDn
			if sz then
				f:SetWidth (sz)
				f:SetHeight (sz)
			end

		else

			local txName = self.Tx or typ.Up

			if typ.Skin then
				txName = Skin:GetTex (txName)
			else
				if txName then
					if type (txName) == "string" then
						txName = gsub (txName, "%$", "Interface\\Icons\\")
					else						
						if (strmatch(txName,"|")) then
							tx:SetColorTexture (Nx.Util_str2rgba (txName))
						else
							tx:SetTexture (txName)
						end
						txName = nil
					end
				end
			end

			if txName then
				tx:SetTexture (txName)
				if typ.UpUV then
					local uv = typ.UpUV
					tx:SetTexCoord (uv[1], uv[2], uv[3], uv[4])
				end
			else
				local rgb = typ.RGBUp
				if rgb then
					tx:SetColorTexture (Nx.Util_str2rgb (rgb))
				end
			end

			if typ.AlphaUp then
				tx:SetVertexColor (1, 1, 1, typ.AlphaUp)
			elseif typ.VRGBAUp then
				tx:SetVertexColor (Nx.Util_str2rgba (typ.VRGBAUp))
			end

			local sz = typ.SizeUp
			if sz then
				f:SetWidth (sz)
				f:SetHeight (sz)
			end
		end
	end

	local of = Nx.Button.OverFrm

	if self.Over then

--		tx:SetBlendMode ("ADD")

--		local al = typ.AlphaOv
--		if al then
--			tx:SetVertexColor (1, 1, 1, al)
--		end

		of:SetPoint ("TOPLEFT", f, -1, 1)

		of:SetWidth (f:GetWidth() + 2)
		of:SetHeight (f:GetHeight() + 2)

		if self.Pressed then
			of.texture:SetColorTexture (Nx.Util_str2rgba (".188|.188|.5|1"))
		else
			of.texture:SetColorTexture (Nx.Util_str2rgba ("0.06|0.06|.250|1"))
		end

--		local lev = f:GetFrameLevel()
--		of:SetFrameLevel (lev + 1)		-- Causing glitch when button pressed
		of:SetParent (f)			-- Seems to make us draw in front

		of:Show()
		Nx.Button.OverFrmOwn = f

	else
--		tx:SetBlendMode ("BLEND")

		if Nx.Button.OverFrmOwn == f then
			of:Hide()
		end
	end

	if typ.Dim then
		SetDesaturation (tx, not self.Pressed)
	end
end

---------------------------------------------------------------------------------------
-- Edit box
---------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------
-- Create a edit box
-- ()
-- ret: edit box table
---------------------------------------------------------------------------------------

function Nx.EditBox:Create (parentFrm, user, func, maxLetters)

	local box = {}					-- New

	setmetatable (box, self)
	self.__index = self

	box:SetUser (user, func)

	local f = CreateFrame ("EditBox", nil, parentFrm)
	box.Frm = f

	f.NxInst = box

	f:SetScript ("OnEditFocusGained", self.OnEditFocusGained)
	f:SetScript ("OnEditFocusLost", self.OnEditFocusLost)
	f:SetScript ("OnTextChanged", self.OnTextChanged)
	f:SetScript ("OnEnterPressed", self.OnEnterPressed)
	f:SetScript ("OnEscapePressed", self.OnEscapePressed)

	f:SetFontObject ("NxFontS")

	local t = f:CreateTexture()
	t:SetColorTexture (.1, .2, .3, 1)
	t:SetAllPoints (f)
	f.texture = t

	f:SetAutoFocus (false)
	f:ClearFocus()

	box.FilterDesc = L["Search: [click]"]
	box.FilterDescEsc = L["Search: %[click%]"]

--	if Nx.Free then
--		box.FilterDesc = "Search: " .. Nx.FreeMsg
--	end

	box.FilterStr = ""
	f:SetText (box.FilterDesc)
	f:SetMaxLetters (maxLetters)

	return box
end

---------------------------------------------------------------------------------------
-- Set handler to notify user
---------------------------------------------------------------------------------------

function Nx.EditBox:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Get text
---------------------------------------------------------------------------------------

function Nx.EditBox:GetText()
	return self.FilterStr
end

---------------------------------------------------------------------------------------

function Nx.EditBox:OnEditFocusGained()

	Nx.ShowMessageTrial()

	local this = self			-- V4
	local self = this.NxInst
	if self.FilterStr ~= "" then
		this:SetText (self.FilterStr)
	else
		this:SetText ("")
	end
end

function Nx.EditBox:OnEditFocusLost()

	local this = self			-- V4
	local self = this.NxInst
	if self.FilterStr == "" then
		this:SetText (self.FilterDesc)
	end
end

function Nx.EditBox:OnTextChanged()

	local this = self			-- V4
	local self = this.NxInst
	self.FilterStr = gsub (this:GetText(), self.FilterDescEsc, "")

--	Nx.prt ("Filter=%s", self.FilterStr)

	if self.UserFunc then
		self.UserFunc (self.User, self, "Changed")
	end
end

function Nx.EditBox:OnEnterPressed()
	self:ClearFocus()
end

function Nx.EditBox:OnEscapePressed()

	local this = self			-- V4
	local self = this.NxInst
	self.FilterStr = ""

	this:ClearFocus()
end

---------------------------------------------------------------------------------------
-- Scroll bar
---------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------
-- Create a Scroll Bar
-- ()
-- ret: table
---------------------------------------------------------------------------------------

--[[

function ScrollBar:Create (parentFrm, typ, bx, by, width, height, func, user)

	local c2rgba = Nx.Util_c2rgba

	parentFrm = parentFrm or UIParent

	local sbar = {}			-- New

	setmetatable (sbar, self)
	self.__index = self

	sbar:SetUser (user, func)

	-- Create frame

	local f = CreateFrame ("Frame", nil, parentFrm)
	sbar.Frm = f
	f.NxSBar = but

	f.NxTip = tip or (typ and Button.TypeData[typ].Tip)

	side = side or "TOPLEFT"
	f:SetPoint (side, bx, by)

	f:SetWidth (width)
	f:SetHeight (height)

	f:SetFrameStrata ("MEDIUM")

	f:SetScript ("OnMouseDown", self.OnMouseDown)
	f:SetScript ("OnMouseUp", self.OnMouseUp)
--	f:SetScript ("OnEnter", self.OnEnter)
--	f:SetScript ("OnLeave", self.OnLeave)
	f:EnableMouse (true)

	f:SetScript ("OnUpdate", self.OnUpdate)

	local t = f:CreateTexture()
	f.texture = t
	t:SetAllPoints (f)

	if typ and Button.TypeData[typ] then

		but.Type = Button.TypeData[typ]

		t:SetTexture (but.Type.Up)
	else
		t:SetTexture (c2rgba ("202020c8"))
	end

	f:Show()

	--

--	Button:Create (f, "Close", nil, nil, -Window.BORDERW, -Window.BORDERH + 4, "TOPRIGHT", 20, 20, win.OnCloseButDown, win)

	--

	return sbar
end

---------------------------------------------------------------------------------------

function ScrollBar:OnMouseDown (button)

--	prt ("ButMouseDown "..tostring (button))

	local sb = this.NxSBar


		local x, y = GetCursorPosition()
		sb.ScrollingX = x / this:GetEffectiveScale()
		sb.ScrollingY = y / this:GetEffectiveScale()
		sb.Scrolling = true



	sb:Update()
end

---------------------------------------------------------------------------------------

function ScrollBar:OnMouseUp (button)

--	prt ("ButMouseUp "..tostring (button))

	local sb = this.NxSBar

	sb.Scrolling = false

	sb:Update()
end

---------------------------------------------------------------------------------------

function ScrollBar:Update()

	if self.Scrolling then

		if self.Func then
			self.Func (self.User, self)
		end

	end
end
--]]

-------------------------------------------------------------------------------
-- Menus

function Nx.Menu:Init()

	self.Menus = {}		-- All created menus

	self.Item_ALPHAFADE = 0
	self.NameNum = 0

	self.__index = self
	Nx.MenuI.__index = Nx.MenuI
end

---------------------------------------------------------------------------------------
-- Check if any menus are opened
---------------------------------------------------------------------------------------

function Nx.Menu:IsAnyOpened()

	-- Only one can be current

	return self.Cur and self.Cur.MainFrm:IsVisible()	-- Esc may have hidden

--[[
	if self.Menus then
		for menu in pairs (self.Menus) do
--			Nx.prt ("%s", menu.MainFrm:GetName())
--			if menu.MainFrm:IsVisible() and not menu.Closing then
			if menu.MainFrm:IsVisible() then
				return true
			end
		end
	end
--]]
end

---------------------------------------------------------------------------------------
-- Create a menu
---------------------------------------------------------------------------------------

function Nx.Menu:Create (parentFrm, width)

	local c2rgba = Nx.Util_str2rgba

	local menu = {}		-- New menu

	self.Menus[menu] = true

	setmetatable (menu, self)

	menu.Items = {}
	menu.Alpha = 1
	menu.CloseTimer = 0	-- In seconds
	menu.Width = width or 210

	self.NameNum = self.NameNum + 1
	local name = format ("NxMenu%d", self.NameNum)

	local f = CreateFrame ("Frame", name, UIParent)
	menu.MainFrm = f

	tinsert (HideFramesOnEsc, name)

	f.NxMenu = menu

	f:Hide()

--	f:SetToplevel (1)
	f:SetScript ("OnUpdate", self.OnUpdate)
--	f:SetScript ("OnLeave", self.OnLeave)
	f:EnableMouse (true)
--[[
	local t = f:CreateTexture()
	t:SetTexture (c2rgba ("202020e0"))
	t:SetAllPoints (f)
	f.texture = t
--]]

	menu:SetSkin()

	return menu
end

hooksecurefunc("ToggleGameMenu", function()
	for k, v in pairs (HideFramesOnEsc) do
		_G[v]:Hide()
	end
end)

function Nx.Menu:ResetSkins()

	if self.Menus then
		for menu, v in pairs (self.Menus) do
			menu:SetSkin()
		end
	end
end

function Nx.Menu:SetSkin()

	local f = self.MainFrm

	local bk = Nx.Skin:GetBackdrop()
	f:SetBackdrop (bk)

	local col = Nx.Skin:GetBGCol()
	f:SetBackdropColor (col[1], col[2], col[3], col[4])

	local col = Nx.Skin:GetBorderCol()
	f:SetBackdropBorderColor (col[1], col[2], col[3], col[4])
end

function Nx.Menu:OnUpdate (elapsed)

	local this = self			-- V4
	local self = this.NxMenu

--	Nx.prt ("elapsed %f %f", elapsed, self.CloseTimer)

	self.Alpha = Nx.Util_StepValue (self.Alpha, self.AlphaTarget, elapsed * 4)
	this:SetAlpha (self.Alpha)

	if self.Closing then

		if self.Alpha <= 0 then
			self.Closing = nil
			this:Hide()
		end
		return
	end

	local x, y = GetCursorPosition()
	x = x / this:GetEffectiveScale()
	y = y / this:GetEffectiveScale()

	if x < this:GetLeft() - 1 or x > this:GetRight()
			or y < this:GetBottom() or y > this:GetTop() + 1 then

--		prt ("MenuC "..x.." "..y)
--		prtFrame ("Menu", this)

		if not Nx.Menu.SliderMoving then

			self.CloseTimer = self.CloseTimer - elapsed
			if self.CloseTimer <= 0 then
				self:Close()
			end
		end

	else
		self.CloseTimer = .5
	end
end

---------------------------------------------------------------------------------------
-- Add sub menu
---------------------------------------------------------------------------------------

function Nx.Menu:AddSubMenu (menu, text)

	local item = {}
	self.Items[#self.Items + 1] = item

	setmetatable (item, Nx.MenuI)

	item.Menu = self
	item.SubMenu = menu
	item.Text = text
	item.ShowState = 1

	return item
end

---------------------------------------------------------------------------------------
-- Add menu item
---------------------------------------------------------------------------------------

function Nx.Menu:AddItem (id, text, func, user)

	local item = {}
	self.Items[#self.Items + 1] = item

	setmetatable (item, Nx.MenuI)

	item.Menu = self
--	item.Type = "Text"
	item.Id = id
	item.Text = text
	item.Func = func
	item.User = user
	item.ShowState = 1

--	item.GetChecked = self.Item_GetChecked
--	item.SetChecked = self.Item_SetChecked
--	item.GetSlider = self.Item_GetSlider
--	item.SetSlider = self.Item_SetSlider

	if text == "" then
		item.Spacer = true
	end

	return item
end

function Nx.MenuI:SetText (text)
	self.Text = text
end

function Nx.MenuI:GetChecked()
	return self.Checked
end

function Nx.MenuI:SetChecked (checked, varName)

	self.Check = true

	if type (checked) == "table" then
		assert (varName)
		self.Table = checked
		self.VarName = varName
		checked = self.Table[varName]
	end

	self.Checked = checked

	if self.Table then
		self.Table[self.VarName] = checked
	end
end

---------------------------------------------------------------------------------------
-- Get slider position
---------------------------------------------------------------------------------------

function Nx.MenuI:GetSlider()
	return self.SliderPos
end

---------------------------------------------------------------------------------------
-- Set slider position and optionally min and max values
---------------------------------------------------------------------------------------

function Nx.MenuI:SetSlider (pos, min, max, step, varName)
	if type (pos) == "table" then
		assert (varName)
		self.Table = pos
		self.VarName = varName
		pos = self.Table[varName]
	end

	self.Slider = true

	if min then
		self.SliderMin = math.min (min, max)
		self.SliderMax = math.max (min, max)
	end

	if step then
		self.Step = step
	end

	-- Floor to step pos
	if self.Step then
		pos = floor (pos / self.Step + .5) * self.Step
	end

	-- Clamp pos
	pos = math.max (pos, self.SliderMin)
	pos = math.min (pos, self.SliderMax)

	self.SliderPos = pos

	if self.Table then
		self.Table[self.VarName] = pos
	end
end

---------------------------------------------------------------------------------------
-- Set menu item show state
-- (false hides. -1 shows as disabled)
---------------------------------------------------------------------------------------

function Nx.MenuI:Show (show)

--	Nx.prt ("show %s %s", self.Text, show and 1 or 0)

	self.ShowState = false
	if show ~= false then
		self.ShowState = (type (show) == "number") and show or 1
	end
--	Nx.prtVar ("show", self.ShowState)
end

---------------------------------------------------------------------------------------
-- Not used?
---------------------------------------------------------------------------------------

--[[
function Nx.Menu:Item_GetUser (item)
	return item.User
end

function Nx.Menu:Item_SetUser (item, user)
	item.User = user
end
--]]

---------------------------------------------------------------------------------------
-- Open a menu
-- self = menu instance
---------------------------------------------------------------------------------------

function Nx.Menu:Open()

	if Nx.Menu.Cur then		-- Force current menu to close
		Nx.Menu.Cur:Close()
	end

	Nx.Menu.Cur = self

	local mf = self.MainFrm

	self.Closing = nil
	self.CloseTimer = 60 * 1
	self.Alpha = 0
	self.AlphaTarget = 1

	local menuW = self.Width
	local menuH = self:Update() + 14

	mf:SetFrameStrata ("DIALOG")
	mf:SetClampedToScreen (true)
	mf:SetWidth (menuW)
	mf:SetHeight (menuH)

	local cx, cy = GetCursorPosition()
	cx = cx / UIParent:GetEffectiveScale()
	cy = cy / UIParent:GetEffectiveScale()

--	prt (format ("Menu Open (%f, %f)", cx, cy))

	local x = cx - 4
	local y = cy + 4
	if Nx.db.profile.Menu.CenterH then
		x = cx - menuW * .5
	end
	if Nx.db.profile.Menu.CenterV then
		y = cy + menuH * .5
	end

	mf:SetPoint ("TOPLEFT", UIParent, "BOTTOMLEFT", x, y)
	mf:Show()

	mf:Raise()
end

function Nx.Menu:Update()

	local mf = self.MainFrm

	local menuX = 14
	local menuY = 14
	local menuW = self.Width

	for n = 1, #self.Items do

		local item = self.Items[n]

		local itemF = item.Frm

--		Nx.prt ("Open %s", item.Text)

		if not item.ShowState then

--			Nx.prt ("Hide %s", item.Text)

			if itemF then
				itemF:Hide()
			end
		else

			local itemH = 12		-- Not counting space between items

			if not item.Spacer then

				item.Alpha = 0
				item.AlphaTarget = self.Item_ALPHAFADE

				if not itemF then
					item.Frm = CreateFrame ("Frame", nil, mf)
					itemF = item.Frm
					itemF.NxMenuItem = item

--					itemF:SetFrameStrata ("DIALOG")
					itemF:SetWidth (menuW - menuX * 2)

					itemF:SetScript ("OnEnter", self.Item_OnEnter)
					itemF:SetScript ("OnLeave", self.Item_OnLeave)
					itemF:SetScript ("OnUpdate", self.Item_OnUpdate)
					itemF:SetScript ("OnMouseDown", self.Item_OnMouseDown)
					itemF:SetScript ("OnMouseUp", self.Item_OnMouseUp)

					local t = itemF:CreateTexture()
					t:SetColorTexture (1, 1, 1, 1)
					t:SetAllPoints (itemF)
					itemF.texture = t
				end

--				if item.SubMenu then
--					item.SubMenu:Close()
--				end

				if item.Text then

					local fstr = item.TextFStr

					if not fstr then

						fstr = itemF:CreateFontString()
						item.TextFStr = fstr
						fstr:SetFontObject ("NxFontMenu")
						fstr:SetPoint ("TOPLEFT", 20, 0)
						fstr:SetWidth (menuW - 20)
						fstr:SetHeight (12)
						fstr:SetJustifyH ("LEFT")
					end

					if item.ShowState < 0 then
						fstr:SetText ("|cff707070" .. item.Text)
					else
						fstr:SetText ("|cfff7f7f7" .. item.Text)
					end

					fstr:Show()
				end

				if item.Check then

					local frm = item.CheckFrm

					if not frm then
						frm = CreateFrame ("Frame", nil, itemF)
						item.CheckFrm = frm

						frm:SetWidth (12)
						frm:SetHeight (12)

						frm.texture = frm:CreateTexture()
						frm.texture:SetAllPoints (frm)
					end

					frm:SetPoint ("TOPLEFT", 1, 0)
					frm:Show()

					self:CheckUpdate (item)
				end

				if item.Slider then

					itemF:SetScript ("OnMouseWheel", self.Item_OnMouseWheel)
					itemF:EnableMouseWheel (true)

					local h = 10

					local frm = item.SliderFrm

					if not frm then
						frm = CreateFrame ("Frame", nil, itemF)
						item.SliderFrm = frm

						frm:SetWidth (102)
						frm:SetHeight (h)

						frm.texture = frm:CreateTexture()
						frm.texture:SetAllPoints (frm)
						frm.texture:SetColorTexture (0, 0, 0, .5)
					end

					local tfrm = item.SliderThumbFrm

					if not tfrm then
						tfrm = CreateFrame ("Frame", nil, frm)
						item.SliderThumbFrm = tfrm

--						tfrm:SetFrameStrata ("DIALOG")
						tfrm:SetWidth (3)
						tfrm:SetHeight (h)

						tfrm.texture = tfrm:CreateTexture()
						tfrm.texture:SetAllPoints (tfrm)
						tfrm.texture:SetColorTexture (.5, 1, .5, 1)
					end

					frm:SetPoint ("TOPLEFT", 12, -itemH - 1)
					frm:Show()

					self:SliderUpdate (item)

					itemH = itemH + h + 2
				end

				itemF:SetPoint ("TOPLEFT", menuX, -menuY)
				itemF:SetHeight (itemH)
				itemF:Show()
				itemF:EnableMouse (true)
			end

			menuY = menuY + itemH + 1
		end
	end

	return menuY
end

---------------------------------------------------------------------------------------

function Nx.Menu:Close()

	self.Closing = true
	self.AlphaTarget = 0

	if Nx.Menu.Cur == self then
		Nx.Menu.Cur = false
	end
end

---------------------------------------------------------------------------------------
-- Set all menu items show state
-- (false hides. -1 shows as disabled)
---------------------------------------------------------------------------------------

function Nx.Menu:Show (show)

	for _, item in ipairs (self.Items) do
		item:Show (show)
	end
end

---------------------------------------------------------------------------------------
-- Update all items check mark

--function Nx.Menu:CheckUpdateAll()
--	for _, item in ipairs (self.Items) do
--		self:CheckUpdate (item)
--	end
--end
---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
-- Update check mark
-- self = invalid
---------------------------------------------------------------------------------------

function Nx.Menu:CheckUpdate (item)

	local f = item.CheckFrm
	if f then
		local t = f.texture
		local txName

		if item.Table then
			item.Checked = item.Table[item.VarName]
		end

		if item.Checked then
			txName = Nx.Skin:GetTex ("ButChk")
		else
			txName = Nx.Skin:GetTex ("But")
		end

		t:SetTexture (txName)
	end
end

---------------------------------------------------------------------------------------
-- Update slider
-- self = invalid
---------------------------------------------------------------------------------------

function Nx.Menu:SliderUpdate (item)

	if item.Table then
		item.SliderPos = item.Table[item.VarName]
	end

	local tfrm = item.SliderThumbFrm
	local per = (item.SliderPos - item.SliderMin) / (item.SliderMax - item.SliderMin)

	tfrm:SetPoint ("TOPLEFT", per * 100, 0)

	if item.Text then

		local fstr = item.TextFStr
		fstr:SetText (format ("%s (%.2f)", item.Text, item.SliderPos))
	end
end

---------------------------------------------------------------------------------------

function Nx.Menu:Item_OnEnter (motion)

	local this = self			-- V4
	local item = this.NxMenuItem

	if item.ShowState and item.ShowState < 0 then
		item.AlphaTarget = .5
	else
		item.AlphaTarget = .9
	end
end

function Nx.Menu:Item_OnLeave (motion)

	local this = self			-- V4
	local item = this.NxMenuItem

	item.AlphaTarget = Nx.Menu.Item_ALPHAFADE

--	item.SliderMoving = nil
end

function Nx.Menu:Item_OnUpdate (elapsed)

	local this = self			-- V4
	local item = this.NxMenuItem

	item.Alpha = Nx.Util_StepValue (item.Alpha, item.AlphaTarget, elapsed * 4)

	this.texture:SetVertexColor (.2, .2, .5, item.Alpha)

	if item.Slider and item == Nx.Menu.SliderMoving then
		Nx.Menu:Item_HandleSlider (item)
	end
end

function Nx.Menu:Item_OnMouseDown (button)

	local this = self			-- V4
	local item = this.NxMenuItem

	if button == "LeftButton" then

		if item.Check then

			item:SetChecked (not item.Checked)
			Nx.Menu:CheckUpdate (item)

			if item.Func then
				item.Func (item.User, item, item.User)
			end

		elseif item.Slider then

			Nx.Menu.SliderMoving = item
			Nx.Menu:Item_HandleSlider (item)

		elseif item.SubMenu then

			item.SubMenu:Open()
--			item.Menu:Close()

		else

			if item.ShowState and item.ShowState >= 0 then
				if item.Func then
					item.Func (item.User, item, item.User)
				end
			end

			item.Menu:Close()
		end
	end
end

function Nx.Menu:Item_OnMouseUp (button)

	local this = self			-- V4
	local item = this.NxMenuItem

	if button == "LeftButton" then
		Nx.Menu.SliderMoving = nil
	end
end

function Nx.Menu:Item_OnMouseWheel (value)

	local this = self			-- V4
	local item = this.NxMenuItem

	value = (value > 0 and 1 or -1) * (item.Step or .01)

	if IsShiftKeyDown() then
		value = value * 10
	end

	local x = item:GetSlider() + value

	if IsAltKeyDown() then
		x = 1
	end

	Nx.Menu:Item_SetUpdateSlider (item, x)
end

function Nx.Menu:Item_HandleSlider (item)

	local frm = item.SliderFrm
	local x = Nx.Util_GetMouseClampedXY (frm)

	if x then

		x = (x - 1) / (frm:GetWidth() - 2) * (item.SliderMax - item.SliderMin) + item.SliderMin

		if IsShiftKeyDown() then
			x = floor (x * 10) / 10
		end

		if IsAltKeyDown() then
			x = 1
		end

		Nx.Menu:Item_SetUpdateSlider (item, x)
	end
end

function Nx.Menu:Item_SetUpdateSlider (item, x)

	local old = item:GetSlider()
	item:SetSlider (x)

--	Nx.prt ("Slider %f", x)

	if item:GetSlider() ~= old then

		Nx.Menu:SliderUpdate (item)

--		Nx.prtVar ("Slider", item.Func)

		if item.Func then
			item.Func (item.User, item, item.User)
		end
	end
end

---------------------------------------------------------------------------------------
-- List
---------------------------------------------------------------------------------------

--[[
Nx.List.FontSizeConvert = {
	10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
	10, 10, 15, 15, 15, 15, 15, 15, 15, 15
}

Nx.List.FontNames = {
	[10] = "FontS",
	[15] = "FontM"
}
--]]

function Nx.List:Init()

	local ldata = Nx:GetData ("List")
	self.SaveData = ldata

	if not ldata.Version or ldata.Version < Nx.VERSIONList then

		if ldata.Version then
			Nx.prt (L["Reset old list data"])
		end
		ldata.Version = Nx.VERSIONList

		for k, list in pairs (ldata) do
			if type (list) == "table" then
--				Nx.prt (" Reset %s", k)
				ldata[k] = nil
			end
		end
	end

	self.Lists = {}

	local frms = {}
	self.Frms = frms		-- Table of frame type tables

	self.FrmsUniqueI = 0

	local types = { "Color", "WatchItem", "Info" }

	for n, s in ipairs (types) do
		frms[s] = {}
	end
end

---------------------------------------------------------------------------------------
-- Free list frames by adding back to global list
---------------------------------------------------------------------------------------

-- Creds to Deathcore for frame fix
local visFrms = {}

function Nx.List:FreeFrames (list)
	local frms = self.Frms
	for n, f in ipairs (list.UsedFrms) do
		if not InCombatLockdown() then
			f:Hide()
		else
			tinsert(visFrms, f)
		end
		tinsert (frms[f.NXListFType], n, f)
	end
	if not InCombatLockdown() then
		for i, j in ipairs (visFrms) do
			j:Hide()
		end
		wipe(visFrms or {})
	end
	list.UsedFrms = wipe (list.UsedFrms or {})
end

---------------------------------------------------------------------------------------
-- Reset list frames by adding back to global list
---------------------------------------------------------------------------------------

--[[
function Nx.List:FreeFrame (list, frm)

	list.UsedFrms[frm] = nil

	local frms = self.Frms

	frm:Hide()
	tinsert (frms[frm.NXListFType], frm)
end
--]]

---------------------------------------------------------------------------------------
-- Get a list frame from the global list
---------------------------------------------------------------------------------------

function Nx.List:GetFrame (list, typ)
	local frms = self.Frms[typ]
	local f = tremove (frms, 1)
	if not f then
		self.FrmsUniqueI = self.FrmsUniqueI + 1
		if typ == "Color" then
			f = CreateFrame ("ColorSelect", nil, list.Frm)
		elseif typ == "WatchItem" then							
				f = CreateFrame ("Button", "NxListFrms" .. self.FrmsUniqueI, list.Frm, "NxWatchListItem")
				f:SetAttribute ("type1", "item")			
		elseif typ == "Info" then
			f = Nx.Info:CreateFrame (list.Frm)
		end
		f.NXListFType = typ
	end
	f:Show()
	f:SetParent (list.Frm)
	tinsert (list.UsedFrms, f)
	return f
end

---------------------------------------------------------------------------------------
-- Flag next update of all lists for a full update
---------------------------------------------------------------------------------------

function Nx.List:NextUpdateFull()

	if self.Lists then
		for inst in pairs (self.Lists) do
			inst.SSW = nil
		end
	end
end

---------------------------------------------------------------------------------------
-- Setup creation font
---------------------------------------------------------------------------------------

function Nx.List:SetCreateFont (font, baseLineH)
--[[
	if type (font) == "number" then

		local h = self.FontSizeConvert[font]
		self.CFont = self.FontNames[h]
		self.CLineH = h
		self.CBaseLineH = baseLineH or h
		return
	end
--]]
	self.CFont = font
--	self.CLineH = nil
	self.CBaseLineH = baseLineH
end

---------------------------------------------------------------------------------------
-- Create
---------------------------------------------------------------------------------------

function Nx.List:Create (saveName, xpos, ypos, width, height, parentFrm, showAll, noHeader)

	if not self.CFont then				-- Not set?
		self:SetCreateFont ("Font.Small")	-- Default
	end

	local inst = {}					-- New instance

	setmetatable (inst, self)
	self.__index = self

	if saveName then

		local save = self.SaveData[saveName] or {}
		self.SaveData[saveName] = save
		inst.Save = save

		if save["ColW"] then
			inst.SaveColumnWidths = { Nx.Split ("^", save["ColW"]) }
		end
	end

	inst.Columns = {}
	inst.Strs = {}
	inst.Buts = {}

	inst.Font = self.CFont
	inst.FontObj = Nx.Font:GetObj (inst.Font)
--	inst.LineH = self.CLineH
	inst.LineHPad = 0
	inst.BaseLineH = self.CBaseLineH
	inst.Top = 1
	inst.Vis = 1
	inst.Selected = 1
	inst.ShowAll = showAll

	inst:SetMinSize()

	-- Add to list of lists

	self.Lists[inst] = true
	inst.UsedFrms = {}

	-- Create list frame

	local frm = CreateFrame ("Frame", nil, parentFrm)
	inst.Frm = frm
	frm.NxInst = inst

	frm:SetScript ("OnMouseDown", self.OnMouseDown)
	frm:EnableMouse (true)
	frm:SetScript ("OnMouseWheel", self.OnMouseWheel)
	frm:EnableMouseWheel (true)

	frm.texture = frm:CreateTexture()
	frm.texture:SetAllPoints (frm)
	frm.texture:SetColorTexture (0, 0, 0, .3)

	frm:SetPoint ("TOPLEFT", xpos, ypos)
	frm:Show()

	-- Create list header

	inst.HdrH = 0

	if not noHeader then

		inst.HdrH = 12

		local hfrm = CreateFrame ("Frame", nil, frm)
		inst.HdrFrm = hfrm
		hfrm.NxInst = inst

		hfrm:SetScript ("OnMouseDown", self.OnHdrMouseDown)
		hfrm:EnableMouse (true)

		hfrm.texture = hfrm:CreateTexture()
		hfrm.texture:SetAllPoints (hfrm)
		hfrm.texture:SetColorTexture (.2, .2, .3, 1)
		hfrm:SetPoint ("TOPLEFT", 0, 0)
		hfrm:Show()
	end

	-- Create selected line frame

	local sfrm = CreateFrame ("Frame", nil, frm)
	inst.SelFrm = sfrm
	sfrm.NxInst = inst

	sfrm.texture = sfrm:CreateTexture()
	sfrm.texture:SetAllPoints (sfrm)
	sfrm.texture:SetColorTexture (.4, .4, .5, .4)
	sfrm.texture:SetBlendMode ("Add")

--	sfrm:SetHeight (inst:GetLineH() + 1)
	sfrm:Hide()

	-- Slider

	if not showAll then
		inst.Slider = Nx.Slider:Create (frm, "V", 10, inst.HdrH)
		inst.Slider:SetUser (inst, self.OnSlider)
	end

	-- Finish setup

	inst:Empty()
	inst:SetSize (width, height)

	-- Reset creation data

	self.CFont = nil

	--

	return inst
end

---------------------------------------------------------------------------------------
-- Set user of the list (for event handlers) and generic callback function
-- (instance)
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:SetUser (user, func)
	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Set list fade
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:SetFade (fade)

--	prt ("Fade "..fade)

	if not self.NoBGFade then
		self.Frm.texture:SetVertexColor (1, 1, 1, fade)
	end

	local hf = self.HdrFrm
	if hf then
		hf.texture:SetVertexColor (1, 1, 1, fade)
	end

	self.SelFrm:SetAlpha (fade)

	if self.Slider then
		self.Slider.Frm.texture:SetAlpha (fade * .6)
		self.Slider.ThumbFrm.texture:SetAlpha (fade * .9)
	end
end

---------------------------------------------------------------------------------------
-- Set list background color
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:SetBGColor (r, g, b, a, noFade)
	if self.Frm.texture then
		self.Frm.texture:SetColorTexture (r, g, b, a or 1)
	end

	self.NoBGFade = noFade
end

---------------------------------------------------------------------------------------
-- Set list font size
-- self = instance
---------------------------------------------------------------------------------------

--[[
function Nx.List:SetFont (fontSize)

	local h = self.FontSizeConvert[fontSize]
	self.Font = self.FontNames[h]
	self.LineH = fontSize

	for id, column in ipairs (self.Columns) do
		column.Font = self.Font
	end
end
--]]

---------------------------------------------------------------------------------------
-- Set list line height
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:SetLineHeight (height, hdrH)

	self.LineHPad = height

	self.HdrH = hdrH or 12

	if self.Slider then
		self.Slider:SetTLOff (self.HdrH)
	end

	self:Update()
end

function Nx.List:GetLineH()
	return Nx.Font:GetH (self.Font) + self.LineHPad
end

---------------------------------------------------------------------------------------
-- Set list item frame info
---------------------------------------------------------------------------------------

function Nx.List:SetItemFrameScaleAlpha (scale, alpha)
	self.ItemFrameScale = scale
	self.ItemFrameAlpha = alpha
end

---------------------------------------------------------------------------------------
-- Lock or unlock list
---------------------------------------------------------------------------------------

function Nx.List:Lock (lock)

	self.Frm:EnableMouse (not lock)
	self.Frm:EnableMouseWheel (not lock)
end

---------------------------------------------------------------------------------------
-- Force a full list update
---------------------------------------------------------------------------------------

function Nx.List:FullUpdate()
	local w = self.SSW
	self.SSW = nil
	self:SetSize (w, self.SSH)
end

---------------------------------------------------------------------------------------
-- Set minimum list size (width or height can be nil)
---------------------------------------------------------------------------------------

function Nx.List:SetMinSize (width, height)
	self.MinW = width or 2
	self.MinH = height or 1
end

---------------------------------------------------------------------------------------
-- Update list size
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:SetSize (width, height)

	if width == self.SSW and height == self.SSH then
--		Nx.prt ("List SetSize SKIP %s %s", width, height)
		return
	end

--	Nx.prt ("List SetSize %s %s", width, height)

	self.SSW = width
	self.SSH = height

	if not self.ShowAll then
		self:Resize (width, height)
	end

	self:Update()
end

function Nx.List:GetSize()
	return self.SSW, self.SSH
end

---------------------------------------------------------------------------------------
-- Update list size
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:Resize (width, height)
	local f = self.Frm
	
	if f:IsProtected() then
		return
	end

	local hdrH = self.HdrH
	local lineH = self:GetLineH()
	local padW = 1
	local padH = 0

	if self.ShowAll then

		height = self.Num * lineH + hdrH + padH * 2

		local last = self.Top + self.Vis - 1
		last = min (last, self.Num)

		local strNum = 1
		local cNum = 1
		width = padW * 2

		local offX = 0

		for k, column in ipairs (self.Columns) do

			local maxCW = column.Width
--			Nx.prt ("colW %s %s", k, maxCW)

			for line = self.Top, last do

				if self.Offsets then
					offX = self.Offsets[line] or 0
				end

				maxCW = max (maxCW, self.Strs[strNum]:GetWidth() + offX)
--				Nx.prt ("maxCW %s %f", line, maxCW)
				strNum = strNum + 1
			end

			strNum = strNum + (self.Vis * cNum - strNum + 1)

			width = width + maxCW

			cNum = cNum + 1

			self.SSW = width
			self.SSH = height
		end

--		width = min (width, 30)
	end

--	prt ("List WH %d %d", width, height)

	width = max (self.MinW, width)
	height = max (self.MinH, height)

	f:SetWidth (width)
	f:SetHeight (height)

	local sfrm = self.SelFrm
	sfrm:SetWidth (width - 10)

	height = max (height - hdrH, 1)

	self.Vis = floor ((height - padH * 2) / lineH)
	self.Vis = max (self.Vis, 0)

--	Nx.prt ("List resize vis %s", self.Vis)

	-- Set columns

	local hf = self.HdrFrm
	if hf then
		hf:SetWidth (width)
		hf:SetHeight (hdrH)
	end

	local x = 0
	local clipW = width - padW * 2

	for k, column in ipairs (self.Columns) do

		local colW = min (column.Width, clipW)
		column.ClipW = colW

		local hfstr = column.FStr
		if hfstr then
			hfstr:SetPoint ("TOPLEFT", padW + x, 0)
			hfstr:SetWidth (colW)
		end

		x = x + column.Width
		clipW = clipW - column.Width
	end

	self:CreateStrings()
	self:CreateButtons()
end

---------------------------------------------------------------------------------------
-- Create any needed list strings
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:CreateStrings()

	local f = self.Frm
	local hdrH = self.HdrH
	local lineH = self:GetLineH()
	local width = f:GetWidth()
	local padW = 1
	local padH = 0

	local x = 0
	local strNum = 1

	for k, column in ipairs (self.Columns) do

		local colW = column.ClipW
		local offX = 0
		local offY = 0

		for n = 1, self.Vis do

			local fstr = self.Strs[strNum]

			if not fstr then
				fstr = f:CreateFontString()
				self.Strs[strNum] = fstr				
			end

			fstr:SetFontObject (column.FontObj)
			fstr:SetJustifyH (column.JustifyH)

			if self.Offsets then
				local line = self.Top + n - 1
				offX = self.Offsets[line] or 0
				offY = self.Offsets[-line] or 0
			end

			fstr:SetPoint ("TOPLEFT", padW + x + offX, -(n - 1) * lineH - hdrH - padH - offY)

			if not self.ShowAll then
				fstr:SetWidth (colW - offX)
			end

			fstr:SetHeight (lineH)
			fstr:Show()

			strNum = strNum + 1
		end

		x = x + column.Width
	end

	-- Hide extra strings

--	prt ("ListSetSize "..self.Vis.." "..#self.Strs)
				
	for n = strNum, #self.Strs do
		self.Strs[n]:Hide()		
	end

end

---------------------------------------------------------------------------------------
-- Create any needed list buttons
-- self = instance
---------------------------------------------------------------------------------------

function Nx.List:CreateButtons()

	local butNum = 1

	if self.ButData then

		local scale = self:GetLineH() / self.BaseLineH

--		Nx.prt ("list but scale %s", scale)

		local f = self.Frm
		local offX = 0
		local offY = 0

		for n = 1, self.Vis do

			local but = self.Buts[butNum]

			if not but then

--				Nx.prt ("List resize create but %s", butNum)

				but = Nx.Button:Create (f, nil, nil, nil, 0, 0, "CENTER", 14, 14, self.OnBut, self)
				self.Buts[butNum] = but

				but.Frm:SetFrameLevel (f:GetFrameLevel() + 1)
			end

			but.Frm:SetScale (scale)

			butNum = butNum + 1
		end
	end

	-- Hide extra buttons

	if self.Buts then
		for n = butNum, table.maxn (self.Buts) do
			if self.Buts[n] then
				self.Buts[n].Frm:Hide()
			end
		end
	end
end

function Nx.List:Update (showLast)

	if self.SortColumnId and not self.Sorted then
		self:Sort()
	end

	local lineH = self:GetLineH()
	local hdrH = self.HdrH

--	Nx.prt ("List lineH %s", lineH)

	if showLast then
		self:ShowLast()
	end

	if self.ShowAll then
		self:Resize (0, 0)
	end

	self.Top = min (self.Top, self.Num - self.Vis + 1)
	self.Top = max (self.Top, 1)

	self.Selected = min (self.Selected, self.Num)

	local last = self.Top + self.Vis - 1
	last = min (last, self.Num)

	if self.Offsets or #self.Strs < self.Vis then
		self:CreateStrings()
	end

	local strNum = 1
	local cNum = 1

	for k, column in ipairs (self.Columns) do

		for line = self.Top, last do

			local txt = column.Data[line]
			self.Strs[strNum]:SetText (txt)

			strNum = strNum + 1
		end

		-- Hide extras

		for n = strNum, self.Vis * cNum do
			self.Strs[n]:SetText ("")
			strNum = strNum + 1
		end

		cNum = cNum + 1
	end

	-- Do final resize and size parent win

	if self.ShowAll then

		self:Resize (0, 0)

		local f = self.Frm
		local win = f:GetParent().NxWin

		if win then
			win:SetSize (f:GetWidth(), -7, true)
		end
	end

	-- Slider

	if not self.ShowAll then
		self.Slider:Set (self.Top, 1, self.Num, self.Vis)
		self.Slider:Update()
	end

	-- Buttons

	if self.ButData then

		if not self.Buts or #self.Buts < self.Vis then
			self:CreateButtons()
		end

--		Nx.prt ("List Update Vis %s", self.Vis)

		local padW = 1
		local padH = 0

		local butNum = 1
		local f = self.Frm
		local offX = 0
		local offY = 0
		local adjY = hdrH + padH + lineH/2 + .5

		for n = 1, self.Vis do

			local line = self.Top + n - 1

			local but = self.Buts[butNum]
			local butType = self.ButData[line]

			if butType then

				if not but then
					Nx.prt (L["!BUT %s"], #self.Buts)
				end
				assert (but)

--				prt ("ListResize but #%d %s", n, butType)

				but:SetType (butType)
				but:SetId (line)

				local butTx = self.ButData[line + 1000000]

				if butType == "Color" then
					local t = self.ButData[line + 8000000]
					butTx = t[self.ButData[line + 9000000]]
				end

--				prtVar ("But Tex", butTx)
				but:SetTexture (butTx)

				local butTip = self.ButData[line + 2000000]
				but.Frm.NxTip = butTip
				but.Frm.NXTipFrm = self.ButData[line + 3000000]

				-- Causes update
				but:SetPressed (self.ButData[-line])

				if self.Offsets then
					offX = self.Offsets[line] or 0
					offY = self.Offsets[-line] or 0
				end

				local scale = self:GetLineH() / self.BaseLineH
				local y = (-(n - 1) * lineH - adjY - offY) / scale

				but.Frm:SetPoint ("CENTER", f, "TOPLEFT", (padW + lineH/2 + offX) / scale, y)
				but.Frm:Show()

			elseif but then

				but.Frm:Hide()
			end

			butNum = butNum + 1
		end

	elseif self.Buts then

		self:CreateButtons()		-- Hides all since we have no data
	end

	-- Frames

--[[ Example: (OLD)
	  text 1 frm 1 (color)
	--------- Visible
	1 text 2
	2 text 3 frm 2 (color). Get frm to keep until scrolls off visible area of list
	3 text 4
	4 text 5 frm 3 (color). Get frm
	---------
	  text 6
--]]

	if self.FrmData then			-- Only used for Info and Watch Items

		Nx.List:FreeFrames (self)

		local lfrm = self.Frm
		local offX = 3
		local offY = 3
		local adjY = hdrH + .5
		local doBind = true

		for n = 1, self.Vis do

			local line = self.Top + n - 1
			local data = self.FrmData[line]

			if data then

				local typ, v1, v2, v3 = Nx.Split ("~", data)

--				Nx.prt ("%s", -(n - 1) * lineH - adjY - offY)

				if typ == "Info" then

					if self.UserFunc then
						self.UserFunc (self.User, "update", v1, -(n - 1) * lineH - adjY)
					end

				elseif typ == "WatchItem" then
					if InCombatLockdown() then
						return
					end
					local f = Nx.List:GetFrame (self, typ)
					f:ClearAllPoints()

					local scale = self.ItemFrameScale * .07 * lineH / 13

					f:SetPoint ("TOPRIGHT", lfrm, "TOPLEFT", offX, -(n - 1) * lineH / scale - adjY - offY)

					f["rangeTimer"] = -1

					f:SetScale (scale)
					f:SetWidth (29)
					f:SetHeight (30)
					f:SetAlpha (self.ItemFrameAlpha)

					local id = tonumber (v1)					
					f:SetID (id)

					SetItemButtonTexture (f, v2);
					SetItemButtonCount (f, tonumber (v3));
					f["charges"] = tonumber (v3);

					f["questLogIndex"] = id

					local start, duration, enable = GetQuestLogSpecialItemCooldown (id)
					if start then
						CooldownFrame_Set (f.Cooldown, start, duration, enable)
						if ( duration > 0 and enable == 0 ) then
							SetItemButtonTextureVertexColor (itemButton, 0.4, 0.4, 0.4)
						else
							SetItemButtonTextureVertexColor (itemButton, 1, 1, 1)
						end
 					end

					local link, item, charges, showItemWhenComplete = GetQuestLogSpecialItemInfo (id)
					f:SetAttribute ("item", link)

					if doBind then
						doBind = false
						local key = GetBindingKey ("NxWATCHUSEITEM")
						if key then
							Nx.qdb.profile.QuestWatch.KeyUseItem = key
							Nx.prt (L["Key %s transfered to Watch List Item"], key)
						end

						if #Nx.qdb.profile.QuestWatch.KeyUseItem > 0 and not InCombatLockdown() then

							local s = GetBindingAction (Nx.qdb.profile.QuestWatch.KeyUseItem)
							s = strmatch (s, L["CLICK (.+):"])
--							Nx.prt ("Key's frm %s", s or "nil")
							if s ~= f:GetName() then
								local ok = SetBindingClick (Nx.qdb.profile.QuestWatch.KeyUseItem, f:GetName())
								Nx.prt (L["Key %s %s #%s %s"], Nx.qdb.profile.QuestWatch.KeyUseItem, f:GetName(), line, ok or "nil")
								Nx.qdb.profile.QuestWatch.KeyUseItem = ""
							end
						end
					end

					f:Show()

				end
			end
		end

	end

	-- Position selected line

	local sfrm = self.SelFrm
	local selY = self.Selected - self.Top

	if selY < 0 or selY >= self.Vis then
		sfrm:Hide()

	else
		sfrm:SetHeight (lineH + 1)
		sfrm:SetPoint ("TOPLEFT", 0, -selY * lineH - self.HdrH)
		sfrm:Show()
	end
end

---------------------------------------------------------------------------------------

function Nx.List:SaveColumns()

	if self.Save then

		local str = ""
		local sep = ""

		for id, column in ipairs (self.Columns) do
			str = str .. sep .. column.Width
			sep = "^"
		end

		self.Save["ColW"] = str
	end
end

function Nx.List:ColumnAdd (name, columnId, width, justifyH, font)

	local colId = columnId or 1
	local w = width or 9999

	if self.SaveColumnWidths then
		w = tonumber (self.SaveColumnWidths[colId]) or w
	end

	local column = {}
	column.Name = name
--	column.Max = 0
	column.Width = w
	column.FontObj = Nx.Font:GetObj (font or self.Font)
	column.JustifyH = justifyH or "LEFT"
	column.Data = {}

	if self.HdrFrm then

		local fstr = self.HdrFrm:CreateFontString()
		column.FStr = fstr

		fstr:SetFontObject (self.FontObj)
		fstr:SetJustifyH (column.JustifyH)
		fstr:SetPoint ("TOPLEFT", 0, 0)

		if w >= 0 then
			fstr:SetWidth (w)
		end

		fstr:SetHeight (self.HdrH)

		fstr:SetText (name)
		fstr:SetTextColor (.8, .8, 1, 1)
		fstr:Show()
	end

	self.Columns[colId] = column

	self.SSW = nil			-- Cause resize
end

function Nx.List:ColumnSetWidth (columnId, width)
	local column = self.Columns[columnId]
	column.Width = width
end

function Nx.List:ColumnGetWidth (columnId)
	return self.Columns[columnId].Width
end

function Nx.List:ColumnSetName (columnId, name)

	local colId = columnId or 1
	local column = self.Columns[colId]

	column.Name = name

	local fstr = column.FStr

	if fstr then

		if self.SortColumnId == columnId then
			name = ">" .. name
		end

		fstr:SetText (name)
		fstr:SetTextColor (.8, .8, 1, 1)

		self.SSW = nil		-- Cause resize
	end
end

function Nx.List:ColumnHitTest (x)

	local colX = 0

	for id, column in ipairs (self.Columns) do

		if x >= colX and x < colX + column.Width then
			return id, column
		end

		colX = colX + column.Width
	end
end

---------------------------------------------------------------------------------------
-- Empty the list
---------------------------------------------------------------------------------------

function Nx.List:Empty()

	self.Num = 0
	self.Data = wipe (self.Data or {})

	for k, column in pairs (self.Columns) do
		column.Data = column.Data and wipe (column.Data)
	end

	if self.ButData then
		wipe (self.ButData)
	end

	if self.Offsets then
		wipe (self.Offsets)
	end

	if self.FrmData then
		wipe (self.FrmData)
	end

--	if self.FrmDataFrm then
--		wipe (self.FrmDataFrm)
--	end

	Nx.List:FreeFrames (self)

	self.Sorted = false
end

---------------------------------------------------------------------------------------

function Nx.List:ColumnSort (columnId)

	self.Sorted = false

	if self.SortColumnId == columnId then
		self.SortColumnId = nil
	else
		self.SortColumnId = columnId
	end

	for id, column in pairs (self.Columns) do
		self:ColumnSetName (id, column.Name)
	end
end

---------------------------------------------------------------------------------------
-- Sort list by a columns contents
---------------------------------------------------------------------------------------

function Nx.List:Sort()

	local column = self.Columns[self.SortColumnId]
	local cData = column.Data
	if not cData then		-- or #cData < self.Num then
		return
	end

	self.Sorted = true

	local cNameData = self.Columns[2].Data

	local t = {}
	for n = 1, self.Num do
		local name = gsub (cNameData[n], "|cff......", "")
		t[n] = gsub (cData[n] or "", "|cff......", "") .. " " .. strsub (name, 1, 1) .. "~" .. n
	end

	sort (t)

	-- Convert sorted to number

	for n = 1, #t do
		local _, i = Nx.Split ("~", t[n])
		t[n] = tonumber (i)
	end

	-- Reorder tables

	local data = {}

	for n = 1, #t do
		data[n] = self.Data[t[n]]
	end

	self.Data = data

	for k, column in pairs (self.Columns) do

		if column.Data then
			local data = {}

			for n = 1, #t do
				data[n] = column.Data[t[n]]
			end

			column.Data = data
		end
	end

	if self.ButData then
		local data = {}

		for n = 1, #t do
			local i = t[n]
			data[n] = self.ButData[i]
			data[-n] = self.ButData[-i]
			data[n + 1000000] = self.ButData[i + 1000000]
			data[n + 2000000] = self.ButData[i + 2000000]
			data[n + 3000000] = self.ButData[i + 3000000]
			data[n + 8000000] = self.ButData[i + 8000000]
			data[n + 9000000] = self.ButData[i + 9000000]
		end

		self.ButData = data
	end

	if self.Offsets then
		local data = {}

		for n = 1, #t do
			data[n] = self.Offsets[t[n]]
		end

		self.Offsets = data
	end
end

---------------------------------------------------------------------------------------

function Nx.List:ItemAdd (userData)

	self.Num = self.Num + 1
	self.Data[self.Num] = userData
end

function Nx.List:ItemGetNum()

	return self.Num
end

function Nx.List:ItemSet (columnId, str, index)

	local i = index or self.Num

	local column = self.Columns[columnId]

	column.Data[i] = str
end

--[[
function Nx.List:ItemGetButtonPressed (index)

	if self.ButData then

		local but = self.Buts[index]

		if but then
			return but:GetPressed()
		end
	end

	return false
end
--]]

function Nx.List:ItemSetButton (typ, pressed, tex, tip, func)

	if not self.ButData then
		self.ButData = {}
	end

	local index = self.Num

	self.ButData[index] = typ
	self.ButData[-index] = pressed

	if tex then
		self.ButData[index + 1000000] = tex
	end
	if tip then
		self.ButData[index + 2000000] = tip
	end
	if func then
		self.ButData[index + 5000000] = func
	end
end

function Nx.List:ItemGetButtonTip (index)

	if self.ButData then
		return self.ButData[index + 2000000]
	end
end

function Nx.List:ItemSetButtonTip (tip, index, frm)

	if self.ButData then

		index = index or self.Num
		self.ButData[index + 2000000] = tip
		if frm then
			self.ButData[index + 3000000] = frm
		end
	end
end

function Nx.List:ItemSetFunc(func, index)
	if self.ButData then
		index = index or self.Num
		self.ButData[index + 5000000] = func
	end
end

function Nx.List:ItemGetFunc (index)
	if self.ButData then
		return self.ButData[index + 5000000]
	end
end

---------------------------------------------------------------------------------------
-- Set a solid colored button used to pick colors
-- Table[key] is a "rrggbbaa" hex number
---------------------------------------------------------------------------------------

function Nx.List:ItemSetColorButton (table, key, hasAlpha)

	if not self.ButData then
		self.ButData = {}
	end

	local index = self.Num

	self.ButData[index] = "Color"
	self.ButData[index + 8000000] = table
	self.ButData[index + 9000000] = key

	if not hasAlpha then
		self.ButData[index + 10000000] = true
	end
end

function Nx.List:ItemSetFrame (typ)

	if not self.FrmData then
		self.FrmData = {}
--		self.FrmDataFrm = {}
	end

	self.FrmData[self.Num] = typ
end

function Nx.List:ItemSetOffset (offX, offY)

	if not self.Offsets then
		self.Offsets = {}
	end

	self.Offsets[self.Num] = offX
	self.Offsets[-self.Num] = offY
end

function Nx.List:ItemGetData (index)

	index = index or self.Selected
	return index and self.Data[index]
end

function Nx.List:ItemSetData (index, data)

	self.Data[index] = data
end

---------------------------------------------------------------------------------------
-- Get extended data
-- (num >= 1)
---------------------------------------------------------------------------------------

function Nx.List:ItemGetDataEx (index, num)

	index = index or self.Selected
	return index and self.Data[index + num * 10000000]
end

---------------------------------------------------------------------------------------
-- Set extended data
---------------------------------------------------------------------------------------

function Nx.List:ItemSetDataEx (index, data, num)

	self.Data[(index or self.Num) + num * 10000000] = data
end

---------------------------------------------------------------------------------------
-- Select list line
-- (index or 0)
---------------------------------------------------------------------------------------

function Nx.List:Select (index)

	assert (index >= 0 and index <= self.Num)

	self.Selected = index

	if index < self.Top then
		self.Top = max (index, 1)

	elseif index >= self.Top + self.Vis then
		self.Top = max (index - self.Vis + 1, 1)
	end
end

---------------------------------------------------------------------------------------
-- Get selected list line
---------------------------------------------------------------------------------------

function Nx.List:GetSelected()

	return self.Selected
end

---------------------------------------------------------------------------------------
-- Send select to user
---------------------------------------------------------------------------------------

function Nx.List:SendUserSelect()

	if self.UserFunc then
		self.UserFunc (self.User, "select", self.Selected, 0)
	end
end

---------------------------------------------------------------------------------------
-- Show last item at bottom of list view
---------------------------------------------------------------------------------------

function Nx.List:ShowLast()

	self.Top = self.Num - self.Vis + 1
	self.Top = max (self.Top, 1)
end

---------------------------------------------------------------------------------------
-- Is the last item shown?
---------------------------------------------------------------------------------------

function Nx.List:IsShowLast()

	local top = self.Num - self.Vis + 1
	top = max (top, 1)
	return self.Top == top
end

function Nx.List:OnHdrMouseDown (click)

	local this = self			-- V4

	local x = Nx.Util_IsMouseOver (this)

	if x then

		local self = this.NxInst

		local id, column = self:ColumnHitTest (x)
		if id then
--			Nx.prt ("OnHdrMouseDown %s", id)

			if IsShiftKeyDown() then

				local add = click == "LeftButton" and 10 or -10
				column.Width = max (column.Width + add, 10)
				self:SaveColumns()
				self:FullUpdate()
			else

				if click == "LeftButton" then

					if id and self.UserFunc then
						self.UserFunc (self.User, "sort", 0, id)
					end
				else
					Nx.prt (L["shift left/right click to change size"])
				end
			end
		end
	end
end

Nx.List.ClickToName = {
	["LeftButton"] = "select",
	["MiddleButton"] = "mid",
	["RightButton"] = "menu",
	["Button4"] = "back",
}

function Nx.List:OnMouseDown (click)

--	prt ("List MouseDown "..click)

	local this = self			-- V4

	local inst = this.NxInst
	local x, y = Nx.Util_IsMouseOver (this)

	if x then

		y = this:GetHeight() - y

		if y >= inst.HdrH then

			y = floor ((y - inst.HdrH) / inst:GetLineH())
			inst.Selected = min (y + inst.Top, inst.Num)

			local id = inst:ColumnHitTest (x)

			if id and inst.UserFunc then				
				inst.UserFunc (inst.User, Nx.List.ClickToName[click], inst.Selected, id)
			end

			inst:Update()
		end
	end
end

function Nx.List:OnMouseWheel (value)

--	prt ("List MouseWheel "..tostring (value))

	if IsShiftKeyDown() then
		value = value * 5

		if IsControlKeyDown() then
			value = value * 20
		end
	end

	local this = self			-- V4

	local inst = this.NxInst
	inst.Top = inst.Top - value
	inst:Update()
end

function Nx.List:OnBut (but, id, click)

	if self.ButData[id] == "Color" then
		self:OpenColorDialog (id)
		return
	end

	self.ButData[-id] = but:GetPressed()

--	prt ("List but %d %s", id, tostring (self.ButData[-id]))	
	if self.UserFunc then
		self.UserFunc (self.User, "button", id, self.ButData[-id], click, but)
	end
end

function Nx.List:OpenColorDialog (id)

	local f = ColorPickerFrame

	f:SetMovable (true)

	self.ColorId = id
	f.NXList = self
	f.NXTbl = self.ButData[id + 8000000]
	f.NXVName = self.ButData[id + 9000000]

	local hasAlpha = not self.ButData[id + 10000000]

	f["func"] = function()
			local f = ColorPickerFrame
			local r, g, b = f:GetColorRGB()
			local a = f["hasOpacity"] and (1 - OpacitySliderFrame:GetValue()) or 1
--			Nx.prt ("%s %s %s %s", r, g, b, a)
			f.NXTbl[f.NXVName] = floor (r * 255) * 0x1000000 + floor (g * 255) * 0x10000 + floor (b * 255) * 0x100 + floor (a * 255)

			local self = f.NXList

			self:Update()

			if self.UserFunc then
				self.UserFunc (self.User, "color", self.ColorId)
			end
		end

	f["hasOpacity"] = hasAlpha
	f["opacityFunc"] = f["func"]
	f["cancelFunc"] = function (old)
			f.NXTbl[f.NXVName] = old
			local self = f.NXList
			self:Update()
			if self.UserFunc then
				self.UserFunc (self.User, "color", self.ColorId)
			end
		end

	local col = f.NXTbl[f.NXVName]
	f["previousValues"] = col
	local r, g, b, a = Nx.Util_str2rgba (col)

	f:SetColorRGB (r, g, b)
	f["opacity"] = 1 - a

	ShowUIPanel (f)
end

function Nx.List:OnSlider (slider, pos)

	self.Top = floor (pos)
	self:Update()
end

---------------------------------------------------------------------------------------
-- Drop down
---------------------------------------------------------------------------------------

function Nx.DropDown:Init()

	-- Create Window

	local win = Nx.Window:Create ("NxDD", nil, nil, nil, 0, true, true, true)
	self.Win = win
	local frm = win.Frm

	win:EnableMenu (false)

	win:InitLayoutData (nil, 0, 0, 200, 200)

	tinsert (HideFramesOnEsc, frm:GetName())

	frm:SetClampedToScreen (true)
	frm:SetToplevel (true)
--	self.Win:Show (false)

	-- List

	Nx.List:SetCreateFont ("Font.Medium")

	local list = Nx.List:Create (false, 0, 0, 1, 1, frm, false, true)
	self.List = list

	list:SetUser (self, self.OnListEvent)

	list:ColumnAdd ("", 1)
--	list:SetLineHeight (0, 0)

	win:Attach (list.Frm, 0, 1, 0, 1)
end

function Nx.DropDown:Start (user, func)

	self.User = user
	self.Func = func

	local list = self.List
	list:Empty()
end

function Nx.DropDown:Add (name, select)

--	Nx.prt ("DropDown Add %s", name)

	local list = self.List
	list:ItemAdd (name)
	list:ItemSet (1, name)

	if select then
		list:Select (list:ItemGetNum())
	end
end

function Nx.DropDown:AddT (data, selectI)

	for n, name in ipairs (data) do
		self:Add (name, n == selectI)
	end
end

function Nx.DropDown:Show (parent, x, y)

	local uiparent = UIParent
	if not x then
		x, y = GetCursorPosition()
		x = x / uiparent:GetEffectiveScale() - 80
		y = y / uiparent:GetEffectiveScale() - GetScreenHeight() + 10
	end

--	Nx.prt ("DropDown Show %s %s", x, y)

	local win = self.Win
	local f = win.Frm
	local list = self.List

	win:SetFrmStrata (4)

	f:SetParent (parent)

	f:SetPoint ("TOPLEFT", uiparent, "TOPLEFT", x, y)
	win:Show()

	list:FullUpdate()
end

---------------------------------------------------------------------------------------
-- On list control updates
---------------------------------------------------------------------------------------

function Nx.DropDown:OnListEvent (eventName, sel, val2, click)

	local name = self.List:ItemGetData (sel)
	if name then

--		Nx.prt ("DropDown %s, %s", eventName, name)

		if eventName == "select" or eventName == "mid" then

			self.Func (self.User, name, sel)
		end
	end

	self.Win:Show (false)
end

---------------------------------------------------------------------------------------
-- Tab Bar
---------------------------------------------------------------------------------------

function Nx.TabBar:GetHeight()
	return 22
end

---------------------------------------------------------------------------------------
-- Create a Tab Bar
-- ()
-- ret: tab bar table
---------------------------------------------------------------------------------------

function Nx.TabBar:Create (name, parentFrm, width, height)

	local c2rgba = Nx.Util_str2rgba

	parentFrm = parentFrm or UIParent

	-- New

	local bar = {}

	setmetatable (bar, self)
	self.__index = self

	bar.Name = name
	bar.Tabs = {}

	-- Create window frame

	local f = CreateFrame ("Frame", name, parentFrm)
	bar.Frm = f
	f.NxInst = bar

--	f:SetMinResize (100, 40)

	f:SetWidth (width)
	f:SetHeight (height)

	f:SetPoint ("TOPLEFT", 100, -100)
--	f:SetFrameStrata ("MEDIUM")

--	f:SetScript ("OnEvent", self.OnEvent)
--	f:RegisterEvent ("PLAYER_LOGIN")

--	f:SetScript ("OnUpdate", self.OnUpdate)

	local t = f:CreateTexture()
	t:SetColorTexture (c2rgba ("0|0|0|.5"))
	t:SetAllPoints (f)
	f.texture = t

	f:Show()

	-- Create GUI elements

	bar:CreateBorders()

	return bar
end

---------------------------------------------------------------------------------------
-- Create border frames
---------------------------------------------------------------------------------------

function Nx.TabBar:CreateBorders()

	local c2rgba = Nx.Util_str2rgba

	local f = CreateFrame ("Frame", nil, self.Frm)

	self.TopFrm = f

	f:SetPoint ("TOPLEFT", 0, 0)
	f:SetPoint ("TOPRIGHT", 0, 0)

--	f:SetWidth (1)
	f:SetHeight (4)

	local t = f:CreateTexture()
	t:SetColorTexture (c2rgba (".313|.313|.313|1"))
	t:SetAllPoints (f)
	f.texture = t

	f:Show()

end

---------------------------------------------------------------------------------------
-- Set user for notify
---------------------------------------------------------------------------------------

function Nx.TabBar:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Add a tab to bar
---------------------------------------------------------------------------------------

function Nx.TabBar:AddTab (name, index, width, press, template, butId)

	local tab = {}
	self.Tabs[index] = tab

	tab.Name = name

	-- (parentFrm, typ, text, tip, bx, by, side, width, height, func, user)

	local w = width or 66
	local x = 1 + (index - 1) * (w + 2)

	tab.W = w

	local but = Nx.Button:Create (self.TopFrm, "Tab", name, nil, x, -1, "TOPLEFT", w, 20, self.OnBut, self, template)
	tab.But = but

	if butId then
		but.Frm:SetID (butId)
	end

	but:SetId (index)

	if press then

		but:SetPressed (true)

		local txt = "|cffffffff" .. name
		but:SetText (txt, 0, 2)
	end
end

---------------------------------------------------------------------------------------
-- Select tab button
---------------------------------------------------------------------------------------

function Nx.TabBar:Select (index, force)

	local selTab = self.Tabs[index]
	if not selTab then
		return
	end

	local but = selTab.But
	if not force and but:GetPressed() then
		return
	end

	local x = 1

	for i, tab in pairs (self.Tabs) do
		if i ~= index then
			tab.But:SetPressed (false)
			tab.But:SetText (tab.Name, 0, 0)
		end

		tab.But:SetPos ("TOPLEFT", x, -1)
		tab.But:SetSize (tab.W, 20)

		x = x + tab.W + 2
	end

	but:SetPressed (true)

	local txt = "|cffffffff" .. selTab.Name
	but:SetText (txt, 0, 2)

	if self.UserFunc then
		self.UserFunc (self.User, index)
	end
end

---------------------------------------------------------------------------------------
-- Enable tab button
---------------------------------------------------------------------------------------

function Nx.TabBar:Enable (index, enable)

	local tab = self.Tabs[index]
	tab.But.Frm:EnableMouse (enable ~= false)
end

---------------------------------------------------------------------------------------
-- Set our fade
---------------------------------------------------------------------------------------

function Nx.TabBar:SetFade (fade)

	local f = self.Frm
	f.texture:SetVertexColor (1, 1, 1, fade * .5)

	local tf = self.TopFrm
	tf.texture:SetVertexColor (1, 1, 1, fade)

	for i, tab in pairs (self.Tabs) do
		local f = tab.But.Frm
		f.texture:SetVertexColor (1, 1, 1, fade)
	end
end

---------------------------------------------------------------------------------------
-- Handle tab button press
---------------------------------------------------------------------------------------

function Nx.TabBar:OnBut (but, id, click)

--	Nx.prt ("TabBar but %d %s", id, click)

	if not but:GetPressed() then
		-- Keep it pressed
		but:SetPressed (true)
		return
	end

--	if but.Frm:GetID() ~= 0 then
--		Nx.prt ("TabBar but id %s", but.Frm:GetID())
--		return
--	end

	self:Select (id, true)
end

---------------------------------------------------------------------------------------
-- Tool Bar
---------------------------------------------------------------------------------------

function Nx.ToolBar:Init()

	local data = Nx:GetDataToolBar()

	if not data.Version or data.Version < Nx.VERSIONTOOLBAR then

		if data.Version then
			Nx.prt (L["Reset old tool bar data"])
		end

		data.Version = Nx.VERSIONTOOLBAR

		for k, bar in pairs (data) do
			if type (bar) == "table" then
--				Nx.prt (" Reset %s", k)
				data[k] = nil
			end
		end
	end

	self.TBs = {}		-- All created tool bars

	self.BORDERW = 5
	self.BORDERH = 5

	self.Borders = {
		"TOPLEFT", "TOPRIGHT", 1, self.BORDERH, "WinBrH",
		"BOTTOMLEFT", "BOTTOMRIGHT", 1, self.BORDERH, "WinBrH",
		"TOPLEFT", "BOTTOMLEFT", self.BORDERW, 1, "WinBrV",
		"TOPRIGHT", "BOTTOMRIGHT", self.BORDERW, 1, "WinBrV",
	}

	local menu = Nx.Menu:Create (UIParent)
	self.Menu = menu

--	self.MenuIUp = menu:AddItem (0, "Move Up", self.Menu_OnUp, self)
--	self.MenuIDn = menu:AddItem (0, "Move Down", self.Menu_OnDn, self)
--	self.MenuIRemove = menu:AddItem (0, "Remove", self.Menu_OnRemove, self)

	self.MenuISize = menu:AddItem (0, "Size", self.Menu_OnSize, self)
	self.MenuISize:SetSlider (8, 8, 32)
	self.MenuISpace = menu:AddItem (0, "Spacing", self.Menu_OnSpace, self)
	self.MenuISpace:SetSlider (0, 0, 15)
	self.MenuIAlignR = menu:AddItem (0, "Align Right", self.Menu_OnAlignR, self)
	self.MenuIAlignR:SetChecked (true)
	self.MenuIAlignB = menu:AddItem (0, "Align Bottom", self.Menu_OnAlignB, self)
	self.MenuIAlignB:SetChecked (true)
	self.MenuIVert = menu:AddItem (0, "Vertical", self.Menu_OnVertical, self)
	self.MenuIVert:SetChecked (true)
end

--function Nx.ToolBar:Menu_OnUp (item)
--end

--function Nx.ToolBar:Menu_OnDn (item)
--end

--function Nx.ToolBar:Menu_OnRemove (item)
--end

function Nx.ToolBar:Menu_OnSize (item)

	self:MenuDoUpdate ("Size", item:GetSlider())
end

function Nx.ToolBar:Menu_OnSpace (item)

	self:MenuDoUpdate ("Space", item:GetSlider())
end

function Nx.ToolBar:Menu_OnAlignR (item)

	self:MenuDoUpdate ("AlignR", item:GetChecked())
end

function Nx.ToolBar:Menu_OnAlignB (item)

	self:MenuDoUpdate ("AlignB", item:GetChecked())
end

function Nx.ToolBar:Menu_OnVertical (item)

	self:MenuDoUpdate ("Vert", item:GetChecked())
end

function Nx.ToolBar:MenuDoUpdate (varName, value)

	local bar = self.Active
	local data = Nx:GetDataToolBar()
	local svdata = data[bar.Name]
	svdata[varName] = value

	bar:Update()
end

function Nx.ToolBar:OpenMenu (bar)

	local data = Nx:GetDataToolBar()
	local svdata = data[bar.Name]

	self.MenuISize:SetSlider (svdata["Size"])
	self.MenuISpace:SetSlider (svdata["Space"] or 3)
	self.MenuIAlignR:SetChecked (svdata["AlignR"])
	self.MenuIAlignB:SetChecked (svdata["AlignB"])
	self.MenuIVert:SetChecked (svdata["Vert"])

	self.Active = bar
	self.Menu:Open()
end

--function Nx.ToolBar:GetHeight()
--	return 22
--end

---------------------------------------------------------------------------------------
-- Create a Tool Bar
-- ()
-- ret: tool bar table
---------------------------------------------------------------------------------------

function Nx.ToolBar:Create (name, parentFrm, size, alignR, alignB)

	local c2rgba = Nx.Util_str2rgba

	parentFrm = parentFrm or UIParent

	local data = Nx:GetDataToolBar()
	local svdata = data[name]

--	svdata = nil

	if not svdata then		-- No data for our name?

		svdata = {}				-- New
		data[name] = svdata

		svdata["Size"] = size
		svdata["Space"] = 1
		svdata["AlignR"] = alignR
		svdata["AlignB"] = alignB
--		svdata["Vert"] = nil
	end

	-- New

	local bar = {}

	setmetatable (bar, self)
	self.__index = self

	assert (self.TBs[bar] == nil)
	self.TBs[bar] = true

	bar.Name = name
	bar.Tools = {}

	bar.Size = size		-- Default

	-- Create window frame
	local f = nil
	local kids = { parentFrm:GetChildren() };
	for _, child in ipairs(kids) do
		if (child:GetName() == name) then
		  f = child
		end
	end
	if f == nil then
		f = CreateFrame ("Frame", name, parentFrm)
	end
	bar.Frm = f
	f.NxInst = bar

--	f:SetMinResize (100, 40)

	f:SetWidth (size)
	f:SetHeight (10)

	f:SetPoint ("TOPRIGHT", 0, 0)

--	f:SetScript ("OnEvent", self.OnEvent)
--	f:RegisterEvent ("PLAYER_LOGIN")

--	f:SetScript ("OnUpdate", self.OnUpdate)

--	local t = f:CreateTexture()
--	t:SetTexture (c2rgba ("00000080"))
--	t:SetAllPoints (f)
--	f.texture = t

	f:Show()

	--

	return bar
end

---------------------------------------------------------------------------------------
-- Set user for notify
---------------------------------------------------------------------------------------

function Nx.ToolBar:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
-- Add a button to bar
---------------------------------------------------------------------------------------

function Nx.ToolBar:AddCustomButton (typ, name, index, func, press)
	tinsert (Nx.BarData,{typ, name, func, press})
	local map = Nx.Map:GetMap (1)
	map:CreateToolBar()
end

function Nx.ToolBar:AddButton (typ, name, index, func, press)

	local tool = {}
	tinsert (self.Tools, tool)

	tool.Name = name
	tool.Func = func

	-- (parentFrm, typ, text, tip, bx, by, side, width, height, func, user)

	local but = Nx.Button:Create (self.Frm, typ, nil, name, 0, 0, "TOPLEFT", 1, 1, self.OnBut, self)
	tool.But = but

	but:SetId (func)

	but:SetPressed (press)
end

---------------------------------------------------------------------------------------
-- Handle toolbar button press
---------------------------------------------------------------------------------------

function Nx.ToolBar:OnBut (but, id, click, x, y)

--	prt ("ToolBar but %d %s", id, click)

	if click == "RightButton" then

		Nx.ToolBar:OpenMenu (self)

	else
		local func = id

		if func then
			func (self.User, but, click, x, y)
		end
	end
end

---------------------------------------------------------------------------------------
-- Update toolbar
---------------------------------------------------------------------------------------

function Nx.ToolBar:Update()

	local data = Nx:GetDataToolBar()
	local svdata = data[self.Name]

	local f = self.Frm

--	Nx.prtVar ("AlignR", svdata["AlignR"])

	f:ClearAllPoints()

	local align = "TOPRIGHT"

	if not svdata["AlignR"] then

		align = "TOPLEFT"

		if svdata["AlignB"] then
			align = "BOTTOMLEFT"
		end
	else
		if svdata["AlignB"] then
			align = "BOTTOMRIGHT"
		end
	end

	f:SetPoint (align, 0, 0)

	local scale = svdata["Size"] / self.Size
	local space = (svdata["Space"] or 0) / scale
	local step = self.Size + space

	local xstep = step
	local ystep = 0

	if svdata["Vert"] then
		xstep = 0
		ystep = step
	end

	local xoff = 0
	local yoff = 0

	for n, tool in ipairs (self.Tools) do

		local but = tool.But
		if but then
			but:SetPos ("TOPLEFT", xoff, -yoff)
		end

		xoff = xoff + xstep
		yoff = yoff + ystep
	end

	if not svdata["Vert"] then
		xoff = xoff - space
	else
		yoff = yoff - space
	end

--	Nx.prt ("scale %f, w %f, xoff %f", scale, self.Size, xoff)

	f:SetWidth (max (xoff, self.Size))
	f:SetHeight (max (yoff, self.Size))

	f:SetScale (scale)
end

---------------------------------------------------------------------------------------
-- Set fade level of toolbar and tools
-- (0-1)
---------------------------------------------------------------------------------------

function Nx.ToolBar:SetFade (fade)

--	Nx.prtVar ("TB Fade", fade)

	self.Frm:SetAlpha (fade)
end

---------------------------------------------------------------------------------------
-- Set level of toolbar and tools
---------------------------------------------------------------------------------------

function Nx.ToolBar:SetLevels (level)

	self.Frm:SetFrameLevel (level)

	for n, tool in ipairs (self.Tools) do

		local but = tool.But
		if but then
			but.Frm:SetFrameLevel (level + 1)
		end
	end
end

---------------------------------------------------------------------------------------
-- Sliders
---------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------
-- Create
-- (parent, "H" or "V", bar size, top or left offset of bar)
---------------------------------------------------------------------------------------

function Nx.Slider:Create (parentFrm, typ, size, tlOff)

--	if 1 then return end

	local inst = {}		-- New instance

	setmetatable (inst, self)
	self.__index = self

	local w = size
	local h = size

	inst.TypeH = typ == "H"

	if inst.TypeH then
		w = 10
	else
		h = 10
	end

	local frm = CreateFrame ("Frame", nil, parentFrm)
	inst.Frm = frm
	frm.NxInst = inst

	frm:SetScript ("OnUpdate", self.OnUpdate)

	frm:SetScript ("OnMouseDown", self.OnMouseDown)
	frm:SetScript ("OnMouseUp", self.OnMouseUp)
	frm:EnableMouse (true)

	frm:SetWidth (w)
	frm:SetHeight (h)

	frm.texture = frm:CreateTexture()
	frm.texture:SetAllPoints (frm)
	frm.texture:SetColorTexture (.3, .3, .4, .6)

	frm:SetPoint ("TOPRIGHT", parentFrm, "TOPRIGHT", 0, -tlOff)
	frm:SetPoint ("BOTTOMRIGHT", parentFrm, "BOTTOMRIGHT", 0, 0)

	frm:Show()

	-- Thumb

	local tfrm = CreateFrame ("Frame", nil, frm)
	inst.ThumbFrm = tfrm

	tfrm:SetWidth (w)
	tfrm:SetHeight (h)

	tfrm.texture = tfrm:CreateTexture()
	tfrm.texture:SetAllPoints (tfrm)
	tfrm.texture:SetColorTexture (.3, .3, .7, .9)

	tfrm:SetPoint ("TOPLEFT", 1, 1)
	tfrm:Show()

	--

	inst:Set (0, 0, 9, 1)
	inst:Update()

	return inst
end

---------------------------------------------------------------------------------------
-- Set user of the slider for callback function
-- (user table, function)
---------------------------------------------------------------------------------------

function Nx.Slider:SetUser (user, func)

	self.User = user
	self.UserFunc = func
end

---------------------------------------------------------------------------------------
--
---------------------------------------------------------------------------------------

function Nx.Slider:SetTLOff (tlOff)

	local par = self.Frm:GetParent()
	self.Frm:SetPoint ("TOPRIGHT", par, "TOPRIGHT", 0, -tlOff)
end

---------------------------------------------------------------------------------------
-- Get slider position
-- self = instance
---------------------------------------------------------------------------------------

function Nx.Slider:Get()
	return self.Pos
end

---------------------------------------------------------------------------------------
-- Set slider position and optionally min and max values
-- self = instance
---------------------------------------------------------------------------------------

function Nx.Slider:Set (pos, min, max, visSize)

	if min then
		self.Min = math.min (min, max)
		self.Max = math.max (min, max)
	end

	if visSize then
		self.VisSz = math.max (visSize, 1)
	end

	-- Clamp pos
	pos = math.max (pos, self.Min)
	pos = math.min (pos, self.Max - self.VisSz + 1)
	self.Pos = pos

--	Nx.prt ("Slider Set %f (%f %f) %f", pos, self.Min, self.Max, self.VisSz)

end

function Nx.Util_c2rgb (colors)

	local r = tonumber (strsub (colors, 1, 2), 16) / 255
	local g = tonumber (strsub (colors, 3, 4), 16) / 255
	local b = tonumber (strsub (colors, 5, 6), 16) / 255
	return r, g, b
end

function Nx.Slider:OnMouseDown (button)

	local this = self			-- V4
	local self = this.NxInst

	if button == "LeftButton" then

		local frm = self.Frm

		local x, y = Nx.Util_IsMouseOver (frm)

		if x and x >= 0 then

			local tfrm = self.ThumbFrm
			local tx, ty = Nx.Util_IsMouseOver (tfrm)

			if self.TypeH then

				local w = (frm:GetRight() or 0) - (frm:GetLeft() or 0)

				x = (x - 1) / (frm:GetWidth() - 2) * (self.Max - self.Min) + self.Min
				self:Set (x)

			else

				if tx then

					self.DragX = x
					self.DragY = y
					self.DragPos = self.Pos

				else

					local h = (frm:GetTop() or 0) - (frm:GetBottom() or 0)
					y = h - y

--					Nx.prt ("Slider XY %f %f", x, y)

					local pos = self.Pos

					if y < -self.TPt then
						pos = pos - self.VisSz
					else
						pos = pos + self.VisSz
					end

					self:Set (pos)
				end
			end

			self:Update()

			if self.UserFunc then
				self.UserFunc (self.User, self, self.Pos)
			end
		end

	end
end

function Nx.Slider:OnMouseUp (button)

	local inst = self.NxInst
	inst.DragX = nil
end

function Nx.Slider:OnUpdate (elapsed)

	local this = self			-- V4
	local self = this.NxInst

	self:Drag()

	if self.NeedUpdate then
		self.NeedUpdate = false
		self:DoUpdate()
	end
end

function Nx.Slider:Drag()

	if self.DragX then

		local frm = self.Frm
		local x, y = Nx.Util_GetMouseClampedXY (frm)

		if x then

			local tfrm = self.ThumbFrm

			if self.TypeH then

				local dx = self.DragX - x

			else

				local dy = self.DragY - y

				local h = (frm:GetTop() or 0) - (frm:GetBottom() or 0)
				y = h - y

				if dy ~= 0 then

--					h = h - tfrm:GetHeight()

					local i = dy / h * (self.Max - self.Min + 1)
					self:Set (self.DragPos + i)

					self:Update()

					if self.UserFunc then
						self.UserFunc (self.User, self, self.Pos)
					end

--					Nx.prt ("Slider Drag %f %f pos %f", dy, i, self.Pos)
				end
			end

		else
--			self.DragX = nil

		end
	end
end

function Nx.Slider:Update()
	self.NeedUpdate = true
end

---------------------------------------------------------------------------------------
-- Update slider
-- self = instance
---------------------------------------------------------------------------------------

function Nx.Slider:DoUpdate()

	local frm = self.Frm
	local tfrm = self.ThumbFrm

	local range = self.Max - self.Min + 1
	local per = (self.Pos - self.Min) / (max (range - self.VisSz, 1))

--	Nx.prt ("Slider Update per %f", per)

	if self.TypeH then

		local w = (frm:GetRight() or 0) - (frm:GetLeft() or 0)		-- Attached, so GetWidth is wrong
		tfrm:SetPoint ("TOPLEFT", per * w, 0)
	else

		local h = (frm:GetTop() or 0) - (frm:GetBottom() or 0)		-- Attached, so GetHeight is wrong

		local tper = min (self.VisSz / range, 1)
		if tper >= 1 or h < 6 then

			self.TPt = 0
			frm:SetAlpha (.3)
			tfrm:Hide()
		else

			frm:SetAlpha (1)
			tfrm:Show()

			local clip = 0

			local th = tper * h
			if th < 5 then
				clip = 5 - th
				th = 5
			end

--			Nx.prt ("Slider Update tper %f h %f th %f", tper, h, th)

			tfrm:SetHeight (th)

			h = h - tper * h
			self.TPt = -per * h

			tfrm:SetPoint ("TOPLEFT", 0, self.TPt)
		end
	end
end

---------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
-- Create graph
---------------------------------------------------------------------------------------

function Nx.Graph:Create (width, height, parentFrm)

	local c2rgba = Nx.Util_str2rgba

--	prt ("Graph "..width)

	local g = {}		-- New graph

	g.Clear = self.Clear
	g.SetLine = self.SetLine
	g.UpdateLine = self.UpdateLine
	g.SetPeak = self.SetPeak
	g.UpdateFrames = self.UpdateFrames
	g.ResetFrames = self.ResetFrames
	g.GetFrame = self.GetFrame

	local f = CreateFrame ("Frame", nil, parentFrm)
	g.MainFrm = f

	f.NxGraph = g
	f.NxSetSize = self.OnSetSize

	f:EnableMouse (true)

	f:SetFrameStrata ("MEDIUM")
	f:SetWidth (width + 2)
	f:SetHeight (height + 2)
	f:SetPoint ("TOPLEFT", 0, 0)

	local t = f:CreateTexture()
	t:SetColorTexture (c2rgba (".125|.125|.125|.625"))
	t:SetAllPoints (f)
	f.texture = t

	f:Show()

	g.Width = width
	g.Height = height
	g.ScaleX = 8

	g.Frms = {}

	g:Clear()

	local sf = CreateFrame ("Slider", nil, f, "NxSliderFrame")
	g.SliderFrm = sf
	sf.NxGraph = g

	local bd = {
		["bgFile"] = "Interface\Buttons\UI-SliderBar-Background",
		["edgeFile"] = "Interface\Buttons\UI-SliderBar-Border",
		["tile"] = true,
		["tileSize"] = 8,
		["edgeSize"] = 8,
		["insets"] = { ["left"] = 3, ["right"] = 3, ["top"] = 6, ["bottom"] = 6 }}
	sf:SetBackdrop (bd)

	sf:SetOrientation ("HORIZONTAL")
--	sf:SetThumbTexture ("Interface\Buttons\UI-SliderBar-Button-Horizontal")
	sf:SetFrameStrata ("MEDIUM")
	sf:SetWidth (100)
	sf:SetHeight (10)
	sf:ClearAllPoints()
	sf:SetPoint ("BOTTOMLEFT", 0, -11)
	sf:SetMinMaxValues (1, 25)
	sf:SetValueStep (.5)
	sf:SetValue (g.ScaleX)
	sf:SetScript ("OnValueChanged", Nx.Graph.ScaleSlider_OnValueChanged)
--[[
	local st = sf:CreateTexture()
	st:SetTexture (c2rgba ("202020ff"))
	st:SetAllPoints (sf)
	sf.texture = st
--]]
	sf:Show()

	return g
end

-- Scale slider value changed
-- self = frm

function Nx.Graph:ScaleSlider_OnValueChanged()

	self.NxGraph.ScaleX = self:GetValue()
	self.NxGraph:UpdateFrames()

--	prt ("Slider "..self.NxGraph.ScaleX)
end

-- Clear graph
-- self = graph

function Nx.Graph:Clear()

	-- Clear values
	self.Values = {}
	self.Values.Next = 1
	self.Peak = 1

	-- Hide frames
	self:ResetFrames()

end

-- Set a graph line
-- self = graph

function Nx.Graph:SetLine (time, value, colorStr, infoStr)

	local pos = self.Values.Next
	assert (pos ~= 0)

	self.Values[-pos] = time
	self.Values[pos] = value
	self.Values[pos + 0x1000000] = colorStr
	self.Values[pos + 0x2000000] = infoStr

	self.Values.Next = pos + 1

	self:UpdateLine (pos)

end

---------------------------------------------------------------------------------------
-- Set a graph line
-- self = graph
---------------------------------------------------------------------------------------

function Nx.Graph:UpdateLine (pos)

	local c2rgb = Nx.Util_c2rgb

	assert (pos ~= 0)

	local time = self.Values[-pos]

	local x = time * self.ScaleX
	if x >= 0 and x < self.Width - 1 then

		local value = self.Values[pos]

		local h = value / self.Peak
		if h > 1 or h < 0 then
			h = 1
		end

		h = h * self.Height

		if h >= .1 then

			h = max (h, 4)

			local f = self:GetFrame()
			f.NxGraphPos = pos
			f:SetHeight (h)
			f:SetWidth (self.ScaleX * .25)

			f:SetPoint ("BOTTOMLEFT", x, 1)

			local colorStr = self.Values[pos + 0x1000000]
			f.texture:SetColorTexture (c2rgb (colorStr))

			f:Show()
		end
	end
end

---------------------------------------------------------------------------------------
-- Set peak value of graph
-- self = graph
---------------------------------------------------------------------------------------

function Nx.Graph:SetPeak (peak)

	if peak < 1 then
		peak = 1
	end

	if peak > self.Peak then

--		prt ("Peak: "..peak)

		self.Peak = peak
		self:UpdateFrames()
	end
end

---------------------------------------------------------------------------------------
-- Update all graph frames
-- self = graph
---------------------------------------------------------------------------------------

function Nx.Graph:UpdateFrames()

	self:ResetFrames()

	for n = 1, self.Values.Next - 1 do
		self:UpdateLine (n)
	end
end

---------------------------------------------------------------------------------------
-- Reset frames
-- self = graph
---------------------------------------------------------------------------------------

function Nx.Graph:ResetFrames()

	local n = 1
	local f

	while true do
		f = self.Frms[n]
		if not f then
			break
		end
		f:Hide()
		n = n + 1
	end

	self.Frms.Next = 1

end

---------------------------------------------------------------------------------------
-- Get next available frame or create one
-- self = graph
---------------------------------------------------------------------------------------

function Nx.Graph:GetFrame()

	local pos = self.Frms.Next

	if pos > 1000 then
		pos = 1		-- Reset. Too many used
	end

	local f = self.Frms[pos]
	if not f then

		f = CreateFrame ("Frame", nil, self.MainFrm)
		self.Frms[pos] = f
		f.NxGraph = self

		f:SetFrameStrata ("MEDIUM")

		local t = f:CreateTexture()
		t:SetAllPoints (f)
		f.texture = t

		f:SetScript ("OnEnter", Nx.Graph.OnEnter)
		f:SetScript ("OnLeave", Nx.Graph.OnLeave)
		f:EnableMouse (true)

	end

	self.Frms.Next = pos + 1

	return f
end

---------------------------------------------------------------------------------------
-- Called when frame size changes
---------------------------------------------------------------------------------------

function Nx.Graph:OnSetSize (w, h)

	local g = self.NxGraph

	if g.Width ~= w or g.Height ~= h then

		g.Width = w
		g.Height = h

		g:UpdateFrames()
	end
end

---------------------------------------------------------------------------------------
-- Handle mouse on graph line
---------------------------------------------------------------------------------------

function Nx.Graph:OnEnter (motion)

	local this = self			-- V4

	if not GameTooltip:IsOwned (this) and this.NxGraphPos then

		local self = this.NxGraph

		Nx.TooltipOwner = this

		GameTooltip:SetOwner (this, "ANCHOR_CURSOR")
		local v = self.Values
		local str = format ("%.2f: %s", v[-this.NxGraphPos], v[this.NxGraphPos + 0x2000000])
--		Nx.prt (str)
		GameTooltip:SetText (str)
		GameTooltip:Show()
	end
end

---------------------------------------------------------------------------------------
-- Handle mouse leaving graph line
---------------------------------------------------------------------------------------

function Nx.Graph:OnLeave (motion)

	-- V4 this

	if GameTooltip:IsOwned (self) then
		GameTooltip:Hide()
	end
end

function NxWatchListItem_OnUpdate(self, elapsed)
	-- Handle range indicator
	local rangeTimer = self.rangeTimer;
	if ( rangeTimer ) then
		rangeTimer = rangeTimer - elapsed;
		if ( rangeTimer <= 0 ) then
			local link, item, charges, showItemWhenComplete = GetQuestLogSpecialItemInfo(self.questLogIndex);
			if ( not charges or charges ~= self.charges ) then
				--ObjectiveTracker_Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST);
				return;
			end
			local count = self.HotKey;
			local valid = IsQuestLogSpecialItemInRange(self.questLogIndex);
			if ( valid == 0 ) then
				count:Show();
				count:SetVertexColor(1.0, 0.1, 0.1);
			elseif ( valid == 1 ) then
				count:Show();
				count:SetVertexColor(0.6, 0.6, 0.6);
			else
				count:Hide();
			end
			rangeTimer = TOOLTIP_UPDATE_TIME;
		end
		self.rangeTimer = rangeTimer;
	end
end

---------------------------------------------------------------------------------------
--EOF
