--[[
Name: Tablet-2.0
Revision: $Rev: 17873 $
Author(s): ckknight (ckknight@gmail.com)
Website: http://ckknight.wowinterface.com/
Documentation: http://wiki.wowace.com/index.php/Tablet-2.0
SVN: http://svn.wowace.com/root/trunk/TabletLib/Tablet-2.0
Description: A library to provide an efficient, featureful tooltip-style display.
Dependencies: AceLibrary, (optional) Dewdrop-2.0
]]

local MAJOR_VERSION = "Tablet-2.0"
local MINOR_VERSION = "$Revision: 17873 $"

if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end

local DEBUG = false


local SCROLL_UP = "Scroll up"
local SCROLL_DOWN = "Scroll down"
local HINT = "Hint"
local DETACH = "Detach"
local DETACH_DESC = "Detach the tablet from its source."
local SIZE = "Size"
local SIZE_DESC = "Scale the tablet."
local CLOSE_MENU = "Close menu"
local CLOSE_MENU_DESC = "Close the menu."
local COLOR = "Background color"
local COLOR_DESC = "Set the background color."
local LOCK = "Lock"
local LOCK_DESC = "Lock the tablet in its current position. Alt+Right-click for menu or Alt+drag to drag it when locked."

if GetLocale() == "deDE" then
	SCROLL_UP = "Hochscrollen"
	SCROLL_DOWN = "Runterscrollen"
	HINT = "Hinweis"
	DETACH = "L\195\182sen"
	DETACH_DESC = "L\195\182st den Tooltip aus seiner Verankerung."
	SIZE = "Gr\195\182\195\159e"
	SIZE_DESC = "Gr\195\182\195\159e des Tooltips \195\164ndern."
	CLOSE_MENU = "Menu schlie\195\159en"
	CLOSE_MENU_DESC = "Schlie\195\159t das Menu."
	COLOR = "Hintergrundfarbe"
	COLOR_DESC = "Hintergrundfarbe setzen."
	LOCK = "Sperren"
	LOCK_DESC = "Sperrt die aktuelle Position vom Tooltip. Alt+Rechts-klick f\195\188rs Men\195\188 oder Alt+Verschieben f\195\188rs verschieben wenn es gesperrt ist."
elseif GetLocale() == "koKR" then
	SCROLL_UP = "위로 스크롤"
	SCROLL_DOWN = "아래로 스크롤"
	HINT = "힌트"
	DETACH = "분리"
	DETACH_DESC = "테이블을 분리합니다."
	SIZE = "크기"
	SIZE_DESC = "테이블의 크기입니다."
	CLOSE_MENU = "메뉴 닫기"
	CLOSE_MENU_DESC = "메뉴를 닫습니다."
	COLOR = "배경 색상"
	COLOR_DESC = "배경 색상을 설정합니다."
	LOCK = "고정"
	LOCK_DESC = "현재 위치에 테이블을 고정합니다. 알트+우클릭 : 메뉴열기, 알트+드래그 : 고정된것을 드래그합니다."
elseif GetLocale() == "zhCN" then
	SCROLL_UP = "向上翻转"
	SCROLL_DOWN = "向上翻转"
	HINT = "提示"
	DETACH = "分离"
	DETACH_DESC = "分离菜单为独立提示."
	SIZE = "尺寸"
	SIZE_DESC = "缩放菜单显示尺寸."
	CLOSE_MENU = "关闭菜单"
	CLOSE_MENU_DESC = "关闭菜单"
	COLOR = "背景颜色"
	COLOR_DESC = "设置菜单背景颜色."
	LOCK = "锁定"
	LOCK_DESC = "锁定菜单当前位置. alt+右键 将显示选项, alt+拖动 可以移动已锁定的菜单."
elseif GetLocale() == "zhTW" then
	SCROLL_UP = "向上翻轉"
	SCROLL_DOWN = "向上翻轉"
	HINT = "提示"
	DETACH = "分離"
	DETACH_DESC = "分離選單為獨立提示。"
	SIZE = "尺寸"
	SIZE_DESC = "縮放選單顯示尺寸。"
	CLOSE_MENU = "關閉選單"
	CLOSE_MENU_DESC = "關閉選單"
	COLOR = "背景顏色"
	COLOR_DESC = "設置選單背景顏色。"
	LOCK = "鎖定"
	LOCK_DESC = "鎖定選單目前位置. Alt+右鍵 將顯示選項，Alt+拖動 可以移動已鎖定的選單。"
elseif GetLocale() == "frFR" then
	SCROLL_UP = "Parcourir vers le haut"
	SCROLL_DOWN = "Parcourir vers le bas"
	HINT = "Astuce"
	DETACH = "D\195\169tacher"
	DETACH_DESC = "Permet de d\195\169tacher le tableau de sa source."
	SIZE = "Taille"
	SIZE_DESC = "Permet de changer l'\195\169chelle du tableau."
	CLOSE_MENU = "Fermer le menu"
	CLOSE_MENU_DESC = "Ferme ce menu."
	COLOR = "Couleur du fond"
	COLOR_DESC = "Permet de d\195\169finir la couleur du fond."
	LOCK = "Bloquer"
	LOCK_DESC = "Bloque le tableau \195\160 sa position actuelle. Alt+clic-droit pour le menu ou Alt+glisser pour le d\195\169placer quand il est bloqu\195\169."
end

local start = GetTime()
local wrap
local GetProfileInfo
if DEBUG then
	local tree = {}
	local treeMemories = {}
	local treeTimes = {}
	local memories = {}
	local times = {}
	function wrap(value, name)
		if type(value) == "function" then
			local oldFunction = value
			memories[name] = 0
			times[name] = 0
			return function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60)
				local pos = table.getn(tree)
				table.insert(tree, name)
				table.insert(treeMemories, 0)
				table.insert(treeTimes, 0)
				local t, mem = GetTime(), gcinfo()
				local r1, r2, r3, r4, r5, r6, r7, r8 = oldFunction(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60)
				mem, t = gcinfo() - mem, GetTime() - t
				if pos > 0 then
					treeMemories[pos] = treeMemories[pos] + mem
					treeTimes[pos] = treeTimes[pos] + t
				end
				local otherMem = table.remove(treeMemories)
				if mem - otherMem > 0 then
					memories[name] = memories[name] + mem - otherMem
				end
				times[name] = times[name] + t - table.remove(treeTimes)
				table.remove(tree)
				return r1, r2, r3, r4, r5, r6, r7, r8
			end
		end
	end

	function GetProfileInfo()
		return GetTime() - start, times, memories
	end
else
	function wrap(value)
		return value
	end
end

local MIN_TOOLTIP_SIZE = 200
local TESTSTRING_EXTRA_WIDTH = 5
local Tablet = {}
local function getsecond(_, value)
	return value
end
local Dewdrop
local sekeys
local CleanCategoryPool
local pool = {}

local lua51 = loadstring("return function(...) return ... end") and true or false
local table_setn = lua51 and function() end or table.setn

local function del(t)
	if t then
		for k in pairs(t) do
			t[k] = nil
		end
		setmetatable(t, nil)
		table_setn(t, 0)
		table.insert(pool, t)
	end
end

local new

local function copy(parent)
	local t
	if table.getn(pool) > 0 then
		t = table.remove(pool)
	else
		t = {}
	end
	if parent then
		for k,v in pairs(parent) do
			t[k] = v
		end
		table_setn(t, table.getn(parent))
		setmetatable(t, getmetatable(parent))
	end
	return t
end

function new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
	local t
	if table.getn(pool) > 0 then
		t = table.remove(pool)
	else
		t = {}
	end
	if k1 then t[k1] = v1
	if k2 then t[k2] = v2
	if k3 then t[k3] = v3
	if k4 then t[k4] = v4
	if k5 then t[k5] = v5
	if k6 then t[k6] = v6
	if k7 then t[k7] = v7
	if k8 then t[k8] = v8
	if k9 then t[k9] = v9
	if k10 then t[k10] = v10
	if k11 then t[k11] = v11
	if k12 then t[k12] = v12
	if k13 then t[k13] = v13
	if k14 then t[k14] = v14
	if k15 then t[k15] = v15
	if k16 then t[k16] = v16
	if k17 then t[k17] = v17
	if k18 then t[k18] = v18
	if k19 then t[k19] = v19
	if k20 then t[k20] = v20
	if k21 then t[k21] = v21
	if k22 then t[k22] = v22
	if k23 then t[k23] = v23
	if k24 then t[k24] = v24
	if k25 then t[k25] = v25
	if k26 then t[k26] = v26
	if k27 then t[k27] = v27
	if k28 then t[k28] = v28
	if k29 then t[k29] = v29
	if k30 then t[k30] = v30
	end end end end end end end end end end end end end end end end end end end end end end end end end end end end end end
	return t
end
local tmp
tmp = setmetatable({}, {__index = function(self, key)
	local t = {}
	tmp[key] = function(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
		for k in pairs(t) do
			t[k] = nil
		end
		if k1 then t[k1] = v1
		if k2 then t[k2] = v2
		if k3 then t[k3] = v3
		if k4 then t[k4] = v4
		if k5 then t[k5] = v5
		if k6 then t[k6] = v6
		if k7 then t[k7] = v7
		if k8 then t[k8] = v8
		if k9 then t[k9] = v9
		if k10 then t[k10] = v10
		if k11 then t[k11] = v11
		if k12 then t[k12] = v12
		if k13 then t[k13] = v13
		if k14 then t[k14] = v14
		if k15 then t[k15] = v15
		if k16 then t[k16] = v16
		if k17 then t[k17] = v17
		if k18 then t[k18] = v18
		if k19 then t[k19] = v19
		if k20 then t[k20] = v20
		if k21 then t[k21] = v21
		if k22 then t[k22] = v22
		if k23 then t[k23] = v23
		if k24 then t[k24] = v24
		if k25 then t[k25] = v25
		if k26 then t[k26] = v26
		if k27 then t[k27] = v27
		if k28 then t[k28] = v28
		if k29 then t[k29] = v29
		if k30 then t[k30] = v30
		end end end end end end end end end end end end end end end end end end end end end end end end end end end end end end
		return t
	end
	return tmp[key]
end})

local headerSize, normalSize
if GameTooltipHeaderText then
	_,headerSize = GameTooltipHeaderText:GetFont()
else
	headerSize = 14
end
if GameTooltipText then
	_,normalSize = GameTooltipText:GetFont()
else
	normalSize = 12
end
local tooltip
local testString
local TabletData = {}
local Category = {}
local Line = {}
do
	local TabletData_mt = { __index = TabletData }
	function TabletData:new(tablet)
		if not testString then
			testString = UIParent:CreateFontString()
			testString:Hide()
		end
		local self = new()
		self.categories = new()
		self.id = 0
		self.width = 0--(MIN_TOOLTIP_SIZE - 20)*tablet.fontSizePercent
		self.tablet = tablet
		self.title = "Title"
		self.titleR, self.titleG, self.titleB = nil, nil, nil
		setmetatable(self, TabletData_mt)
		return self
	end

	function TabletData:del()
		for k, v in ipairs(self.categories) do
			v:del()
		end
		del(self.categories)
		del(self)
	end

	function TabletData:Display()
		if self.tablet == tooltip or self.tablet.registration.showTitleWhenDetached then
			local info = new(
				'hideBlankLine', true,
				'text', self.title,
				'justify', "CENTER",
				'font', GameTooltipHeaderText,
				'isTitle', true
			)
			if self.titleR then
				info.textR = self.titleR
				info.textG = self.titleG
				info.textB = self.titleB
			end
			self:AddCategory(info, 1)
			del(info)
		end
		if self.tablet == tooltip or self.tablet.registration.showHintWhenDetached then
			if self.hint then
				self:AddCategory(nil):AddLine(
					'text', HINT .. ": " .. self.hint,
					'textR', 0,
					'textG', 1,
					'textB', 0,
					'wrap', true
				)
			end
		end

		local tabletData = self.tabletData
		local width
		for k, v in ipairs(self.categories) do
			if v.columns <= 2 then
				width = v.x1
			else
				width = v.x1 + v.x2 + v.x3 + v.x4 + v.x5 + v.x6 + (v.columns - 1) * 20
			end
			if self.width < width then
				self.width = width
			end
		end

		local good = false
		local lastTitle = true
		for k, v in ipairs(self.categories) do
			if lastTitle then
				v.hideBlankLine = true
				lastTitle = false
			end
			if v:Display(self.tablet) then
				good = true
			end
			if v.isTitle then
				lastTitle = true
			end
		end
		if not good then
			if self.tablet == tooltip or not self.tablet.registration.hideWhenEmpty then
				local width
				local info = new(
					'hideBlankLine', true,
					'text', self.title,
					'justify', "CENTER",
					'font', GameTooltipHeaderText,
					'isTitle', true
				)
				local cat = self:AddCategory(info)
				del(info)
				self.width = self.categories[table.getn(self.categories)].x1
				cat:Display(self.tablet)
			else
				self.tablet:__Hide()
				self.tablet.tmpHidden = true
			end
		else
			self.tablet:__Show()
			self.tablet.tmpHidden = nil
		end
	end

	function TabletData:AddCategory(info, index)
		local made = false
		if not info then
			made = true
			info = new()
		end
		local cat = Category:new(self, info)
		if index then
			table.insert(self.categories, index, cat)
		else
			table.insert(self.categories, cat)
		end
		if made then
			del(info)
		end
		return cat
	end
	
	function TabletData:SetHint(hint)
		self.hint = hint
	end
	
	function TabletData:SetTitle(title)
		self.title = title or "Title"
	end
	
	function TabletData:SetTitleColor(r, g, b)
		self.titleR = r
		self.titleG = g
		self.titleB = b
	end
end
do
	local Category_mt = { __index = Category }
	function Category:new(tabletData, info, superCategory)
		local self = copy(info)
		if superCategory and not self.noInherit then
			self.superCategory = superCategory.superCategory
			for k, v in pairs(superCategory) do
				if string.find(k, "^child_") then
					local k = strsub(k, 7)
					if self[k] == nil then
						self[k] = v
					end
				end
			end
			self.columns = superCategory.columns
		else
			self.superCategory = self
		end
		self.tabletData = tabletData
		self.lines = new()
		if not self.columns then
			self.columns = 1
		end
		self.x1 = 0
		self.x2 = 0
		self.x3 = 0
		self.x4 = 0
		self.x5 = 0
		self.x6 = 0
		setmetatable(self, Category_mt)
		self.lastWasTitle = nil
		if self.text or self.text2 or self.text3 or self.text4 or self.text5 or self.text6 then
			local x = new(
				'category', category,
				'text', self.text,
				'textR', self.textR or 1,
				'textG', self.textG or 1,
				'textB', self.textB or 1,
				'fakeChild', true,
				'func', self.func,
				'arg1', info.arg1,
				'arg2', self.arg2,
				'arg3', self.arg3,
				'hasCheck', self.hasCheck,
				'checked', self.checked,
				'checkIcon', self.checkIcon,
				'isRadio', self.isRadio,
				'font', self.font,
				'size', self.size,
				'wrap', self.wrap,
				'catStart', true,
				'indentation', self.indentation,
				'noInherit', true,
				'justify', self.justify,
				'justify2', self.justify2,
				'justify3', self.justify3,
				'justify4', self.justify4,
				'justify5', self.justify5,
				'justify6', self.justify6
			)
			if self.isTitle then
				x.textR = self.textR or 1
				x.textG = self.textG or 0.823529
				x.textB = self.textB or 0
			else
				x.textR = self.textR or 1
				x.textG = self.textG or 1
				x.textB = self.textB or 1
			end
			x.text2 = self.text2
			x.text3 = self.text3
			x.text4 = self.text4
			x.text5 = self.text5
			x.text6 = self.text6
			x.text2R = self.text2R or self.textR2 or 1
			x.text2G = self.text2G or self.textG2 or 1
			x.text2B = self.text2B or self.textB2 or 1
			x.text3R = self.text3R or self.textR3 or 1
			x.text3G = self.text3G or self.textG3 or 1
			x.text3B = self.text3B or self.textB3 or 1
			x.text4R = self.text4R or self.textR4 or 1
			x.text4G = self.text4G or self.textG4 or 1
			x.text4B = self.text4B or self.textB4 or 1
			x.text5R = self.text5R or self.textR5 or 1
			x.text5G = self.text5G or self.textG5 or 1
			x.text5B = self.text5B or self.textB5 or 1
			x.text6R = self.text6R or self.textR6 or 1
			x.text6G = self.text6G or self.textG6 or 1
			x.text6B = self.text6B or self.textB6 or 1
			x.font2 = self.font2
			x.font3 = self.font3
			x.font4 = self.font4
			x.font5 = self.font5
			x.font6 = self.font6
			x.size2 = self.size2
			x.size3 = self.size3
			x.size4 = self.size4
			x.size5 = self.size5
			x.size6 = self.size6
			self:AddLine(x)
			del(x)
			self.lastWasTitle = true
		end
		return self
	end

	function Category:del()
		local prev = garbageLine
		for k, v in pairs(self.lines) do
			v:del()
		end
		del(self.lines)
		del(self)
	end

	function Category:AddLine(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
		self.lastWasTitle = nil
		local line
		if type(k1) == "table" then
			Line:new(self, k1, v1)
		else
			local info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
			Line:new(self, info)
			del(info)
		end
	end

	function Category:AddCategory(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
		local lastWasTitle = self.lastWasTitle
		self.lastWasTitle = nil
		local info
		if type(k1) == "table" then
			info = k1
		else
			info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
		end
		if lastWasTitle or table.getn(self.lines) == 0 then
			info.hideBlankLine = true
		end
		local cat = Category:new(self.tabletData, info, self)
		table.insert(self.lines, cat)
		return cat
	end

	function Category:HasChildren()
		local hasChildren = false
		for k, v in ipairs(self.lines) do
			if v.HasChildren then
				if v:HasChildren() then
					return true
				end
			end
			if not v.fakeChild then
				return true
			end
		end
		return false
	end

	local lastWasTitle = false
	function Category:Display(tablet)
		if not self.isTitle and not self.showWithoutChildren and not self:HasChildren() then
			return false
		end
		if not self.hideBlankLine and not lastWasTitle then
			local info = new(
				'blank', true,
				'fakeChild', true
			)
			self:AddLine(info, 1)
			del(info)
		end
		local good = false
		if table.getn(self.lines) > 0 then
			self.tabletData.id = self.tabletData.id + 1
			self.id = self.tabletData.id
			for k, v in ipairs(self.lines) do
				if v:Display(tablet) then
					good = true
				end
			end
		end
		lastWasTitle = self.isTitle
		return good
	end
end
do
	local Line_mt = { __index = Line }
	function Line:new(category, info, position)
		local self = copy(info)
		if not info.noInherit then
			for k, v in pairs(category) do
				if string.find(k, "^child_") then
					local k = strsub(k, 7)
					if self[k] == nil then
						self[k] = v
					end
				end
			end
		end
		self.category = category
		if position then
			table.insert(category.lines, position, self)
		else
			table.insert(category.lines, self)
		end
		setmetatable(self, Line_mt)
		local columns = category.columns
		if columns == 1 then
			if not self.justify then
				self.justify = "LEFT"
			end
		elseif columns == 2 then
			self.justify = "LEFT"
			self.justify2 = "RIGHT"
			if self.wrap then
				self.wrap2 = false
			end
		elseif columns == 3 then
			if not self.justify then
				self.justify = "LEFT"
			end
			if not self.justify2 then
				self.justify2 = "CENTER"
			end
			if not self.justify3 then
				self.justify3 = "RIGHT"
			end
			if self.wrap then
				self.wrap2 = false
				self.wrap3 = false
			elseif self.wrap2 then
				self.wrap3 = false
			end
		elseif columns == 4 then
			if not self.justify then
				self.justify = "LEFT"
			end
			if not self.justify2 then
				self.justify2 = "CENTER"
			end
			if not self.justify3 then
				self.justify3 = "CENTER"
			end
			if not self.justify4 then
				self.justify4 = "RIGHT"
			end
			if self.wrap then
				self.wrap2 = false
				self.wrap3 = false
				self.wrap4 = false
			elseif self.wrap2 then
				self.wrap3 = false
				self.wrap4 = false
			elseif self.wrap3 then
				self.wrap4 = false
			end
		elseif columns == 5 then
			if not self.justify then
				self.justify = "LEFT"
			end
			if not self.justify2 then
				self.justify2 = "CENTER"
			end
			if not self.justify3 then
				self.justify3 = "CENTER"
			end
			if not self.justify4 then
				self.justify4 = "CENTER"
			end
			if not self.justify5 then
				self.justify5 = "RIGHT"
			end
			if self.wrap then
				self.wrap2 = false
				self.wrap3 = false
				self.wrap4 = false
				self.wrap5 = false
			elseif self.wrap2 then
				self.wrap3 = false
				self.wrap4 = false
				self.wrap5 = false
			elseif self.wrap3 then
				self.wrap4 = false
				self.wrap5 = false
			elseif self.wrap4 then
				self.wrap5 = false
			end
		elseif columns == 6 then
			if not self.justify then
				self.justify = "LEFT"
			end
			if not self.justify2 then
				self.justify2 = "CENTER"
			end
			if not self.justify3 then
				self.justify3 = "CENTER"
			end
			if not self.justify4 then
				self.justify4 = "CENTER"
			end
			if not self.justify5 then
				self.justify5 = "CENTER"
			end
			if not self.justify6 then
				self.justify6 = "RIGHT"
			end
			if self.wrap then
				self.wrap2 = false
				self.wrap3 = false
				self.wrap4 = false
				self.wrap5 = false
				self.wrap6 = false
			elseif self.wrap2 then
				self.wrap3 = false
				self.wrap4 = false
				self.wrap5 = false
				self.wrap6 = false
			elseif self.wrap3 then
				self.wrap4 = false
				self.wrap5 = false
				self.wrap6 = false
			elseif self.wrap4 then
				self.wrap5 = false
				self.wrap6 = false
			elseif self.wrap5 then
				self.wrap6 = false
			end
		end
		if self.textR2 then
			self.text2R, self.textR2 = self.text2R or self.textR2
			self.text2G, self.textG2 = self.text2G or self.textG2
			self.text2B, self.textB2 = self.text2B or self.textB2
			if self.textR3 then
				self.text3R, self.textR3 = self.text3R or self.textR3
				self.text3G, self.textG3 = self.text3G or self.textG3
				self.text3B, self.textB3 = self.text3B or self.textB3
				if self.textR4 then
					self.text4R, self.textR4 = self.text4R or self.textR4
					self.text4G, self.textG4 = self.text4G or self.textG4
					self.text4B, self.textB4 = self.text4B or self.textB4
					if self.textR5 then
						self.text5R, self.textR5 = self.text5R or self.textR5
						self.text5G, self.textG5 = self.text5G or self.textG5
						self.text5B, self.textB5 = self.text5B or self.textB5
						if self.textR5 then
							self.text6R, self.textR6 = self.text6R or self.textR6
							self.text6G, self.textG6 = self.text6G or self.textG6
							self.text6B, self.textB6 = self.text6B or self.textB6
						end
					end
				end
			end
		end
		if not self.indentation or self.indentation < 0 then
			self.indentation = 0
		end
		if not self.font then
			self.font = GameTooltipText
		end
		if not self.font2 then
			self.font2 = self.font
		end
		if not self.font3 then
			self.font3 = self.font
		end
		if not self.font4 then
			self.font4 = self.font
		end
		if not self.font5 then
			self.font5 = self.font
		end
		if not self.font6 then
			self.font6 = self.font
		end
		if not self.size then
			_,self.size = self.font:GetFont()
		end
		if not self.size2 then
			_,self.size2 = self.font2:GetFont()
		end
		if not self.size3 then
			_,self.size3 = self.font3:GetFont()
		end
		if not self.size4 then
			_,self.size4 = self.font4:GetFont()
		end
		if not self.size5 then
			_,self.size5 = self.font5:GetFont()
		end
		if not self.size6 then
			_,self.size6 = self.font6:GetFont()
		end

		local fontSizePercent = category.tabletData.tablet.fontSizePercent
		local w = 0
		self.checkWidth = 0
		if self.text then
			if not self.wrap then
				testString:SetWidth(0)
				testString:SetFontObject(self.font)
				local font,_,flags = testString:GetFont()
				testString:SetFont(font, self.size * fontSizePercent, flags)
				testString:SetText(self.text)
				local checkWidth = self.hasCheck and self.size * fontSizePercent or 0
				self.checkWidth = checkWidth
				w = testString:GetWidth() + self.indentation * fontSizePercent + checkWidth + TESTSTRING_EXTRA_WIDTH
				if category.superCategory.x1 < w then
					category.superCategory.x1 = w
				end
			else
				if columns == 1 then
					testString:SetWidth(0)
					testString:SetFontObject(self.font)
					local font,_,flags = testString:GetFont()
					testString:SetFont(font, self.size * fontSizePercent, flags)
					testString:SetText(self.text)
					local checkWidth = self.hasCheck and self.size * fontSizePercent or 0
					self.checkWidth = checkWidth
					w = testString:GetWidth() + self.indentation * fontSizePercent + checkWidth + TESTSTRING_EXTRA_WIDTH
					if w > (MIN_TOOLTIP_SIZE - 20) * fontSizePercent then
						w = (MIN_TOOLTIP_SIZE - 20) * fontSizePercent
					end
				else
					w = MIN_TOOLTIP_SIZE * fontSizePercent / 2
				end
				if category.superCategory.x1 < w then
					category.superCategory.x1 = w
				end
			end
		end
		if columns == 2 and self.text2 then
			if not self.wrap2 then
				testString:SetWidth(0)
				testString:SetFontObject(self.font2)
				local font,_,flags = testString:GetFont()
				testString:SetFont(font, self.size2 * fontSizePercent, flags)
				testString:SetText(self.text2)
				w = w + 40 * fontSizePercent + testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
				if category.superCategory.x1 < w then
					category.superCategory.x1 = w
				end
			else
				w = w + 40 * fontSizePercent + MIN_TOOLTIP_SIZE * fontSizePercent / 2
				if category.superCategory.x1 < w then
					category.superCategory.x1 = w
				end
			end
		elseif columns >= 3 then
			if self.text2 then
				if not self.wrap2 then
					testString:SetWidth(0)
					testString:SetFontObject(self.font2)
					local font,_,flags = testString:GetFont()
					testString:SetFont(font, self.size2 * fontSizePercent, flags)
					testString:SetText(self.text2)
					local w = testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
					if category.superCategory.x2 < w then
						category.superCategory.x2 = w
					end
				else
					local w = MIN_TOOLTIP_SIZE / 2
					if category.superCategory.x2 < w then
						category.superCategory.x2 = w
					end
				end
			end
			if self.text3 then
				if not self.wrap3 then
					testString:SetWidth(0)
					testString:SetFontObject(self.font3)
					local font,_,flags = testString:GetFont()
					testString:SetFont(font, self.size3 * fontSizePercent, flags)
					testString:SetText(self.text3)
					local w = testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
					if category.superCategory.x3 < w then
						category.superCategory.x3 = w
					end
				else
					local w = MIN_TOOLTIP_SIZE / 2
					if category.superCategory.x3 < w then
						category.superCategory.x3 = w
					end
				end
			end
			if columns >= 4 then
				if self.text4 then
					if not self.wrap4 then
						testString:SetWidth(0)
						testString:SetFontObject(self.font4)
						local font,_,flags = testString:GetFont()
						testString:SetFont(font, self.size4 * fontSizePercent, flags)
						testString:SetText(self.text4)
						w = testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
						if category.superCategory.x4 < w then
							category.superCategory.x4 = w
						end
					else
						local w = MIN_TOOLTIP_SIZE / 2
						if category.superCategory.x4 < w then
							category.superCategory.x4 = w
						end
					end
				end
				if columns >= 5 then
					if self.text5 then
						if not self.wrap5 then
							testString:SetWidth(0)
							testString:SetFontObject(self.font5)
							local font,_,flags = testString:GetFont()
							testString:SetFont(font, self.size5 * fontSizePercent, flags)
							testString:SetText(self.text5)
							w = testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
							if category.superCategory.x5 < w then
								category.superCategory.x5 = w
							end
						else
							local w = MIN_TOOLTIP_SIZE / 2
							if category.superCategory.x5 < w then
								category.superCategory.x5 = w
							end
						end
					end
					if columns >= 6 then
						if self.text6 then
							if not self.wrap6 then
								testString:SetWidth(0)
								testString:SetFontObject(self.font6)
								local font,_,flags = testString:GetFont()
								testString:SetFont(font, self.size6 * fontSizePercent, flags)
								testString:SetText(self.text6)
								w = testString:GetWidth() + TESTSTRING_EXTRA_WIDTH
								if category.superCategory.x6 < w then
									category.superCategory.x6 = w
								end
							else
								local w = MIN_TOOLTIP_SIZE / 2
								if category.superCategory.x6 < w then
									category.superCategory.x6 = w
								end
							end
						end
					end
				end
			end
		end
		return self
	end

	function Line:del()
		del(self)
	end

	function Line:Display(tablet)
		tablet:AddLine(self)
		return true
	end
end

local function button_OnEnter()
	if type(this.self:GetScript("OnEnter")) == "function" then
		this.self:GetScript("OnEnter")()
	end
	this.highlight:Show()
end

local function button_OnLeave()
	if type(this.self:GetScript("OnLeave")) == "function" then
		this.self:GetScript("OnLeave")()
	end
	this.highlight:Hide()
end

local function NewLine(self)
	if self.maxLines <= self.numLines then
		self.maxLines = self.maxLines + 1
		local button = CreateFrame("Button", nil, self)
		button.indentation = 0
		local check = button:CreateTexture(nil, "ARTWORK")
		local left = button:CreateFontString(nil, "ARTWORK")
		local right = button:CreateFontString(nil, "ARTWORK")
		local third = button:CreateFontString(nil, "ARTWORK")
		local fourth = button:CreateFontString(nil, "ARTWORK")
		local fifth = button:CreateFontString(nil, "ARTWORK")
		local sixth = button:CreateFontString(nil, "ARTWORK")
		local highlight = button:CreateTexture(nil, "BACKGROUND")
		highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
		button.highlight = highlight
		highlight:SetBlendMode("ADD")
		highlight:SetAllPoints(button)
		highlight:Hide()
		table.insert(self.buttons, button)
		table.insert(self.checks, check)
		table.insert(self.lefts, left)
		table.insert(self.rights, right)
		table.insert(self.thirds, third)
		table.insert(self.fourths, fourth)
		table.insert(self.fifths, fifth)
		table.insert(self.sixths, sixth)
		left:SetWidth(0)
		if self.maxLines == 1 then
			left:SetFontObject(GameTooltipHeaderText)
			right:SetFontObject(GameTooltipHeaderText)
			third:SetFontObject(GameTooltipHeaderText)
			fourth:SetFontObject(GameTooltipHeaderText)
			fifth:SetFontObject(GameTooltipHeaderText)
			sixth:SetFontObject(GameTooltipHeaderText)
			left:SetJustifyH("CENTER")
			button:SetPoint("TOPLEFT", self, "TOPLEFT", 8, -10)
		else
			left:SetFontObject(GameTooltipText)
			right:SetFontObject(GameTooltipText)
			third:SetFontObject(GameTooltipText)
			fourth:SetFontObject(GameTooltipText)
			fifth:SetFontObject(GameTooltipText)
			sixth:SetFontObject(GameTooltipText)
			button:SetPoint("TOPLEFT", self.buttons[self.maxLines - 1], "BOTTOMLEFT", 0, -2)
		end
		button:SetScript("OnEnter", button_OnEnter)
		button:SetScript("OnLeave", button_OnLeave)
		button.check = check
		button.self = self
		button:SetPoint("RIGHT", self, "RIGHT", -12, 0)
		check.shown = false
		check:SetPoint("TOPLEFT", button, "TOPLEFT")
		left:SetPoint("TOPLEFT", check, "TOPLEFT")
		right:SetPoint("TOPLEFT", left, "TOPRIGHT", 40 * self.fontSizePercent, 0)
		third:SetPoint("TOPLEFT", right, "TOPRIGHT", 20 * self.fontSizePercent, 0)
		fourth:SetPoint("TOPLEFT", third, "TOPRIGHT", 20 * self.fontSizePercent, 0)
		fifth:SetPoint("TOPLEFT", fourth, "TOPRIGHT", 20 * self.fontSizePercent, 0)
		sixth:SetPoint("TOPLEFT", fifth, "TOPRIGHT", 20 * self.fontSizePercent, 0)
		right:SetJustifyH("RIGHT")
		local _,size = GameTooltipText:GetFont()
		check:SetHeight(size * 1.5)
		check:SetWidth(size * 1.5)
		check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
		check:SetAlpha(0)
		if not button.clicked then
			button:SetScript("OnMouseWheel", self:GetScript("OnMouseWheel"))
			button:EnableMouseWheel(true)
			button:Hide()
		end
		check:Show()
		left:Hide()
		right:Hide()
		third:Hide()
		fourth:Hide()
		fifth:Hide()
		sixth:Hide()
	end
end
NewLine = wrap(NewLine, "NewLine")

local function GetMaxLinesPerScreen(self)
	if self == tooltip then
		return floor(50 / self.fontSizePercent)
	else
		return floor(30 / self.fontSizePercent)
	end
end
GetMaxLinesPerScreen = wrap(GetMaxLinesPerScreen, "GetMaxLinesPerScreen")

local detachedTooltips = {}
local AcquireDetachedFrame, ReleaseDetachedFrame
local function AcquireFrame(self, registration, data, detachedData)
	if not detachedData then
		detachedData = data
	end
	if tooltip then
		tooltip.data = data
		tooltip.detachedData = detachedData
		local fontSizePercent = tooltip.data and tooltip.data.fontSizePercent or 1
		local transparency = tooltip.data and tooltip.data.transparency or 0.75
		local r = tooltip.data and tooltip.data.r or 0
		local g = tooltip.data and tooltip.data.g or 0
		local b = tooltip.data and tooltip.data.b or 0
		tooltip:SetFontSizePercent(fontSizePercent)
		tooltip:SetTransparency(transparency)
		tooltip:SetColor(r, g, b)
	else
		tooltip = CreateFrame("Frame", "Tablet20Frame", UIParent)
		self.tooltip = tooltip
		tooltip.data = data
		tooltip.detachedData = detachedData
		tooltip:EnableMouse(true)
		tooltip:EnableMouseWheel(true)
		tooltip:SetFrameStrata("TOOLTIP")
		tooltip:SetFrameLevel(10)
		local backdrop = new(
			'bgFile', "Interface\\Buttons\\WHITE8X8",
			'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
			'tile', true,
			'tileSize', 16,
			'edgeSize', 16,
			'insets', new(
				'left', 5,
				'right', 5,
				'top', 5,
				'bottom', 5
			)
		)
		tooltip:SetBackdrop(backdrop)
		del(backdrop.insets)
		del(backdrop)
		tooltip:SetBackdropColor(0, 0, 0, 1)

		tooltip.numLines = 0
		tooltip.owner = nil
		tooltip.fontSizePercent = tooltip.data and tooltip.data.fontSizePercent or 1
		tooltip.maxLines = 0
		tooltip.buttons = {}
		tooltip.checks = {}
		tooltip.lefts = {}
		tooltip.rights = {}
		tooltip.thirds = {}
		tooltip.fourths = {}
		tooltip.fifths = {}
		tooltip.sixths = {}
		tooltip.transparency = tooltip.data and tooltip.data.transparency or 0.75
		tooltip:SetBackdropColor(0, 0, 0, tooltip.transparency)
		tooltip:SetBackdropBorderColor(1, 1, 1, tooltip.transparency)
		tooltip.scroll = 0

		tooltip:SetScript("OnUpdate", function()
			if not tooltip.updating and not tooltip.enteredFrame then
				tooltip.scroll = 0
				tooltip:Hide()
				tooltip.registration.tooltip = nil
				tooltip.registration = nil
			end
		end)

		tooltip:SetScript("OnEnter", function()
			if tooltip.clickable then
				tooltip.enteredFrame = true
			end
		end)

		tooltip:SetScript("OnLeave", function()
			if not tooltip.updating then
				tooltip.enteredFrame = false
			end
		end)

		tooltip:SetScript("OnMouseWheel", function()
			tooltip.updating = true
			tooltip:Scroll(arg1 < 0)
			tooltip.updating = false
		end)

		NewLine(tooltip)

		tooltip.scrollUp = tooltip:CreateFontString(nil, "ARTWORK")
		tooltip.scrollUp:SetPoint("TOPLEFT", tooltip.buttons[1], "BOTTOMLEFT", 0, -2)
		tooltip.scrollUp:SetPoint("RIGHT", tooltip, "RIGHT", 0, -10)
		tooltip.scrollUp:SetFontObject(GameTooltipText)
		tooltip.scrollUp:Hide()
		local font,_,flags = tooltip.scrollUp:GetFont()
		tooltip.scrollUp:SetFont(font, normalSize * tooltip.fontSizePercent, flags)
		tooltip.scrollUp:SetJustifyH("CENTER")
		tooltip.scrollUp:SetTextColor(1, 0.823529, 0)
		tooltip.scrollUp:SetText(" ")

		tooltip.scrollDown = tooltip:CreateFontString(nil, "ARTWORK")
		tooltip.scrollDown:SetPoint("TOPLEFT", tooltip.buttons[1], "BOTTOMLEFT", 0, -2)
		tooltip.scrollDown:SetPoint("RIGHT", tooltip, "RIGHT", 0, -10)
		tooltip.scrollDown:SetFontObject(GameTooltipText)
		tooltip.scrollDown:Hide()
		local font,_,flags = tooltip.scrollUp:GetFont()
		tooltip.scrollDown:SetFont(font, normalSize * tooltip.fontSizePercent, flags)
		tooltip.scrollDown:SetJustifyH("CENTER")
		tooltip.scrollDown:SetTextColor(1, 0.823529, 0)
		tooltip.scrollDown:SetText(" ")

		function tooltip:SetOwner(o)
			self:Hide(o)
			self.owner = o
		end
		tooltip.SetOwner = wrap(tooltip.SetOwner, "tooltip:SetOwner")

		function tooltip:IsOwned(o)
			return self.owner == o
		end
		tooltip.IsOwned = wrap(tooltip.IsOwned, "tooltip:IsOwned")

		function tooltip:ClearLines(hide)
			CleanCategoryPool(self)
			for i = 1, self.numLines do
				local button = self.buttons[i]
				local check = self.checks[i]
				if not button.clicked or hide then
					button:Hide()
				end
				check.shown = false
				check:SetAlpha(0)
			end
			self.numLines = 0
		end
		tooltip.ClearLines = wrap(tooltip.ClearLines, "tooltip:ClearLines")

		function tooltip:NumLines()
			return self.numLines
		end

		local lastWidth
		local old_tooltip_Hide = tooltip.Hide
		tooltip.__Hide = old_tooltip_Hide
		function tooltip:Hide(newOwner)
			if self == tooltip or newOwner == nil then
				old_tooltip_Hide(self)
			end
			self:ClearLines(true)
			self.owner = nil
			self.lastWidth = nil
			self.tmpHidden = nil
		end
		tooltip.Hide = wrap(tooltip.Hide, "tooltip:Hide")

		local old_tooltip_Show = tooltip.Show
		tooltip.__Show = old_tooltip_Show
		function tooltip:Show(tabletData)
			if self.owner == nil or self.notInUse then
				return
			end
			if not self.tmpHidden then
				old_tooltip_Show(self)
			end

			local maxWidth = tabletData and tabletData.width or self:GetWidth() - 20
			local hasWrap = false
			local screenWidth = GetScreenWidth()
			local scrollMax = self.numLines
			if scrollMax > GetMaxLinesPerScreen(self) + self.scroll then
				scrollMax = GetMaxLinesPerScreen(self) + self.scroll
			end
			local numColumns

			local height = 20
			if scrollMax ~= self.numLines then
				self.scrollDown:SetWidth(maxWidth)
				height = height + self.scrollDown:GetHeight() + 2
			end
			if self.scroll ~= 0 then
				self.scrollUp:SetWidth(maxWidth)
				height = height + self.scrollUp:GetHeight() + 2
			end
			self:SetWidth(maxWidth + 20)

			local tmp = self.scroll + 1
			if tmp ~= 1 then
				tmp = tmp + 1
			end
			for i = 1, self.numLines do
				if i < tmp or i > scrollMax or (i == scrollMax and i ~= self.numLines) then
					self.buttons[i]:ClearAllPoints()
					self.buttons[i]:Hide()
				else
					local button = self.buttons[i]
					local left = self.lefts[i]
					local right = self.rights[i]
					local check = self.checks[i]
					button:SetWidth(maxWidth)
					button:SetHeight(math.max(left:GetHeight(), right:GetHeight()))
					height = height + button:GetHeight() + 2
					if i == self.scroll + 1 then
						button:SetPoint("TOPLEFT", self, "TOPLEFT", 10, -10)
					else
						button:SetPoint("TOPLEFT", self.buttons[i - 1], "BOTTOMLEFT", 0, -2)
					end
					if button.clicked then
						check:SetPoint("TOPLEFT", button, "TOPLEFT", button.indentation * self.fontSizePercent + (check.width - check:GetWidth()) / 2 + 1, -1)
					else
						check:SetPoint("TOPLEFT", button, "TOPLEFT", button.indentation * self.fontSizePercent + (check.width - check:GetWidth()) / 2, 0)
					end
					button:Show()
				end
			end
			if self.scroll ~= 0 then
				self.scrollUp:SetPoint("TOPLEFT", self, "TOPLEFT", 10, -10)
				self.buttons[self.scroll + 2]:SetPoint("TOPLEFT", self.scrollUp, "BOTTOMLEFT", 0, -2)
				self.scrollUp:SetText(SCROLL_UP .. " (" .. self.scroll + 2 .. " / " .. self.numLines .. ")")
				self.scrollUp:Show()
			else
				self.scrollUp:Hide()
			end
			if scrollMax ~= self.numLines and self.buttons[scrollMax - 1] then
				self.scrollDown:SetPoint("TOPLEFT", self.buttons[scrollMax - 1], "BOTTOMLEFT", 0, -2)
				self.scrollDown:SetText(SCROLL_DOWN .. " (" .. scrollMax - 1 .. " / " .. self.numLines .. ")")
				self.scrollDown:Show()
			else
				self.scrollDown:Hide()
			end
			self:SetHeight(height)
		end
		tooltip.Show = wrap(tooltip.Show, "tooltip:Show")

		local lastMouseDown
		local function button_OnClick()
			if this.self:HasScript("OnClick") and type(this.self:GetScript("OnClick")) == "function" then
				this.self:GetScript("OnClick")()
			end
			if arg1 == "RightButton" then
				if this.self:HasScript("OnClick") and type(this.self:GetScript("OnClick")) == "function" then
					this.self:GetScript("OnClick")()
				end
			elseif arg1 == "LeftButton" then
				if this.self.preventClick == nil or GetTime() > this.self.preventClick and GetTime() < lastMouseDown + 0.5 then
					this.self.preventClick = nil
					this.self.updating = true
					this.self.preventRefresh = true
					this.func(this.a1, this.a2, this.a3)
					if this.self then
						this.self.preventRefresh = false
						this.self:children()
						this.self.updating = false
					end
				end
			end
		end
		local function button_OnMouseUp()
			if this.self:HasScript("OnMouseUp") and type(this.self:GetScript("OnMouseUp")) == "function" then
				this.self:GetScript("OnMouseUp")()
			end
			if arg1 ~= "RightButton" then
				if this.clicked then
					local a,b,c,d,e = this.check:GetPoint(1)
					this.check:SetPoint(a,b,c,d-1,e+1)
					this.clicked = false
				end
			end
		end
		local function button_OnMouseDown()
			if this.self:HasScript("OnMouseDown") and type(this.self:GetScript("OnMouseDown")) == "function" then
				this.self:GetScript("OnMouseDown")()
			end
			lastMouseDown = GetTime()
			if arg1 ~= "RightButton" then
				local a,b,c,d,e = this.check:GetPoint(1)
				this.check:SetPoint(a,b,c,d+1,e-1)
				this.clicked = true
			end
		end
		function tooltip:AddLine(info)
			local category = info.category.superCategory
			local maxWidth = category.tabletData.width
			local text = info.blank and "\n" or info.text
			local id = info.id
			local func = info.func
			local checked = info.checked
			local isRadio = info.isRadio
			local checkTexture = info.checkTexture
			local fontSizePercent = self.fontSizePercent
			if not info.font then
				info.font = GameTooltipText
			end
			if not info.size then
				_,info.size = info.font:GetFont()
			end
			local catStart = false
			local columns = category and category.columns or 1
			local x1, x2, x3, x4, x5, x6
			if category then
				x1, x2, x3, x4, x5, x6 = category.x1, category.x2, category.x3, category.x4, category.x5, category.x6
			else
				x1, x2, x3, x4, x5, x6 = 0, 0, 0, 0, 0, 0
			end
			if info.isTitle then
				justAddedTitle = true
			end

			self.numLines = self.numLines + 1
			NewLine(self)
			self.lefts[self.numLines]:Show()
			self.buttons[self.numLines]:Show()
			num = self.numLines

			local button = self.buttons[num]
			button.indentation = info.indentation
			local left = self.lefts[num]
			local right = self.rights[num]
			local third = self.thirds[num]
			local fourth = self.fourths[num]
			local fifth = self.fifths[num]
			local sixth = self.sixths[num]
			local check = self.checks[num]
			do -- if columns >= 1 then
				left:SetFontObject(info.font)
				left:SetText(text)
				left:Show()
				if info.textR and info.textG and info.textB then
					left:SetTextColor(info.textR, info.textG, info.textB)
				else
					left:SetTextColor(1, 0.823529, 0)
				end
				local a,_,b = left:GetFont()
				left:SetFont(a, info.size * fontSizePercent, b)
				left:SetJustifyH(info.justify)
				if columns < 2 then
					right:SetText(nil)
					right:Hide()
					right:SetPoint("TOPLEFT", left, "TOPRIGHT", 40 * fontSizePercent, 0)
					right:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, 0)
					third:SetText(nil)
					third:Hide()
					fourth:SetText(nil)
					fourth:Hide()
					fifth:SetText(nil)
					fifth:Hide()
					sixth:SetText(nil)
					sixth:Hide()
				else
					right:SetFontObject(info.font2)
					right:SetText(info.text2)
					right:Show()
					if info.text2R and info.text2G and info.text2B then
						right:SetTextColor(info.text2R, info.text2G, info.text2B)
					else
						right:SetTextColor(1, 0.823529, 0)
					end
					local a,_,b = right:GetFont()
					right:SetFont(a, info.size2 * fontSizePercent, b)
					right:SetJustifyH(info.justify2)
					if columns < 3 then
						right:SetPoint("TOPLEFT", left, "TOPRIGHT", 40 * fontSizePercent, 0)
						right:SetPoint("TOPRIGHT", button, "TOPRIGHT", -5, 0)
						third:SetText(nil)
						third:Hide()
						fourth:SetText(nil)
						fourth:Hide()
						fifth:SetText(nil)
						fifth:Hide()
						sixth:SetText(nil)
						sixth:Hide()
					else
						third:SetFontObject(info.font3)
						third:SetText(info.text3)
						third:Show()
						if info.text3R and info.text3G and info.text3B then
							third:SetTextColor(info.text3R, info.text3G, info.text3B)
						else
							third:SetTextColor(1, 0.823529, 0)
						end
						local a,_,b = third:GetFont()
						third:SetFont(a, info.size3 * fontSizePercent, b)
						right:ClearAllPoints()
						right:SetPoint("TOPLEFT", left, "TOPRIGHT", 20 * fontSizePercent, 0)
						third:SetJustifyH(info.justify3)
						if columns < 4 then
							fourth:SetText(nil)
							fourth:Hide()
							fifth:SetText(nil)
							fifth:Hide()
							sixth:SetText(nil)
							sixth:Hide()
						else
							fourth:SetFontObject(info.font4)
							fourth:SetText(info.text4)
							fourth:Show()
							if info.text4R and info.text4G and info.text4B then
								fourth:SetTextColor(info.text4R, info.text4G, info.text4B)
							else
								fourth:SetTextColor(1, 0.823529, 0)
							end
							local a,_,b = fourth:GetFont()
							fourth:SetFont(a, info.size4 * fontSizePercent, b)
							fourth:SetJustifyH(info.justify4)
							if columns < 5 then
								fifth:SetText(nil)
								fifth:Hide()
								sixth:SetText(nil)
								sixth:Hide()
							else
								fifth:SetFontObject(info.font5)
								fifth:SetText(info.text5)
								fifth:Show()
								if info.text5R and info.text5G and info.text5B then
									fifth:SetTextColor(info.text5R, info.text5G, info.text5B)
								else
									fifth:SetTextColor(1, 0.823529, 0)
								end
								local a,_,b = fourth:GetFont()
								fifth:SetFont(a, info.size5 * fontSizePercent, b)
								fifth:SetJustifyH(info.justify5)
								if columns < 6 then
									sixth:SetText(nil)
									sixth:Hide()
								else
									sixth:SetFontObject(info.font6)
									sixth:SetText(info.text6)
									sixth:Show()
									if info.text5R and info.text6G and info.text6B then
										sixth:SetTextColor(info.text6R, info.text6G, info.text6B)
									else
										sixth:SetTextColor(1, 0.823529, 0)
									end
									local a,_,b = fourth:GetFont()
									sixth:SetFont(a, info.size6 * fontSizePercent, b)
									sixth:SetJustifyH(info.justify6)
								end
							end
						end
					end
				end
			end

			check:SetWidth(info.size)
			check:SetHeight(info.size)
			check.width = info.size
			if info.hasCheck then
				check.shown = true
				check:Show()
				if isRadio then
					check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
					if info.checked then
						check:SetAlpha(1)
						check:SetTexCoord(0.25, 0.5, 0, 1)
					else
						check:SetAlpha(self.transparency)
						check:SetTexCoord(0, 0.25, 0, 1)
					end
				else
					if info.checkIcon then
						check:SetTexture(info.checkIcon)
						if string.sub(info.checkIcon, 1, 16) == "Interface\\Icons\\" then
							check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
						else
							check:SetTexCoord(0, 1, 0, 1)
						end
					else
						check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
						check:SetWidth(info.size * 1.5)
						check:SetHeight(info.size * 1.5)
						check.width = info.size * 1.2
						check:SetTexCoord(0, 1, 0, 1)
					end
					check:SetAlpha(info.checked and 1 or 0)
				end
				left:SetPoint("TOPLEFT", check, "TOPLEFT", check.width, 0)
			else
				left:SetPoint("TOPLEFT", check, "TOPLEFT")
			end
			if columns == 1 then
				left:SetWidth(maxWidth)
			elseif columns == 2 then
				left:SetWidth(0)
				right:SetWidth(0)
				if info.wrap then
					left:SetWidth(maxWidth - right:GetWidth() - 40 * fontSizePercent)
				elseif info.wrap2 then
					right:SetWidth(maxWidth - left:GetWidth() - 40 * fontSizePercent)
				end
				right:ClearAllPoints()
				right:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0, 0)
				if not info.text2 then
					left:SetJustifyH(info.justify or "LEFT")
				end
			elseif columns == 3 then
				left:SetWidth(x1 - info.checkWidth)
				right:SetWidth(x2)
				third:SetWidth(x3)
				right:ClearAllPoints()
				local num = (category.tabletData.width - x1 - x2 - x3) / 2
				right:SetPoint("TOPLEFT", left, "TOPRIGHT", num, 0)
				third:SetPoint("TOPLEFT", right, "TOPRIGHT", num, 0)
			elseif columns == 4 then
				left:SetWidth(x1 - info.checkWidth)
				right:SetWidth(x2)
				third:SetWidth(x3)
				fourth:SetWidth(x4)
				local num = (category.tabletData.width - x1 - x2 - x3 - x4) / 3
				right:SetPoint("TOPLEFT", left, "TOPRIGHT", num, 0)
				third:SetPoint("TOPLEFT", right, "TOPRIGHT", num, 0)
				fourth:SetPoint("TOPLEFT", third, "TOPRIGHT", num, 0)
			elseif columns == 5 then
				left:SetWidth(x1 - info.checkWidth)
				right:SetWidth(x2)
				third:SetWidth(x3)
				fourth:SetWidth(x4)
				fifth:SetWidth(x5)
				local num = (category.tabletData.width - x1 - x2 - x3 - x4 - x5) / 4
				right:SetPoint("TOPLEFT", left, "TOPRIGHT", num, 0)
				third:SetPoint("TOPLEFT", right, "TOPRIGHT", num, 0)
				fourth:SetPoint("TOPLEFT", third, "TOPRIGHT", num, 0)
				fifth:SetPoint("TOPLEFT", fourth, "TOPRIGHT", num, 0)
			elseif columns == 6 then
				left:SetWidth(x1 - info.checkWidth)
				right:SetWidth(x2)
				third:SetWidth(x3)
				fourth:SetWidth(x4)
				fifth:SetWidth(x5)
				sixth:SetWidth(x6)
				local num = (category.tabletData.width - x1 - x2 - x3 - x4 - x5 - x6) / 5
				right:SetPoint("TOPLEFT", left, "TOPRIGHT", num, 0)
				third:SetPoint("TOPLEFT", right, "TOPRIGHT", num, 0)
				fourth:SetPoint("TOPLEFT", third, "TOPRIGHT", num, 0)
				fifth:SetPoint("TOPLEFT", fourth, "TOPRIGHT", num, 0)
				sixth:SetPoint("TOPLEFT", fifth, "TOPRIGHT", num, 0)
			end
			if not self.locked or IsAltKeyDown() then
				local func = info.func
				if func then
					if type(func) == "string" then
						Tablet:assert(type(info.arg1) == "table", "Cannot call method " .. info.func .. " on a non-table")
						func = info.arg1[func]
						Tablet:assert(type(func) == "function", "Method " .. info.func .. " nonexistant")
					end
					Tablet:assert(type(func) == "function", "func must be a function or method")
					button.func = func
					button.a1 = info.arg1
					button.a2 = info.arg2
					button.a3 = info.arg3
					button.self = self
					button:SetScript("OnMouseUp", button_OnMouseUp)
					button:SetScript("OnMouseDown", button_OnMouseDown)
					button:SetScript("OnClick", button_OnClick)
					if button.clicked then
						button:SetButtonState("PUSHED")
					end
					button:EnableMouse(true)
				else
					button:SetScript("OnMouseDown", nil)
					button:SetScript("OnMouseUp", nil)
					button:SetScript("OnClick", nil)
					button:EnableMouse(false)
				end
			else
				button:SetScript("OnMouseDown", nil)
				button:SetScript("OnMouseUp", nil)
				button:SetScript("OnClick", nil)
				button:EnableMouse(false)
			end
		end
		tooltip.AddLine = wrap(tooltip.AddLine, "tooltip:AddLine")

		function tooltip:SetFontSizePercent(percent)
			local data, detachedData = self.data, self.detachedData
			if detachedData and detachedData.detached then
				data = detachedData
			end
			local lastSize = self.fontSizePercent
			percent = tonumber(percent) or 1
			if percent < 0.25 then
				percent = 0.25
			elseif percent > 4 then
				percent = 4
			end
			self.fontSizePercent = percent
			if data then
				data.fontSizePercent = percent
			end
			self.scrollUp:SetFont(font, normalSize * self.fontSizePercent, flags)
			self.scrollDown:SetFont(font, normalSize * self.fontSizePercent, flags)
			local ratio = self.fontSizePercent / lastSize
			for i = 1, self.numLines do
				local left = self.lefts[i]
				local right = self.rights[i]
				local third = self.thirds[i]
				local fourth = self.fourths[i]
				local fifth = self.fifths[i]
				local sixth = self.sixths[i]
				local check = self.checks[i]
				local font, size, flags = left:GetFont()
				left:SetFont(font, size * ratio, flags)
				font, size, flags = right:GetFont()
				right:SetFont(font, size * ratio, flags)
				font, size, flags = third:GetFont()
				third:SetFont(font, size * ratio, flags)
				font, size, flags = fourth:GetFont()
				fourth:SetFont(font, size * ratio, flags)
				font, size, flags = fifth:GetFont()
				fifth:SetFont(font, size * ratio, flags)
				font, size, flags = sixth:GetFont()
				sixth:SetFont(font, size * ratio, flags)
				check.width = check.width * ratio
				check:SetWidth(check:GetWidth() * ratio)
				check:SetHeight(check:GetHeight() * ratio)
			end
			self:SetWidth((self:GetWidth() - 51) * ratio + 51)
			self:SetHeight((self:GetHeight() - 51) * ratio + 51)
			if self:IsShown() and self.children then
				self:Show()
			end
		end
		tooltip.SetFontSizePercent = wrap(tooltip.SetFontSizePercent, "tooltip:SetFontSizePercent")

		function tooltip:GetFontSizePercent()
			return self.fontSizePercent
		end

		function tooltip:SetTransparency(alpha)
			local data, detachedData = self.data, self.detachedData
			if detachedData and detachedData.detached then
				data = detachedData
			end
			self.transparency = alpha
			if data then
				data.transparency = alpha ~= 0.75 and alpha or nil
			end
			self:SetBackdropColor(self.r or 0, self.g or 0, self.b or 0, alpha)
			self:SetBackdropBorderColor(1, 1, 1, alpha)
		end
		tooltip.SetTransparency = wrap(tooltip.SetTransparency, "tooltip:SetTransparency")

		function tooltip:GetTransparency()
			return self.transparency
		end

		function tooltip:SetColor(r, g, b)
			local data, detachedData = self.data, self.detachedData
			if detachedData and detachedData.detached then
				data = detachedData
			end
			self.r = r
			self.g = g
			self.b = b
			if data then
				data.r = r ~= 0 and r or nil
				data.g = g ~= 0 and g or nil
				data.b = b ~= 0 and b or nil
			end
			self:SetBackdropColor(r or 0, g or 0, b or 0, self.transparency)
			self:SetBackdropBorderColor(1, 1, 1, self.transparency)
		end
		tooltip.SetColor = wrap(tooltip.SetColor, "tooltip:SetColor")

		function tooltip:GetColor()
			return self.r, self.g, self.b
		end

		function tooltip:Scroll(down)
			if down then
				if IsShiftKeyDown() then
					self.scroll = self.numLines - GetMaxLinesPerScreen(self)
				else
					self.scroll = self.scroll + 3
				end
			else
				if IsShiftKeyDown() then
					self.scroll = 0
				else
					self.scroll = self.scroll - 3
				end
			end
			if self.scroll > self.numLines - GetMaxLinesPerScreen(self) then
				self.scroll = self.numLines - GetMaxLinesPerScreen(self)
			end
			if self.scroll < 0 then
				self.scroll = 0
			end
			if self:IsShown() then
				self:Show()
			end
		end
		tooltip.Scroll = wrap(tooltip.Scroll, "tooltip:Scroll")

		function tooltip.Detach(tooltip)
			local owner = tooltip.owner
			tooltip:Hide()
			self:assert(tooltip.detachedData, "You cannot detach if detachedData is not present")
			tooltip.detachedData.detached = true
			local detached = AcquireDetachedFrame(self, tooltip.registration, tooltip.data, tooltip.detachedData)

			detached.menu, tooltip.menu = tooltip.menu, nil
			detached.children = tooltip.children
			tooltip.children = nil
			detached:SetOwner(owner)
			detached:children()
			detached:Show()
		end
		tooltip.Detach = wrap(tooltip.Detach, "tooltip:Detach")

	end

	tooltip.registration = registration
	registration.tooltip = tooltip
	return tooltip
end
AcquireFrame = wrap(AcquireFrame, "AcquireFrame")

function ReleaseDetachedFrame(self, data, detachedData)
	if not detachedData then
		detachedData = data
	end
	for _, detached in ipairs(detachedTooltips) do
		if detached.detachedData == detachedData then
			detached.notInUse = true
			detached:Hide()
			detached.registration.tooltip = nil
			detached.registration = nil
		end
	end
end
ReleaseDetachedFrame = wrap(ReleaseDetachedFrame, "ReleaseDetachedFrame")

local StartCheckingAlt, StopCheckingAlt
do
	local frame
	function StartCheckingAlt(func)
		if not frame then
			frame = CreateFrame("Frame")
		end
		local last = IsAltKeyDown()
		frame:SetScript("OnUpdate", function()
			local now = IsAltKeyDown()
			if last ~= now then
				last = now
				func()
			end
		end)
	end
	function StopCheckingAlt()
		if frame then
			frame:SetScript("OnUpdate", nil)
		end
	end
end

function AcquireDetachedFrame(self, registration, data, detachedData)
	if not detachedData then
		detachedData = data
	end
	for _, detached in ipairs(detachedTooltips) do
		if detached.notInUse then
			detached.data = data
			detached.detachedData = detachedData
			detached.notInUse = nil
			local fontSizePercent = detachedData.fontSizePercent or 1
			local transparency = detachedData.transparency or 0.75
			local r = detachedData.r or 0
			local g = detachedData.g or 0
			local b = detachedData.b or 0
			detached:SetFontSizePercent(fontSizePercent)
			detached:SetTransparency(transparency)
			detached:SetColor(r, g, b)
			detached:ClearAllPoints()
			detached:SetPoint(detachedData.anchor or "CENTER", UIParent, detachedData.anchor or "CENTER", detachedData.offsetx or 0, detachedData.offsety or 0)
			detached.registration = registration
			registration.tooltip = detached
			return detached
		end
	end

	if not Dewdrop and AceLibrary:HasInstance("Dewdrop-2.0") then
		Dewdrop = AceLibrary("Dewdrop-2.0")
	end
	StartCheckingAlt(function()
		for _, detached in ipairs(detachedTooltips) do
			if detached:IsShown() and detached.locked then
				detached:EnableMouse(IsAltKeyDown())
				detached:children()
				if detached.moving then
					local a1 = arg1
					arg1 = "LeftButton"
					if type(detached:GetScript("OnMouseUp")) == "function" then
						detached:GetScript("OnMouseUp")()
					end
					arg1 = a1
				end
			end
		end
	end)
	if not tooltip then
		AcquireFrame(self, {})
	end
	local detached = CreateFrame("Frame", "Tablet20DetachedFrame" .. (table.getn(detachedTooltips) + 1), UIParent)
	table.insert(detachedTooltips, detached)
	detached.notInUse = true
	detached:EnableMouse(not data.locked)
	detached:EnableMouseWheel(true)
	detached:SetMovable(true)
	detached:SetPoint(data.anchor or "CENTER", UIParent, data.anchor or "CENTER", data.offsetx or 0, data.offsety or 0)

	detached.numLines = 0
	detached.owner = nil
	detached.fontSizePercent = 1
	detached.maxLines = 0
	detached.buttons = {}
	detached.checks = {}
	detached.lefts = {}
	detached.rights = {}
	detached.thirds = {}
	detached.fourths = {}
	detached.fifths = {}
	detached.sixths = {}
	detached.transparency = 0.75
	detached.r = 0
	detached.g = 0
	detached.b = 0
	detached:SetFrameStrata("BACKGROUND")
	detached:SetBackdrop(tmp.a(
		'bgFile', "Interface\\Buttons\\WHITE8X8",
		'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
		'tile', true,
		'tileSize', 16,
		'edgeSize', 16,
		'insets', tmp.b(
			'left', 5,
			'right', 5,
			'top', 5,
			'bottom', 5
		)
	))
	detached.locked = detachedData.locked
	detached.scroll = 0
	detached:EnableMouse(not detached.locked)

	local width = GetScreenWidth()
	local height = GetScreenHeight()
	detached:SetScript("OnMouseDown", function()
		if arg1 == "LeftButton" then
			detached:StartMoving()
			detached.moving = true
		end
	end)

	detached:SetScript("OnMouseUp", function()
		if arg1 == "LeftButton" then
			detached:StopMovingOrSizing()
			detached.moving = nil
			local anchor
			local offsetx
			local offsety
			if detached:GetTop() + detached:GetBottom() < height then
				anchor = "BOTTOM"
				offsety = detached:GetBottom()
				if offsety < 0 then
					offsety = 0
				end
				if offsety < MainMenuBar:GetTop() and MainMenuBar:IsVisible() then
					offsety = MainMenuBar:GetTop()
				end
				local top = 0
				if FuBar then
					for i = 1, FuBar:GetNumPanels() do
						local panel = FuBar:GetPanel(i)
						if panel:GetAttachPoint() == "BOTTOM" then
							if panel.frame:GetTop() > top then
								top = panel.frame:GetTop()
								break
							end
						end
					end
				end
				if offsety < top then
					offsety = top
				end
			else
				anchor = "TOP"
				offsety = detached:GetTop() - height
				if offsety > 0 then
					offsety = 0
				end
				local bottom = GetScreenHeight()
				if FuBar then
					for i = 1, FuBar:GetNumPanels() do
						local panel = FuBar:GetPanel(i)
						if panel:GetAttachPoint() == "TOP" then
							if panel.frame:GetBottom() < bottom then
								bottom = panel.frame:GetBottom()
								break
							end
						end
					end
				end
				bottom = bottom - GetScreenHeight()
				if offsety > bottom then
					offsety = bottom
				end
			end
			if detached:GetLeft() + detached:GetRight() < width * 2 / 3 then
				anchor = anchor .. "LEFT"
				offsetx = detached:GetLeft()
				if offsetx < 0 then
					offsetx = 0
				end
			elseif detached:GetLeft() + detached:GetRight() < width * 4 / 3 then
				if anchor == "" then
					anchor = "CENTER"
				end
				offsetx = (detached:GetLeft() + detached:GetRight() - GetScreenWidth()) / 2
			else
				anchor = anchor .. "RIGHT"
				offsetx = detached:GetRight() - width
				if offsetx > 0 then
					offsetx = 0
				end
			end
			detached:ClearAllPoints()
			detached:SetPoint(anchor, UIParent, anchor, offsetx, offsety)
			local t = detached.detachedData
			if t.anchor ~= anchor or math.abs(t.offsetx - offsetx) > 8 or math.abs(t.offsety - offsety) > 8 then
				detached.preventClick = GetTime() + 0.05
			end
			t.anchor = anchor
			t.offsetx = offsetx
			t.offsety = offsety
			detached:Show()
		end
	end)

	Dewdrop:Register(detached,
		'children', function(level, value)
			if not detached.registration then
				return
			end
			if detached.menu then
				detached.menu(level, value)
				if level == 1 then
					Dewdrop:AddLine()
				end
			end
			if level == 1 then
				if not detached.registration.cantAttach then
					Dewdrop:AddLine(
						'text', DETACH,
						'tooltipTitle', DETACH,
						'tooltipText', DETACH_DESC,
						'checked', true,
						'arg1', detached,
						'func', "Attach",
						'closeWhenClicked', true
					)
				end
				Dewdrop:AddLine(
					'text', LOCK,
					'tooltipTitle', LOCK,
					'tooltipText', LOCK_DESC,
					'checked', detached:IsLocked(),
					'arg1', detached,
					'func', "Lock",
					'closeWhenClicked', not detached:IsLocked()
				)
				Dewdrop:AddLine(
					'text', COLOR,
					'tooltipTitle', COLOR,
					'tooltipText', COLOR_DESC,
					'hasColorSwatch', true,
					'r', detached.r,
					'g', detached.g,
					'b', detached.b,
					'hasOpacity', true,
					'opacity', detached.transparency,
					'colorFunc', function(r, g, b, a)
						detached:SetColor(r, g, b)
						detached:SetTransparency(a)
					end
				)
				Dewdrop:AddLine(
					'text', SIZE,
					'tooltipTitle', SIZE,
					'tooltipText', SIZE_DESC,
					'hasArrow', true,
					'hasSlider', true,
					'sliderFunc', function(value)
						detached:SetFontSizePercent(value)
					end,
					'sliderMax', 2,
					'sliderMin', 0.5,
					'sliderStep', 0.05,
					'sliderIsPercent', true,
					'sliderValue', detached:GetFontSizePercent()
				)
				Dewdrop:AddLine(
					'text', CLOSE_MENU,
					'tooltipTitle', CLOSE_MENU,
					'tooltipText', CLOSE_MENU_DESC,
					'func', function()
						Dewdrop:Close()
					end
				)
			end
		end,
		'point', function()
			local x, y = detached:GetCenter()
			if x < GetScreenWidth() / 2 then
				if y < GetScreenHeight() / 2 then
					return "BOTTOMLEFT", "BOTTOMRIGHT"
				else
					return "TOPLEFT", "TOPRIGHT"
				end
			else
				if y < GetScreenHeight() / 2 then
					return "BOTTOMRIGHT", "BOTTOMLEFT"
				else
					return "TOPRIGHT", "TOPLEFT"
				end
			end
		end
	)

	NewLine(detached)

	detached.scrollUp = detached:CreateFontString(nil, "ARTWORK")
	detached.scrollUp:SetPoint("TOPLEFT", detached.buttons[1], "BOTTOMLEFT", 0, -2)
	detached.scrollUp:SetPoint("RIGHT", detached, "RIGHT", 0, -10)
	detached.scrollUp:SetFontObject(GameTooltipText)
	detached.scrollUp:Hide()
	local font,_,flags = detached.scrollUp:GetFont()
	detached.scrollUp:SetFont(font, normalSize * detached.fontSizePercent, flags)
	detached.scrollUp:SetJustifyH("CENTER")
	detached.scrollUp:SetTextColor(1, 0.823529, 0)
	detached.scrollUp:SetText(" ")

	detached.scrollDown = detached:CreateFontString(nil, "ARTWORK")
	detached.scrollDown:SetPoint("TOPLEFT", detached.buttons[1], "BOTTOMLEFT", 0, -2)
	detached.scrollDown:SetPoint("RIGHT", detached, "RIGHT", 0, -10)
	detached.scrollDown:SetFontObject(GameTooltipText)
	detached.scrollDown:Hide()
	local font,_,flags = detached.scrollUp:GetFont()
	detached.scrollDown:SetFont(font, normalSize * detached.fontSizePercent, flags)
	detached.scrollDown:SetJustifyH("CENTER")
	detached.scrollDown:SetTextColor(1, 0.823529, 0)
	detached.scrollDown:SetText(" ")

	detached:SetScript("OnMouseWheel", function()
		detached:Scroll(arg1 < 0)
	end)

	detached.SetTransparency = tooltip.SetTransparency
	detached.GetTransparency = tooltip.GetTransparency
	detached.SetColor = tooltip.SetColor
	detached.GetColor = tooltip.GetColor
	detached.SetFontSizePercent = tooltip.SetFontSizePercent
	detached.GetFontSizePercent = tooltip.GetFontSizePercent
	detached.SetOwner = tooltip.SetOwner
	detached.IsOwned = tooltip.IsOwned
	detached.ClearLines = tooltip.ClearLines
	detached.NumLines = tooltip.NumLines
	detached.__Hide = detached.Hide
	detached.__Show = detached.Show
	detached.Hide = tooltip.Hide
	detached.Show = tooltip.Show
	local old_IsShown = detached.IsShown
	function detached:IsShown()
		if self.tmpHidden then
			return true
		else
			return old_IsShown(self)
		end
	end
	detached.AddLine = tooltip.AddLine
	detached.Scroll = tooltip.Scroll
	function detached:IsLocked()
		return self.locked
	end
	function detached:Lock()
		self:EnableMouse(self.locked)
		self.locked = not self.locked
		self.detachedData.locked = self.locked or nil
		self:children()
	end

	function detached.Attach(detached)
		self:assert(detached, "Detached tooltip not given.")
		self:assert(detached.AddLine, "detached argument not a Tooltip.")
		self:assert(detached.owner, "Detached tooltip has no owner.")
		self:assert(not detached.notInUse, "Detached tooltip not in use.")
		detached.menu = nil
		detached.detachedData.detached = nil
		detached:SetOwner(nil)
		detached.notInUse = TRUE
	end

	return AcquireDetachedFrame(self, registration, data, detachedData)
end
AcquireDetachedFrame = wrap(AcquireDetachedFrame, "AcquireDetachedFrame")

function Tablet:Close(parent)
	if not parent then
		if tooltip and tooltip:IsShown() then
			tooltip:Hide()
			tooltip.registration.tooltip = nil
			tooltip.registration = nil
			tooltip.enteredFrame = false
		end
		return
	else
		self:argCheck(parent, 2, "table", "string")
	end
	local info = self.registry[parent]
	self:assert(info, "You cannot close a tablet with an unregistered parent frame.")
	local data = info.data
	local detachedData = info.detachedData
	if detachedData and detachedData.detached then
		ReleaseDetachedFrame(self, data, detachedData)
	elseif tooltip.data == data then
		tooltip:Hide()
		tooltip.registration.tooltip = nil
		tooltip.registration = nil
	end
	tooltip.enteredFrame = false
end
Tablet.Close = wrap(Tablet.Close, "Tablet:Close")

local currentFrame
local currentTabletData

function Tablet:Open(fakeParent, parent)
	self:argCheck(fakeParent, 2, "table", "string")
	self:argCheck(parent, 3, "nil", "table", "string")
	if not parent then
		parent = fakeParent
	end
	local info = self.registry[parent]
	self:assert(info, "You cannot open a tablet with an unregistered parent frame.")
	self:Close()
	local data = info.data
	local detachedData = info.detachedData
	local children = info.children
	if not children then
		return
	end
	local frame = AcquireFrame(self, info, data, detachedData)
	frame.clickable = info.clickable
	frame.menu = info.menu
	local children = info.children
	function frame:children()
		if not self.preventRefresh then
			currentFrame = self
			currentTabletData = TabletData:new(self)
			self:ClearLines()
			if children then
				children()
			end
			currentTabletData:Display(currentFrame)
			self:Show(currentTabletData)
			currentTabletData:del()
			currentTabletData = nil
			currentFrame = nil
		end
	end
	frame:SetOwner(fakeParent)
	frame:children()
	local point = info.point
	local relativePoint = info.relativePoint
	if type(point) == "function" then
		local b
		point, b = point(fakeParent)
		if b then
			relativePoint = b
		end
	end
	if type(relativePoint) == "function" then
		relativePoint = relativePoint(fakeParent)
	end
	if not point then
		point = "CENTER"
	end
	if not relativePoint then
		relativePoint = point
	end
	frame:ClearAllPoints()
	if type(parent) ~= "string" then
		frame:SetPoint(point, fakeParent, relativePoint)
	end
	local offsetx = 0
	local offsety = 0
	if frame:GetBottom() and frame:GetLeft() then
		if frame:GetRight() > GetScreenWidth() then
			offsetx = frame:GetRight() - GetScreenWidth()
		elseif frame:GetLeft() < 0 then
			offsetx = -frame:GetLeft()
		end
		local ratio = GetScreenWidth() / GetScreenHeight()
		if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
			if frame:GetCenter() < GetScreenWidth() / 2 then
				offsetx = frame:GetRight() - GetScreenWidth() / 2
			else
				offsetx = frame:GetLeft() - GetScreenWidth() / 2
			end
		end
		if frame:GetBottom() < 0 then
			offsety = frame:GetBottom()
		elseif frame:GetTop() and frame:GetTop() > GetScreenHeight() then
			offsety = frame:GetTop() - GetScreenHeight()
		end
		if MainMenuBar:IsVisible() and frame:GetBottom() < MainMenuBar:GetTop() and offsety < frame:GetBottom() - MainMenuBar:GetTop() then
			offsety = frame:GetBottom() - MainMenuBar:GetTop()
		end

		if FuBar then
			local top = 0
			if FuBar then
				for i = 1, FuBar:GetNumPanels() do
					local panel = FuBar:GetPanel(i)
					if panel:GetAttachPoint() == "BOTTOM" then
						if panel.frame:GetTop() and panel.frame:GetTop() > top then
							top = panel.frame:GetTop()
							break
						end
					end
				end
			end
			if frame:GetBottom() < top and offsety < frame:GetBottom() - top then
				offsety = frame:GetBottom() - top
			end
			local bottom = GetScreenHeight()
			if FuBar then
				for i = 1, FuBar:GetNumPanels() do
					local panel = FuBar:GetPanel(i)
					if panel:GetAttachPoint() == "TOP" then
						if panel.frame:GetBottom() and panel.frame:GetBottom() < bottom then
							bottom = panel.frame:GetBottom()
							break
						end
					end
				end
			end
			if frame:GetTop() > bottom and offsety < frame:GetTop() - bottom then
				offsety = frame:GetTop() - bottom
			end
		end
	end
	if type(fakeParent) ~= "string" then
		frame:SetPoint(point, fakeParent, relativePoint, -offsetx, -offsety)
	end

	if detachedData and (info.cantAttach or detachedData.detached) and frame == tooltip then
		detachedData.detached = false
		frame:Detach()
	end
	if (not detachedData or not detachedData.detached) and GetMouseFocus() == fakeParent then
		self.tooltip.enteredFrame = true
	end
end
Tablet.Open = wrap(Tablet.Open, "Tablet:Open")

function Tablet:Register(parent, k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
	self:argCheck(parent, 2, "table", "string")
	if self.registry[parent] then
		self:Unregister(parent)
	end
	local info
	if type(k1) == "table" and k1[0] then
		self:assert(type(self.registry[k1]) == "table", "Other parent not registered")
		info = copy(self.registry[k1])
		if type(v1) == "function" then
			info.point = v1
			info.relativePoint = nil
		end
	else
		info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
	end
	self.registry[parent] = info
	info.data = info.data or info.detachedData or {}
	info.detachedData = info.detachedData or info.data
	local data = info.data
	local detachedData = info.detachedData
	if not self.onceRegistered[parent] and type(parent) == "table" and type(parent.SetScript) == "function" and not info.dontHook then
		if not Dewdrop then
			Dewdrop = AceLibrary("Dewdrop-2.0")
		end
		local script = parent:GetScript("OnEnter")
		parent:SetScript("OnEnter", function()
			if script then
				script()
			end
			if self.registry[parent] then
				if (not data or not detachedData.detached) and not Dewdrop:IsOpen(parent) then
					self:Open(parent)
					self.tooltip.enteredFrame = true
				end
			end
		end)
		local script = parent:GetScript("OnLeave")
		parent:SetScript("OnLeave", function()
			if script then
				script()
			end
			if self.registry[parent] then
				if self.tooltip and (not data or not detachedData or not detachedData.detached) then
					self.tooltip.enteredFrame = false
				end
			end
		end)
		if parent:HasScript("OnMouseDown") then
			local script = parent:GetScript("OnMouseDown")
			parent:SetScript("OnMouseDown", function()
				if script then
					script()
				end
				if self.registry[parent] and self.registry[parent].tooltip and self.registry[parent].tooltip == self.tooltip then
					self.tooltip:Hide()
				end
			end)
		end
		if parent:HasScript("OnMouseWheel") then
			local script = parent:GetScript("OnMouseWheel")
			parent:SetScript("OnMouseWheel", function()
				if script then
					script()
				end
				if self.registry[parent] and self.registry[parent].tooltip then
					self.registry[parent].tooltip:Scroll(arg1 < 0)
				end
			end)
		end
	end
	self.onceRegistered[parent] = true
	if GetMouseFocus() == parent then
		self:Open(parent)
	end
end
Tablet.Register = wrap(Tablet.Register, "Tablet:Register")

function Tablet:Unregister(parent)
	self:argCheck(parent, 2, "table", "string")
	self:assert(self.registry[parent], "You cannot unregister a parent frame if it has not been registered already.")
	self.registry[parent] = nil
end
Tablet.Unregister = wrap(Tablet.Unregister, "Tablet:Unregister")

function Tablet:IsRegistered(parent)
	self:argCheck(parent, 2, "table", "string")
	return self.registry[parent] and true
end
Tablet.IsRegistered = wrap(Tablet.IsRegistered, "Tablet:IsRegistered")

local _id = 0
local addedCategory
local currentCategoryInfo
local depth = 0
local categoryPool = {}
function CleanCategoryPool(self)
	for k,v in pairs(categoryPool) do
		del(v)
		categoryPool[k] = nil
	end
	_id = 0
end

function Tablet:AddCategory(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
	self:assert(currentFrame, "You must add categories in within a registration.")
	local info = new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20, k21, v21, k22, v22, k23, v23, k24, v24, k25, v25, k26, v26, k27, v27, k28, v28, k29, v29, k30, v30)
	local cat = currentTabletData:AddCategory(info)
	del(info)
	return cat
end
Tablet.AddCategory = wrap(Tablet.AddCategory, "Tablet:AddCategory")

function Tablet:SetHint(text)
	self:assert(currentFrame, "You must set hint within a registration.")
	self:assert(not currentCategoryInfo, "You cannot set hint in a category.")
	currentTabletData:SetHint(text)
end
Tablet.SetHint = wrap(Tablet.SetHint, "Tablet:SetHint")

function Tablet:SetTitle(text)
	self:assert(currentFrame, "You must set title within a registration")
	self:assert(not currentCategoryInfo, "You cannot set title in a category.")
	currentTabletData:SetTitle(text)
end
Tablet.SetTitle = wrap(Tablet.SetTitle, "Tablet:SetTitle")

function Tablet:SetTitleColor(r, g, b)
	self:assert(currentFrame, "You must set title color within a registration")
	self:assert(not currentCategoryInfo, "You cannot set title color in a category.")
	self:argCheck(r, 2, "number")
	self:argCheck(g, 3, "number")
	self:argCheck(b, 4, "number")
	currentTabletData:SetTitleColor(r, g, b)
end
Tablet.SetTitleColor = wrap(Tablet.SetTitleColor, "Tablet:SetTitleColor")

function Tablet:GetNormalFontSize()
	return normalSize
end

function Tablet:GetHeaderFontSize()
	return headerSize
end

function Tablet:GetNormalFontObject()
	return GameTooltipText
end

function Tablet:GetHeaderFontObject()
	return GameTooltipHeaderText
end

function Tablet:SetFontSizePercent(parent, percent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		if info.tooltip then
			info.tooltip:SetFontSizePercent(percent)
		else
			local data = info.data
			local detachedData = info.detachedData
			if detachedData.detached then
				detachedData.fontSizePercent = percent
			else
				data.fontSizePercent = percent
			end
		end
	elseif type(parent) == "table" then
		parent.fontSizePercent = percent
	else
		self:assert(false, "You cannot change font size with an unregistered parent frame.")
	end
end
Tablet.SetFontSizePercent = wrap(Tablet.SetFontSizePercent, "Tablet:SetFontSizePercent")

function Tablet:GetFontSizePercent(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		local data = info.data
		local detachedData = info.detachedData
		if detachedData.detached then
			return detachedData.fontSizePercent or 1
		else
			return data.fontSizePercent or 1
		end
	elseif type(parent) == "table" then
		return parent.fontSizePercent or 1
	else
		self:assert(false, "You cannot check font size with an unregistered parent frame.")
	end
end
Tablet.GetFontSizePercent = wrap(Tablet.GetFontSizePercent, "Tablet:GetFontSizePercent")

function Tablet:SetTransparency(parent, percent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		if info.tooltip then
			info.tooltip:SetTransparency(percent)
		else
			local data = info.data
			local detachedData = info.detachedData
			if detachedData.detached then
				detachedData.transparency = percent
			elseif data then
				data.transparency = percent
			end
		end
	elseif type(parent) == "table" then
		parent.transparency = percent
	else
		self:assert(false, "You cannot change transparency with an unregistered parent frame.")
	end
end
Tablet.SetTransparency = wrap(Tablet.SetTransparency, "Tablet:SetTransparency")

function Tablet:GetTransparency(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		local data = info.data
		local detachedData = info.detachedData
		if detachedData.detached then
			return detachedData.transparency or 0.75
		else
			return data.transparency or 0.75
		end
	elseif type(parent) == "table" then
		return parent.transparency or 0.75
	else
		self:assert(parent, "You must provide a parent frame to check transparency")
	end
end
Tablet.GetTransparency = wrap(Tablet.GetTransparency, "Tablet:GetTransparency")

function Tablet:SetColor(parent, r, g, b)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		if info.tooltip then
			info.tooltip:SetColor(r, g, b)
		else
			local data = info.data
			local detachedData = info.detachedData
			if detachedData.detached then
				detachedData.r = r
				detachedData.g = g
				detachedData.b = b
			else
				data.r = r
				data.g = g
				data.b = b
			end
		end
	elseif type(parent) == "table" then
		parent.r = r
		parent.g = g
		parent.b = b
	else
		self:assert(false, "You cannot change color with an unregistered parent frame.")
	end
end
Tablet.SetColor = wrap(Tablet.SetColor, "Tablet:SetColor")

function Tablet:GetColor(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	if info then
		local data = info.data
		local detachedData = info.detachedData
		if detachedData.detached then
			return detachedData.r or 0, detachedData.g or 0, detachedData.b or 0
		else
			return data.r or 0, data.g or 0, data.b or 0
		end
	elseif type(parent) == "table" then
		return parent.r or 0, parent.g or 0, parent.b or 0
	else
		self:assert(parent, "You must provide a parent frame to check color")
	end
end
Tablet.GetColor = wrap(Tablet.GetColor, "Tablet:GetColor")

function Tablet:Detach(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot detach tablet with an unregistered parent frame.")
	self:assert(info.detachedData, "You cannot detach tablet without a data field.")
	if info.tooltip and info.tooltip == tooltip then
		tooltip:Detach()
	else
		info.detachedData.detached = true
		local detached = AcquireDetachedFrame(self, info, info.data, info.detachedData)

		detached.menu = info.menu
		local children = info.children
		function detached:children()
			if not self.preventRefresh then
				currentFrame = self
				currentTabletData = TabletData:new(self)
				self:ClearLines()
				if children then
					children()
				end
				currentTabletData:Display(currentFrame)
				self:Show(currentTabletData)
				currentTabletData:del()
				currentTabletData = nil
				currentFrame = nil
			end
		end
		detached:SetOwner(parent)
		detached:children()
	end
end
Tablet.Detach = wrap(Tablet.Detach, "Tablet:Detach")

function Tablet:Attach(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot detach tablet with an unregistered parent frame.")
	self:assert(info.detachedData, "You cannot attach tablet without a data field.")
	if info.tooltip and info.tooltip ~= tooltip then
		info.tooltip:Attach()
	else
		info.detachedData.detached = false
	end
end
Tablet.Attach = wrap(Tablet.Attach, "Tablet:Attach")

function Tablet:IsAttached(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot check tablet with an unregistered parent frame.")
	return not info.detachedData or not info.detachedData.detached
end
Tablet.IsAttached = wrap(Tablet.IsAttached, "Tablet:IsAttached")

function Tablet:Refresh(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot refresh tablet with an unregistered parent frame.")
	local tt = info.tooltip
	if tt and not tt.preventRefresh and tt:IsShown() then
		tt.updating = true
		tt:children()
		tt.updating = false
	end
end
Tablet.Refresh = wrap(Tablet.Refresh, "Tablet:Refresh")

function Tablet:IsLocked(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot detach tablet with an unregistered parent frame.")
	return info.detachedData and info.detachedData.locked
end
Tablet.IsLocked = wrap(Tablet.IsLocked, "Tablet:IsLocked")

function Tablet:ToggleLocked(parent)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot detach tablet with an unregistered parent frame.")
	if info.tooltip and info.tooltip ~= tooltip then
		info.tooltip:Lock()
	elseif info.detachedData then
		info.detachedData.locked = info.detachedData.locked
	end
end
Tablet.ToggleLocked = wrap(Tablet.ToggleLocked, "Tablet:ToggleLocked")

function Tablet:UpdateDetachedData(parent, detachedData)
	self:argCheck(parent, 2, "table", "string")
	local info = self.registry[parent]
	self:assert(info, "You cannot detach tablet with an unregistered parent frame.")
	self:argCheck(detachedData, 3, "table")
	if info.data == info.detachedData then
		info.data = detachedData
	end
	info.detachedData = detachedData
	if info.detachedData.detached then
		self:Detach(parent)
	elseif info.tooltip and info.tooltip.owner then
		self:Attach(parent)
	end
end
Tablet.UpdateDetachedData = wrap(Tablet.UpdateDetachedData, "Tablet:UpdateDetachedData")

if DEBUG then
	function Tablet:ListProfileInfo()
		local duration, times, memories = GetProfileInfo()
		self:assert(duration and times and memories)
		local t = new()
		for method in pairs(memories) do
			table.insert(t, method)
		end
		table.sort(t, function(alpha, bravo)
			if memories[alpha] ~= memories[bravo] then
				return memories[alpha] < memories[bravo]
			elseif times[alpha] ~= times[bravo] then
				return times[alpha] < times[bravo]
			else
				return alpha < bravo
			end
		end)
		local memory = 0
		local time = 0
		for _,method in ipairs(t) do
			DEFAULT_CHAT_FRAME:AddMessage(format("%s || %.3f s || %.3f%% || %d KiB", method, times[method], times[method] / duration * 100, memories[method]))
			memory = memory + memories[method]
			time = time + times[method]
		end
		DEFAULT_CHAT_FRAME:AddMessage(format("%s || %.3f s || %.3f%% || %d KiB", "Total", time, time / duration * 100, memory))
		table_setn(t, 0)
		del(t)
	end
	SLASH_TABLET1 = "/tablet"
	SLASH_TABLET2 = "/tabletlib"
	SlashCmdList["TABLET"] = function(msg)
		TabletLib:GetInstance(MAJOR_VERSION):ListProfileInfo()
	end
end

local function activate(self, oldLib, oldDeactivate)
	Tablet = self
	if oldLib then
		self.registry = oldLib.registry
		self.onceRegistered = oldLib.onceRegistered
		self.tooltip = oldLib.tooltip
	else
		self.registry = {}
		self.onceRegistered = {}
	end

	tooltip = self.tooltip

	if oldDeactivate then
		oldDeactivate(oldLib)
	end
end

local function deactivate(self)
	StopCheckingAlt()
end

AceLibrary:Register(Tablet, MAJOR_VERSION, MINOR_VERSION, activate, deactivate)
