Quantcast
local mod = StarTip:NewModule("UnitTooltip", "AceEvent-3.0")
mod.name = "Unit Tooltip"
mod.toggled = true
local WidgetText = LibStub("LibScriptableWidgetText-1.0", true)
assert(WidgetText, "Text module requires LibScriptableWidgetText-1.0")
local LCDText = LibStub("LibScriptableLCDText-1.0", true)
assert(LCDText, mod.name .. " requires LibScriptableLCDText-1.0")
local LibCore = LibStub("LibScriptableLCDCore-1.0", true)
assert(LibCore, mod.name .. " requires LibScriptableLCDCore-1.0")
local LibTimer = LibStub("LibScriptableUtilsTimer-1.0", true)
assert(LibTimer, mod.name .. " requires LibScriptableUtilsTimer-1.0")
local LibEvaluator = LibStub("LibScriptableUtilsEvaluator-1.0", true)
assert(LibEvaluator, mod.name .. " requires LibScriptableUtilsEvaluator-1.0")
local _G = _G
local StarTip = _G.StarTip
local L = StarTip.L
local self = mod
local GameTooltip = _G.GameTooltip
local tinsert = _G.tinsert
local unpack = _G.unpack
local select = _G.select
local format = _G.format
local floor = _G.floor
local tostring = _G.tostring
local LSM = _G.LibStub("LibSharedMedia-3.0")
local factionList = {}
local linesToAdd = {}
local linesToAddR = {}
local linesToAddG = {}
local linesToAddB = {}
local linesToAddRight = {}
local linesToAddRightR = {}
local linesToAddRightG = {}
local linesToAddRightB = {}
local lines = {}
mod.lines = lines
local environment = StarTip.environment
local appearance = StarTip:GetModule("Appearance")
local function errorhandler(err)
    return geterrorhandler()(err)
end
local ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_MARQUEE, ALIGN_AUTOMATIC, ALIGN_PINGPONG = 1, 2, 3, 4, 5, 6
local SCROLL_RIGHT, SCROLL_LEFT = 1, 2
local function copy(src, dst)
    if type(src) ~= "table" then return nil end
    if type(dst) ~= "table" then dst = {} end
    for k, v in pairs(src) do
        if type(v) == "table" then
            v = copy(v)
        end
        dst[k] = v
    end
    return dst
end
local defaults = {profile={titles=true, empty = true, lines = {}, refreshRate = 0, color = {r = 1, g = 1, b = 1}}}
local defaultLines={
    [1] = {
        name = "UnitName",
        left = [[
local r, g, b
if UnitIsPlayer(unit) then
    r, g, b = ClassColor(unit)
else
    if UnitIsTapped(unit) and not UnitIsTappedByPlayer(unit) then
        r, g, b = .5, .5, .5
    else
        r, g, b = UnitSelectionColor(unit)
    end
end
local afk = AFK(unit)
if afk then
    afk = " " .. Angle(afk)
else
    afk = ""
end
local dnd = DND(unit)
if dnd and afk == "" then
    afk = " " .. Angle(dnd)
end
local offline = Offline(unit)
if offline then
    afk = " " .. Angle(offline)
end
local dead = Dead(unit)
if dead then
    afk =  afk .. " " .. Angle(dead)
end
local port = StarTip:GetModule("Portrait")
local texture  = ""
if port and port.texture then
    texture = port.texture:GetTexture()
end
if texture ~= "" then
    texture = Texture("", port.db.profile.size) .. " "
end
return texture .. Colorize((Name(unit, true) or Name(unit)) .. afk , r, g, b)
]],
        right = nil,
        bold = true,
        enabled = true,
        cols = 80,
        leftOutlined = 3,
	leftUpdating = true,
	update = 500
    },
    [2] = {
        name = "Target",
        left = 'return L["Target:"]',
        right = [[
-- Here's an example of how to deal with DogTag support.
-- Note that you have to consider which quote characters
-- you are using to contain the entire dog tag within,
-- as opposed what you're using inside. Here I'm using `'` surrounding
-- everything, and I'm using `"` within the dog tag itself.
-- Also, each new line inside a string should end with a '\'.
-- Also note; you'll have to "escape" where you wish to enter a '\' character
-- inside your dog tag. Do that by repeating the '\' character.
-- Example: return "\\ FOO \\"
-- Would print "\ FOO \"

self.unitOverride = unit .. "mouseover"
self.clearOverride = true

if not UnitExists(self.unitOverride) then return L["None"] end

local dt = '\
[IsUnit("player") and "<YOU>":ClassColor or Color(Name, %f, %f, %f ) \
(if PvP then \
 "++":Red \
end)]'

local r, g, b
if UnitIsPlayer(self.unitOverride) then
    r, g, b = ClassColor(unit)
else
    r, g, b = UnitSelectionColor(self.unitOverride)
end

return dt:format(r, g, b)
]],
        rightUpdating = true,
	leftUpdating = true,
        update = 500,
        enabled = true,
	dogtag = true
    },
    [3] = {
        name = L["Guild"],
        left = 'return L["Guild:"]',
        right = [[
return Guild(unit, true)
]],
        enabled = true
    },
    [4] = {
        name = L["Rank"],
        left = 'return L["Rank:"]',
        right = [[
local rank = Rank(unit)
local index = RankIndex(unit)
if rank then
  return format("%s (%d)", rank, index)
end
]],
        enabled = true,
    },
    [5] = {
        name = L["Realm"],
        left = 'if Realm(unit) then return L["Realm:"] end',
        right = [[
return Realm(unit)
]],
        enabled = true
    },
    [6] = {
        name = L["Level"],
        left = 'return L["Level:"]',
        right = [[
local classification = Classification(unit)
local lvl = Level(unit)
local str = ""
local r, g, b
if classification then
    str = classification
end
if lvl then
    str = str .. " (" .. lvl .. ")"
end
str = Colorize(str, DifficultyColor(unit))
return str
]],
        enabled = true,
    },
    [7] = {
        name = L["Gender"],
        left = 'return L["Gender:"]',
        right = [[
local sex = UnitSex(unit)
if sex == 2 then
    return L["Male"]
elseif sex == 3 then
    return L["Female"]
end
]],
        enabled = true
    },
    [8] = {
        name = L["Race"],
        left = 'return L["Race:"]',
        right = [[
return SmartRace(unit)
]],
        enabled = true,
    },
    [9] = {
        name = "Class",
        left = 'return L["Class:"]',
        right = [[
local class, tag = UnitClass(unit)
if class == UnitName(unit) then return end
local r, g, b
if UnitIsPlayer(unit) then
    r, g, b = ClassColor(unit)
else
    r, g, b = 1, 1, 1
end
return Texture(format("Interface\\Addons\\StarTip\\Media\\icons\\%s.tga", tag), 16) .. Colorize(" " .. class, r, g, b)
]],
        enabled = true,
        cols = 100
    },
    [10] = {
        name = L["Druid Form"],
        left = 'return L["Form:"]',
        right = [[
return DruidForm(unit)
]],
        enabled = true
    },
    [11] = {
        name = L["Faction"],
        left = 'return L["Faction:"]',
        right = [[
return Faction(unit)
]],
        enabled = true,
    },
    [12] = {
        name = L["Status"],
        left = 'return L["Status:"]',
        right = [[
if not UnitIsConnected(unit) then
    return L["Offline"]
elseif HasAura(unit, GetSpellInfo(19752)) then
    return L["Divine Intervention"]
elseif UnitIsFeignDeath(unit) then
    return L["Feigned Death"]
elseif UnitIsGhost(unit) then
    return L["Ghost"]
elseif UnitIsDead(unit) and HasAura(unit, GetSpellInfo(20707)) then
    return L["Soulstoned"]
elseif UnitIsDead(unit) then
    return L["Dead"]
end
return L["Alive"]
]],
        enabled = true,
    },
    [13] = {
        name = L["Health"],
        left = 'return L["Health:"]',
        right = [[
if not UnitExists(unit) then self:Stop(); return self.lastHealth end
local health, maxHealth = HP(unit), MaxHP(unit)
local r, g, b = HPColor(health, maxHealth)
local value = L["Unknown"]
if maxHealth == 100 then
    value = Colorize(health .. "%", r, g, b)
elseif maxHealth ~= 0 then
    value = Colorize(format("%s/%s (%d%%)", Short(health, true), Short(maxHealth, true), health/maxHealth*100), r, g, b)
end
self.lastHealth = value
return value
]],
        rightUpdating = true,
        update = 1000,
        enabled = true
    },
    [14] = {
        name = L["Mana"],
        left = [[
return PowerName(unit)
]],
        right = [[
if not UnitExists(unit) then self:Stop(); return self.lastMana end
local mana = Power(unit)
local maxMana = MaxPower(unit)
local r, g, b = PowerColor(nil, unit)
local h, s, v = RGB2HSV(r, g, b)
s = .5
r, g, b = HSV2RGB(h, s, v)
local value = L["Unknown"]
if maxMana == 100 or maxMana == 120 then
    value = Colorize(tostring(mana), r, g, b)
elseif maxMana ~= 0 then
    value = Colorize(format("%s/%s (%d%%)", Short(mana, true), Short(maxMana, true), mana/maxMana*100), r, g, b)
end
self.lastMana = value
return value
]],
        rightUpdating = true,
        enabled = true,
        update = 1000,
	colorR = "",
    },
    [15] = {
        name = L["Effects"],
        left = 'return L["Effects:"]',
        right = [[
local name = Name(unit)
local str = ""
if UnitIsBanished(unit) then
    str = str .. Angle(L["Banished"])
end
if UnitIsCharmed(unit) then
    str = str .. Angle(L["Charmed"])
end
if UnitIsConfused(unit) then
    str = str .. Angle(L["Confused"])
end
if UnitIsDisoriented(unit) then
    str = str .. Angle(L["Disoriented"])
end
if UnitIsFeared(unit) then
    str = str .. Angle(L["Feared"])
end
if UnitIsFrozen(unit) then
    str = str .. Angle(L["Frozen"])
end
if UnitIsHorrified(unit) then
    str = str .. Angle(L["Horrified"])
end
if UnitIsIncapacitated(unit) then
    str = str .. Angle(L["Incapacitated"])
end
if UnitIsPolymorphed(unit) then
    str = str .. Angle(L["Polymorphed"])
end
if UnitIsSapped(unit) then
    str = str .. Angle(L["Sapped"])
end
if UnitIsShackled(unit) then
    str = str .. Angle(L["Shackled"])
end
if UnitIsAsleep(unit) then
    str = str .. Angle(L["Asleep"])
end
if UnitIsStunned(unit) then
    str = str .. Angle(L["Stunned"])
end
if UnitIsTurned(unit) then
    str = str .. Angle(L["Turned"])
end
if UnitIsDisarmed(unit) then
    str = str .. Angle(L["Disarmed"])
end
if UnitIsPacified(unit) then
    str = str .. Angle(L["Pacified"])
end
if UnitIsRooted(unit) then
    str = str .. Angle(L["Rooted"])
end
if UnitIsSilenced(unit) then
    str = str .. Angle(L["Silenced"])
end
if UnitIsEnsnared(unit) then
    str = str .. Angle(L["Ensnared"])
end
if UnitIsEnraged(unit) then
    str = str .. Angle(L["Enraged"])
end
if UnitIsWounded(unit) then
    str = str .. Angle(L["Wounded"])
end
if str == "" then
    return L["Has Control"] .. " "
else
    return str .. " "
end
]],
        rightUpdating = true,
        enabled = true,
        update = 500,
    },
    [16] = {
        name = L["Marquee"],
        left = 'return "StarTip " .. StarTip.version',
        leftUpdating = true,
        enabled = false,
        marquee = true,
        cols = 40,
        bold = true,
        align = WidgetText.ALIGN_MARQUEE,
        update = 1000,
        speed = 200,
        direction = WidgetText.SCROLL_LEFT,
        dontRtrim = true,
	colorL = "return random(), random(), random(), 1"
    },
    [17] = {
        name = L["Memory Usage"],
        left = "return L['Memory Usage:']",
        right = [[
local mem, percent, memdiff, totalMem, totaldiff, memperc = GetMemUsage("StarTip", true)
if mem then
    local num = floor(memperc)
    if num < 1 then num = 1 end
    if num > 100 then num = 100 end
    local r, g, b = gradient[num][1], gradient[num][2], gradient[num][3]
    return Colorize(format("%s (%.2f%%)", memshort(mem), memperc), r, g, b)
end
]],
        rightUpdating = true,
        update = 1000
    },
    [18] = {
        name = L["CPU Usage"],
        desc = L["Note that you must turn on CPU profiling"],
        left = 'return "CPU Usage:"',
        right = [[
local cpu, percent, cpudiff, totalCPU, totaldiff, cpuperc = GetCPUUsage("StarTip", true)
if cpu then
    local num = floor(cpuperc)
    if num < 1 then num = 1 end
    if num > 100 then num = 100 end
    local r, g, b = gradient[num][1], gradient[num][2], gradient[num][3]
    return Colorize(format("%s (%.2f%%)", timeshort(cpu), cpuperc), r, g, b)
end
]],
        rightUpdating = true,
        update = 1000
    },
    [19] = {
        name = L["Talents"],
        left = "return L['Talents:']",
        right = [[
if not UnitExists(unit) then return lastTalents end
local str = SpecText(unit)
local ilvl = UnitILevel(unit, true)
if ilvl then
    str = format("%s (%s ilvl)", str, ilvl)
end
lastTalents = str
return str
]],
        rightUpdating = true,
        enabled = true,
        cols = 180,
        update = 1000
    },
    [20] = {
        name = "Current Role",
        left = [[
return "Current Role:"
]],
        right = [[
return GetRole(unit)
]],
        rightUpdating = true,
        enabled = true,
        update = 1000,
        deleted = true
    },
    [21] = {
        name = "Old Role",
        left = [[
return "Old Role:"
]],
        right = [[
return select(2, GetRole(unit))
]],
        rightUpdating = true,
        enabled = true,
        update = 1000,
        deleted = true
    },
    [22] = {
        name = "Avg Item Level",
        left = [[
if not UnitExists(unit) then return "" end
return "Item Level:"
]],
        right = [[
if not UnitExists(unit) then return "" end
return UnitILevel(unit)
]],
        rightUpdating = true,
        enabled = true,
        update = 1000,
        deleted = true
    },
    [23] = {
        name = L["Zone"],
        left = [[
-- This doesn't work. Leaving it here for now.
return L["Zone:"]
]],
        right = [[
return select(6, UnitGuildInfo(unit))
]],
        enabled = false
    },
    [24] = {
        name = L["Location"],
        left = [[
return L["Location:"]
]],
        right = [[
return select(3, GetUnitTooltipScan(unit))
]],
        enabled = true
    },
    [25] = {
        name = L["Range"],
        left = [[
if not UnitExists(unit) then return lastRange end
local min, max = RangeCheck:GetRange(unit)
local str
if not min then
    str = ""
elseif not max then
    str = format(L["Target is over %d yards"], min)
else
    str = format(L["Between %s and %s yards"], min, max)
end
lastRange = str
return str
]],
        leftUpdating = true,
        enabled = true,
        update = 500
    },
    [26] = {
        name = L["Movement"],
        left = [[
if not UnitExists(unit) then return "" end
local pitch = GetUnitPitch(unit)
local speed = GetUnitSpeed(unit)
local str = ""
if abs(pitch) > .01 then
    str = format("Pitch: %.1f", pitch)
end
if speed > 0 then
    if str ~= "" then
        str = str .. " - "
    end
    str = str .. format("Speed: %.1f", speed)
end
if str == "" then return "blank()" end
return str
]],
        leftUpdating = true,
        enabled = true,
        update = 500
    },
    [27] = {
        name = L["Guild Note"],
        left = [[
return L["Guild Note:"]
]],
        right = [[
return select(7, UnitGuildInfo(unit))
]],
        enabled = true
    },
    [28] = {
        name = L["Main"],
        left = [[
-- This requires Chatter
return L["Main:"]
]],
        right = [[
if not _G.Chatter then return end
local mod = _G.Chatter:GetModule("Alt Linking")
local name = UnitName(unit)
return mod.db.realm[name]
]],
        enabled = true
    },
    [29] = {
        name = "Recount",
        left = [[
return "Recount:"
]],
right = [[
local val, perc, persec, maxvalue, total = RecountUnitData(unit)
if val and val ~= 0 then
    local p = total ~= 0 and (val / maxvalue) or 1
    local r, g, b = Gradient(p)
    local prefix=""
    if persec then
        prefix = persec .. ", "
    end
    return Colorize(string.format("%d (%s%d%%)", val, prefix, perc), r, g, b)
end
]],
        enabled = true,
        rightUpdating = true,
        update = 1000
    },
    [30] = {
        name = "DPS",
        left = [[
return "DPS:"
]],
        right = [[
return UnitDPS(unit)
]],
        enabled = true,
        rightUpdating = true,
        update = 1000
    },
    [31] = {
        name = "Skada DPS",
        left = [[
return "Skada DPS:"
]],
        right = [[
local dps = SkadaUnitDPS(unit)
if dps then
    return format("%d", dps)
end
]],
        enabled = true,
        rightUpdating = true,
        update = 1000
    },
    [32] = {
        name = L["Spell Cast"],
        left = [[
local cast_data = CastData(unit)
if cast_data then
    if cast_data.channeling then
        return L["Channeling:"]
    end
    return L["Casting:"]
end
return ""
]],
        right = [[
local cast_data = CastData(unit)
if cast_data then
  local spell,stop_message,target = cast_data.spell,cast_data.stop_message,cast_data.target
  local stop_time,stop_duration = cast_data.stop_time
  local i = -1
  if cast_data.casting then
    local start_time = cast_data.start_time
    i = (GetTime() - start_time) / (cast_data.end_time - start_time) * 100
  elseif cast_data.channeling then
    local end_time = cast_data.end_time
    i = (end_time - GetTime()) / (end_time - cast_data.start_time) * 100
  end
  local icon = Texture(format("Interface\\Addons\\StarTip\\Media\\gradient\\gradient-%d.blp", i), 12) .. " "

  if stop_time then
    stop_duration = GetTime() - stop_time
  end
  Alpha(-(stop_duration or 0) + 1)

  if stop_message then
    return stop_message
  elseif target then
    return icon .. format("%s (%s)",spell,target)
  else
    return icon .. spell
  end
end
]],
        enabled = true,
        cols = 100,
        rightUpdating = true,
        update = 500
    },
    [33] = {
        name = L["Fails"],
        left = [[
local fails = NumFails(unit)
if fails and fails > 0 then
  return format(L["Fails: %d"], fails)
end
]],
        enabled = true
    },
    [34] = {
        name = L["Threat"],
        left = [[
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(unit, "target")
if not threatpct then return "" end
isTanking = isTanking and 0 or 1
return Colorize(format("%s: %d%% (%.2f%%)", L["Threat"], threatpct, rawthreatpct), 1, isTanking, isTanking)
]],
        enabled = true,
        update = 300,
        leftUpdating = true
    },
    [35] = {
        name = L["Feats"],
        left = [[
return L["Feats:"];
]],
        right = [[
if UnitExists(unit) and not UnitIsPlayer(unit) then return end
if not UnitExists(unit) then return lastFeats end
local feats = UnitFeats(unit)
local txt
if feats and feats > 0 then
    self:Stop()
    txt = feats
else
    txt = L["Loading Achievements..."]
end
lastFeats = txt
return txt
]],
        enabled = true,
        update = 500,
        rightUpdating = true
    },
    [36] = {
        name = L["PVP Rank"],
        left = [[
return L["PVP Rank:"];
]],
		right = [[
if UnitExists(unit) and not UnitIsPlayer(unit) then return end
if not UnitExists(unit) then return lastPVPRank end
lastPVPRank = PVPRank(unit)
return lastPVPRank
]],
        enabled = true,
        update = 300,
        rightUpdating = true,
        cols = 50
    },
    [37] = {
        name = L["Arena 2s"],
		left = [[
if not UnitExists(unit) then return lastArena2 end
lastArena2 =  ArenaTeam(unit, 2)
return lastArena2
]],
        enabled = true,
        update = 300,
        leftUpdating = true,
        cols = 100
    },
    [38] = {
        name = L["Arena 3s"],
		left = [[
if not UnitExists(unit) then return lastArena3 end
lastArena3 = ArenaTeam(unit, 3)
return lastArena3
]],
        enabled = true,
        update = 300,
        leftUpdating = true,
        cols = 100
    },
    [39] = {
        name = L["Arena 5s"],
		left = [[
if not UnitExists(unit) then return lastArena5 end
lastArena5 = ArenaTeam(unit, 5)
return lastArena5
]],
        enabled = true,
        update = 300,
        leftUpdating = true,
        cols = 100
    },
    [40] = {
        name = L["Raid Group"],
        enabled = true,
        right = [[
if UnitInRaid(unit) then
 local size = GetNumRaidMembers()
 local uname=Name(unit)
 for i = 1, size do
  local name,_,subgroup = GetRaidRosterInfo(i)
  if uname==name then
   return format("Raid Group: %s", subgroup)
  end
 end
end
]],
    }
}
for i, v in ipairs(defaultLines) do
    v.default = true
end
local options = {}
function mod:ReInit()
    self:ClearLines()
    for k, v in ipairs(defaultLines) do
        for j, vv in ipairs(self.db.profile.lines) do
            vv.colorLeft = nil
            vv.colorRight = nil
            if v.name == vv.name then
                for k, val in pairs(v) do
                    if v[k] ~= vv[k] and not vv[k.."Dirty"] then
                        vv[k] = v[k]
                    end
                end
                v.tagged = true
                v.default = true
            end
        end
    end
    for k, v in ipairs(defaultLines) do
        if not v.tagged then
            tinsert(self.db.profile.lines, copy(v))
        end
    end
    self:CreateLines()
end
function mod:OnInitialize()
    self.db = StarTip.db:RegisterNamespace(self:GetName(), defaults)
    self.leftLines = StarTip.leftLines
    self.rightLines = StarTip.rightLines
    self:RegisterEvent("UPDATE_FACTION")
    StarTip:SetOptionsDisabled(options, true)
    self.core = StarTip.core
    self.evaluator = LibEvaluator
    self:ReInit()
end

local draw
local update
function mod:OnEnable()
    StarTip:SetOptionsDisabled(options, false)
    self.timer = LibTimer:New("Text module", self.db.profile.refreshRate, true, draw, nil, self.db.profile.errorLevel)
    self.trunkTimer = LibTimer:New("Text trunk timer", 500, true, self.AppendTrunk, self, self.db.profile.errorLevel)
end

function mod:OnDisable()
    StarTip:SetOptionsDisabled(options, true)
    self.timer:Del()
    self.trunkTimer:Del()
end

function mod:GetOptions()
    self:RebuildOpts()
    return options
end

function mod:UPDATE_FACTION()
    for i = 1, GetNumFactions() do
        local name = GetFactionInfo(i)
        factionList[name] = true
    end
end

local widgetUpdate
do
    local widgetsToDraw = {}
    function widgetUpdate(widget)
	tinsert(widgetsToDraw, widget)
	if mod.db.profile.refreshRate == 0 then
		draw(true)
	end
    end
    --local fontsList = LSM:List("font")
    function draw(bypass)
        if not UnitExists(StarTip.unit) then
		mod.timer:Stop()
		mod.trunkTimer:Stop()
		StarTip.tooltipMain:Hide()
		return
	end
--[[
        if #StarTip.trunk > 0 and not bypass then
            wipe(widgetsToDraw)
            for k, v in pairs(lines) do
                if v.leftObj then
    	             v.leftObj:Update()
            	     tinsert(widgetsToDraw, v.leftObj)
                end
                if v.rightObj then
    		    v.rightObj:Update()
                    tinsert(widgetsToDraw, v.rightObj)
                end
            end
        end
]]
        for i, widget in ipairs(widgetsToDraw) do
            local font = LSM:Fetch("font", appearance.db.profile.font)
            local headerFont = LSM:Fetch("font", appearance.db.profile.headerFont)
            local justification = "LEFT"
            if widget.x == 2 then
                justification = "RIGHT"
            end
            local outlined = ""
            if widget.config.outlined and widget.config.outlined > 1 then
                if widget.config.outlined == 2 then
			outlined = "OUTLINE"
                elseif widget.config.outlined == 3 then
			outline = "THICKOUTLINE"
                end
            end
            if widget.y == 1 then
		widget.fontObj:SetFont(headerFont, appearance.db.profile.fontSizeHeader, outlined)
            else
                widget.fontObj:SetFont(font, appearance.db.profile.fontSizeNormal, outlined)
            end
            local colSpan = 1
            if not widget.config.right and widget.x == 1 then
               colSpan = 2
            end
            if type(widget.y) == "number" and widget.y <= StarTip.tooltipMain:GetLineCount() and (widget.buffer == "blank()" or widget.buffer ~= "") then
		if widget.buffer == "blank()" then widget.buffer = "" end
                StarTip.tooltipMain:SetCell(widget.y, widget.x, widget.buffer, widget.fontObj,
			justification, colSpan, nil, 0, 0, nil, nil, 40)

		if type(widget.config.color) == "string" and (widget.config.color == " " or widget.config.color ~= "") and widget.color.is_valid then
			widget.color:Eval()
			local r, g, b, a = widget.color:P2N()
			StarTip.tooltipMain:SetCellColor(widget.y, widget.x,
        	            r or 0, g or 0, b or 0, a or 1)
		end
            end
        end
        table.wipe(widgetsToDraw)
    end
end

function mod:AppendTrunk()
	for i, v in ipairs(StarTip.trunk) do
		if #v == 2 then
			local y = StarTip.tooltipMain:AddLine('', '')
			StarTip.tooltipMain:SetCell(y, 1, v[1])
			StarTip.tooltipMain:SetCell(y, 2, v[2])
		else
			local y = StarTip.tooltipMain:AddLine('')
			StarTip.tooltipMain:SetCell(y, 1, v[1], nil, "LEFT", 2)
		end
	end
	StarTip:TrunkClear()
end

function mod:StopLines()
    for k, v in pairs(lines) do
        if v.leftObj then
            v.leftObj:Stop()
        end
        if v.rightObj then
            v.rightObj:Stop()
        end
    end

end

function mod:ClearLines()
    self:StopLines()
    wipe(lines)
end

local tbl
function mod:CreateLines()
    local llines = {}
    local j = 0
    for i, v in ipairs(self.db.profile.lines) do
        if not v.deleted and v.enabled and v.left then
            v = copy(v)
            j = j + 1
            llines[j] = copy(v)
            llines[j].config = copy(v)

            v.value = v.left
            v.outlined = v.leftOutlined
            v.color = v.colorL
            v.maxWidth = v.maxWidthL
            v.minWidth = v.minWidthL
            local update = v.update or 0
            v.update = 0
            if v.left and v.leftUpdating then v.update = update end
            mod.core.environment.unit = StarTip.unit or "player"
            llines[j].leftObj = v.left and WidgetText:New(mod.core, "StarTip.UnitTooltip:" .. v.name .. ":left:", copy(v), 0, 0, v.layer or 0, StarTip.db.profile.errorLevel, widgetUpdate)

            v.value = v.right
            v.outlined = v.rightOutlined
            v.update = 0
            if v.right and v.rightUpdating then v.update = update end
            v.color = v.colorR
            v.maxWidth = v.maxWidthR
            v.minWidth = v.minWidthR
            llines[j].rightObj = v.right and WidgetText:New(mod.core, "StarTip.UnitTooltip:" .. v.name .. ":right:", copy(v), 0, 0, v.layer or 0, StarTip.db.profile.errorLevel, widgetUpdate)

           if v.left then
               llines[j].leftObj.fontObj = _G[v.name .. "Left"] or CreateFont(v.name .. "Left")
           end
           if v.right then
               llines[j].rightObj.fontObj = _G[v.name .. "Right"] or CreateFont(v.name .. "Right")
           end
        end
    end
    self:ClearLines()
    lines = setmetatable(llines, {__call=function(self)
            local lineNum = 0
            StarTip.tooltipMain:Clear()
            --GameTooltip:ClearLines()
            for i, v in ipairs(self) do
                if v.leftObj then
                    v.leftObj.x = nil
                    v.leftObj.y = nil
                end
                if v.rightObj then
                    v.rightObj.x = nil
                    v.rightObj.y = nil
                end
                local left, right = '', ''
                environment.unit = v.leftObj and v.leftObj.unitOverride or StarTip.unit or "mouseover"
                if v.right then
                    if v.rightObj then
                        environment.self = v.rightObj
                        right = mod.evaluator.ExecuteCode(environment, v.name .. " right", v.right)
                        if type(right) == "number" then right = right .. "" end
                    end
                    if v.leftObj then
                        environment.self = v.leftObj
                        left = mod.evaluator.ExecuteCode(environment, v.name .. " left", v.left)
                        if type(left) == "number" then left = left .. "" end
                    end
                else
                    if v.leftObj then
                        environment.self = v.leftObj
                        left = mod.evaluator.ExecuteCode(environment, v.name .. " left", v.left)
                        if type(left) == "number" then left = left .. "" end
                    end
                    right = ''
                end

                if type(left) == "string" and type(right) == "string" then
                    lineNum = lineNum + 1
                    if v.right and v.right ~= "" then
                        --GameTooltip:AddDoubleLine(' ', ' ', mod.db.profile.color.r, mod.db.profile.color.g, mod.db.profile.color.b, mod.db.profile.color.r, mod.db.profile.color.g, mod.db.profile.color.b)
                        local y, x = StarTip.tooltipMain:AddLine('', '')
                        --v.leftObj.fontString = mod.leftLines[lineNum]
                        --v.rightObj.fontString = mod.rightLines[lineNum]
			--v.leftObj.fontString = StarTip.qtipLines[y][1]
			--v.rightObj.fontString = StarTip.qtipLines[y][2]
			v.leftObj.y = y
			v.leftObj.x = 1
			v.rightObj.y = y
			v.rightObj.x = 2
                    else
                        local y, x = StarTip.tooltipMain:AddLine('')
                        v.leftObj.y = y
                        v.leftObj.x = 1
                        --GameTooltip:AddLine(' ', mod.db.profile.color.r, mod.db.profile.color.g, mod.db.profile.color.b, v.wordwrap)
                        --v.leftObj.fontString = mod.leftLines[lineNum]
                    end
                    if v.rightObj then
			v.rightObj.buffer = false
                        v.rightObj:Start()
                    end
                    if v.leftObj then
			v.leftObj.buffer = false
                        v.leftObj:Start()
                    end
                    v.lineNum = lineNum
                end
            end
    end})
end

function mod:OnHide()
    self:StopLines()
    self.timer:Stop()
    self.trunkTimer:Stop()
end

function mod:OnFadeOut()
    self:OnHide()
end

local function escape(text)
    return string.gsub(text, "|","||")
end

local function unescape(text)
    return string.gsub(text, "||", "|")
end

function mod:GetNames()
    local new = {}
    for i, v in ipairs(self.db.profile.lines) do
        new[i] = v.name
    end
    return new
end

function mod:RebuildOpts()
    options = {
        add = {
            name = L["Add Line"],
            desc = L["Give the line a name"],
            type = "input",
            validate = function(info, v)
                for k, line in pairs(self.db.profile.lines) do
			if line.name == v and not v.deleted then
				StarTip:Print(format(L["A line named %s already exists."], v))
				return false
			end
		end
		return true
            end,
            set = function(info, v)
                if v == "" then return end
		local key
		for k, v in pairs(self.db.profile.lines) do
			if v.name == v then
				key = k
				break
			end
		end
		if key then tremove(self.db.profile.lines, key) end
                tinsert(self.db.profile.lines, {name = v, left = nil, right = nil, update=500, enabled = true, custom=true})
                self:RebuildOpts()
                StarTip:RebuildOpts()
                self:ClearLines()
                self:CreateLines()
            end,
            order = 5
        },
        refreshRate = {
            name = L["Refresh Rate"],
            desc = L["The rate at which the tooltip will be refreshed"],
            type = "input",
            pattern = "%d",
            get = function() return tostring(self.db.profile.refreshRate) end,
            set = function(info, v)
                self.db.profile.refreshRate = tonumber(v)
                self:OnDisable()
                self:OnEnable()
            end,
            order = 6
        },
        color = {
            name = L["Default Color"],
            desc = L["The default color for tooltip lines"],
            type = "color",
            get = function() return self.db.profile.color.r, self.db.profile.color.g, self.db.profile.color.b end,
            set = function(info, r, g, b)
                self.db.profile.color.r = r
                self.db.profile.color.g = g
                self.db.profile.color.b = b
            end,
            order = 7
        },
        defaults = {
            name = L["Restore Defaults"],
            desc = L["Roll back to defaults."],
            type = "execute",
            func = function()
                local replace = {}
                for i, v in ipairs(self.db.profile.lines) do
                    local insert = true
                    for j, vv in ipairs(defaultLines) do
                        if v.name == vv.name then
                            insert = false
                        end
                    end
                    if insert then
                        tinsert(replace, v)
                    end
                end
                table.wipe(self.db.profile.lines)
                for i, v in ipairs(defaultLines) do
                    tinsert(self.db.profile.lines, copy(v))
                end
                for i, v in ipairs(replace) do
                    tinsert(self.db.profile.lines, copy(v))
                end
                StarTip:RebuildOpts()
                self:CreateLines()
            end,
            order = 9
        },
    }
    for i, v in ipairs(self.db.profile.lines) do
        if type(v) == "table" and not v.deleted then
            options["line" .. i] = {
                name = v.name,
                type = "group",
                order = i + 5
            }
            options["line" .. i].args = {
                    enabled = {
                        name = L["Enabled"],
                        desc = L["Whether to show this line or not"],
                        type = "toggle",
                        get = function() return self.db.profile.lines[i].enabled end,
                        set = function(info, val)
                            v.enabled = val
                            v.enabledDirty = true
                            self:CreateLines()
                        end,
                        order = 2
                    },
                    leftUpdating = {
                        name = L["Left Updating"],
                        desc = L["Whether this line's left segment refreshes"],
                        type = "toggle",
                        get = function() return v.leftUpdating end,
                        set = function(info, val)
                            v.leftUpdating = val
                            v.leftUpdatingDirty = true
                            self:CreateLines()
                        end,
                        order = 3
                    },
                    rightUpdating = {
                        name = L["Right Updating"],
                        desc = L["Whether this line's right segment refreshes"],
                        type = "toggle",
                        get = function() return v.rightUpdating end,
                        set = function(info, val)
                            v.rightUpdating = val
                            v.rightUpdatingDirty = true
                            self:CreateLines()
                        end,
                        order = 4
                    },
                    up = {
                        name = L["Move Up"],
                        desc = L["Move this line up by one"],
                        type = "execute",
                        func = function()
                            if i == 1 then return end
                            local tmp = self.db.profile.lines[i - 1]
                            if not v.left then v.left = "" end
                            if not v.right then v.right = "" end
                            if not tmp.left then tmp.left = "" end
                            if not tmp.right then tmp.right = "" end
                            self.db.profile.lines[i - 1] = v
                            self.db.profile.lines[i] = tmp
                            self:RebuildOpts()
                            StarTip:RebuildOpts()
                            self:CreateLines()
                        end,
                        order = 5
                    },
                    down = {
                        name = L["Move Down"],
                        desc = L["Move this line down by one"],
                        type = "execute",
                        func = function()
                            if i == #self.db.profile.lines then return end
                            local tmp = self.db.profile.lines[i + 1]
                            if tmp.deleted then return end
                            if not v.left then v.left = "" end
                            if not v.right then v.right = "" end
                            if not tmp.left then tmp.left = "" end
                            if not tmp.right then tmp.right = "" end
                            self.db.profile.lines[i + 1] = v
                            self.db.profile.lines[i] = tmp
                            self:RebuildOpts()
                            StarTip:RebuildOpts()
                            self:CreateLines()
                        end,
                        order = 6
                    },
                    --[[bold = {
                        name = "Bold",
                        desc = "Whether to bold this line or not",
                        type = "toggle",
                        get = function() return self.db.profile.lines[i].bold end,
                        set = function(info, val)
                            v.bold = val
                            v.boldDirty = true
                            self:CreateLines()
                        end,
                        order = 7
                    },]]
                    leftOutlined = {
                        name = L["Left Outlined"],
                        desc = L["Whether the left widget is outlined or not"],
                        type = "select",
                        values = {L["None"], L["Outlined"], L["Thick Outlilned"]},
                        get = function() return v.leftOutlined or 1 end,
                        set = function(info, val)
                            v.leftOutlined = val
                            v.leftOutlinedDirty = true
                            self:CreateLines()
                        end,
                        order = 8
                    },
                    rightOutlined = {
                        name = L["Right Outlined"],
                        desc = L["Whether the right widget is outlined or not"],
                        type = "select",
                        values = {L["None"], L["Outlined"], L["Thick Outlilned"]},
                        get = function() return v.rightOutlined or 1 end,
                        set = function(info, val)
                            v.rightOutlined = val
                            v.rightOutlinedDirty = true
                            self:CreateLines()
                        end,
                        order = 9
                    },
                    updateAnyways = {
			name = L["Update Anyways"],
			desc = L["Line segments won't update if the text never changes. Enable this option to bypass this restriction"],
			type = "toggle",
			get = function() return v.updateAnyways end,
			set = function(info, val)
				v.updateAnyways = val
				v.updateAnywayDirty = true
				self:CreateLines()
			end,
			order = 10
                    },
                    dogtag = {
                        name = L["Dog Tags"],
                        desc = L["Whether to parse the return values as DogTags or not."],
                        type = "toggle",
                        get = function() return v.dogtag end,
                        set = function(info, val)
                            v.dogtag = val
                            v.dogtagDIrty = true
                            self:CreateLines()
                        end,
                        order = 11
                    },
                    unitOverride = {
                        name = L["Unit Override"],
                        desc = L["There's a default unit provided to each run environment, and you access it with 'unit' in your script. These \"units\" include 'mouseover', 'target', 'party1', etc... Here you can override that unit specifier."],
			type = "input",
			get = function() return v.unitOverride end,
			set = function(info, val)
				v.unitOverride = val
				v.unitOverrideDirty = true
				self:CreateLines()
			end,
			order = 12
                    },
--[[
                    wordwrap = {
                        name = L["Word Wrap"],
                        desc = L["Whether this line should word wrap lengthy text"],
                        type = "toggle",
                        get = function()
                            return v.wordwrap
                        end,
                        set = function(info, val)
                            v.wordwrap = val
                        end,
                        order = 10
                    },
]]

                    delete = {
                        name = L["Delete"],
                        desc = L["Delete this line"],
                        type = "execute",
                        func = function()
                            local name = v.name
                            local delete = true
                            for i, line in ipairs(defaultLines) do
                                if line.name == name then
                                    delete = false
                                end
                            end
                            tremove(self.db.profile.lines, i)
                            if not delete then
                                wipe(v)
                                v.deleted = true
                                v.name = name
                                tinsert(self.db.profile.lines, v)
                            end
                            StarTip:RebuildOpts()
                            self:ClearLines()
                            self:CreateLines()
                        end,
                        order = 100
                    },
                    linesHeader = {
                        name = L["Lines"],
                        type = "header",
                        order = 101
                    },
                    left = {
                        name = L["Left Segment"],
                        type = "input",
                        desc = L["Enter code for this line's left segment."],
                        get = function() return escape(v.left or "") end,
                        set = function(info, val)
                            v.left = unescape(val)
                            v.leftDirty = true
                            if val == "" then v.left = nil end
                            self:CreateLines()
                        end,
                        --[[validate = function(info, str)
                            return mod.evaluator:Validate(environment, str)
                        end,]]
                        multiline = true,
                        width = "full",
                        order = 113
                    },
                    right = {
                        name = L["Right Segment"],
                        type = "input",
                        desc = L["Enter code for this line's right segment."],
                        get = function() return escape(v.right or "") end,
                        set = function(info, val)
                            v.right = unescape(val);
                            v.rightDirty = true
                            if val == "" then v.right = nil end
                            self:CreateLines()
                        end,
                        multiline = true,
                        width = "full",
                        order = 114
                    },
                    colorL = {
                        name = L["Left Color"],
                        desc = L["Background color for left segment. Return r, g, b, and a."],
			type = "input",
                        get = function()
				return escape(v.colorL or "")
                        end,
                        set = function(info, val)
                            v.colorL = unescape(val)
                            self:CreateLines()
                        end,
			multiline = true,
			width = "full",
                        order = 115
                    },
                    colorR = {
                        name = L["Right Color"],
                        desc = L["Background color for right segment. Return r, g, b, and a."],
			type = "input",
                        get = function()
				return escape(v.colorR or "")
                        end,
                        set = function(info, val)
                            v.colorR = unescape(val)
                            self:CreateLines()
                        end,
			multiline = true,
			width = "full",
                        order = 116
                    },

                    marquee = {
                        name = "Marquee Settings",
                        type = "group",
                        args = {
                            header = {
                                name = L["Note that only the left line script is used for marquee text"],
                                type = "header",
                                order = 1
                            },
                            prefix = {
                                name = L["Prefix"],
                                desc = L["The prefix for this marquee"],
                                type = "input",
                                width = "full",
                                multiline = true,
                                get = function()
                                    return v.prefix
                                end,
                                set = function(info, val)
                                    v.prefix = val
                                    v.prefixDirty = true
                                    self:CreateLines()
                                end,
                                order = 2
                            },
                            postfix = {
                                name = "Postfix",
                                desc = L["The postfix for this marquee"],
                                type = "input",
                                width = "full",
                                multiline = true,
                                get = function()
                                    return v.postfix or WidgetText.defaults.postfix
                                end,
                                set = function(info, val)
                                    v.postfix = v
                                    v.postfixDirty = true
                                    self:CreateLines()
                                end,
                                order = 3
                            },
                            --[[precision = {
                                name = "Precision",
                                desc = L["How precise displayed numbers are"],
                                type = "input",
                                pattern = "%d",
                                get = function()
                                    return tostring(v.precision or WidgetText.defaults.precision)
                                end,
                                set = function(info, val)
                                    v.precision = tonumber(val)
                                    v.precisionDirty = true
                                    self:CreateLines()
                                end,
                                order = 4
                            },]]
                            align = {
                                name = L["Alignment"],
                                desc = L["The alignment information"],
                                type = "select",
                                values = WidgetText.alignmentList,
                                get = function()
                                    return v.align or WidgetText.defaults.align
                                end,
                                set = function(info, val)
                                    v.align = val
                                    v.alignDirty = true
                                    self:CreateLines()
                                end,
                                order = 5
                            },
                            update = {
                                name = L["Text Update"],
                                desc = L["How often to update the text. A value of zero means the text won't repeatedly update."],
                                type = "input",
                                pattern = "%d",
                                get = function()
                                    return tostring(v.update or WidgetText.defaults.update)
                                end,
                                set = function(info, val)
print("lolol")
                                    v.update = tonumber(val)
                                    v.updateDirty = true
                                    self:CreateLines()
                                end,
                                order = 6
                            },
                            speed = {
                                name = L["Scroll Speed"],
                                desc = L["How fast to scroll marquee text."],
                                type = "input",
                                pattern = "%d",
                                get = function()
                                    return tostring(v.speed or WidgetText.defaults.speed)
                                end,
                                set = function(info, val)
                                    v.speed = tonumber(val)
                                    v.speedDirty = true
                                    self:CreateLines()
                                end,
                                order = 7
                            },
                            direction = {
                                name = L["Direction"],
                                desc = L["Which direction to scroll."],
                                type = "select",
                                values = WidgetText.directionList,
                                get = function()
                                    return v.direction or WidgetText.defaults.direction
                                end,
                                set = function(info, val)
                                    v.direction = val
                                    v.directionDirty = true
                                    self:CreateLines()
                                end,
                                order = 8
                            },
                            cols = {
                                name = L["Columns"],
                                desc = L["How wide the marquee is. If your text is cut short then increase this value."],
                                type = "input",
                                pattern = "%d",
                                get = function()
                                    return tostring(v.cols or WidgetText.defaults.cols)
                                end,
                                set = function(info, val)
                                    v.cols = tonumber(val)
                                    v.colsDirty = true
                                    self:CreateLines()
                                end,
                                order = 9
                            },
                            dontRtrim = {
                                name = L["Don't right trim"],
                                desc = L["Prevent trimming white space to the right of text"],
                                type = "toggle",
                                get = function()
                                    return v.dontRtrim or WidgetText.defaults.dontRtrim
                                end,
                                set = function(info, val)
                                    v.dontRtrim = val
                                    v.dontRtrimDirty = true
                                    self:CreateLines()
                                end,
                                order = 10
                            },
                            limited = {
                                name = L["Clip Length"],
                                desc = L["Whether to clip the string's length when it is longer than the value of Columns."],
                                type = "toggle",
                                get = function()
                                    return v.limited or WidgetText.defaults.limited
                                end,
                                set = function(info, val)
                                    v.limited = val
                                    v.limitedDirty = true
                                    sel:CreateLines()
                                end,
                                order = 11
                            }
                        },
                        order = 9
                    }
            }
        end
        --[[if v.desc then
            options["line" .. i].args.desc = {
                name = v.desc,
                type = "header",
                order = 1
            }
        end]]
    end
end
local plugin = LibStub("LibScriptablePluginString-1.0")
local ff = CreateFrame("Frame")
function mod:SetUnit()
    if ff:GetScript("OnUpdate") then ff:SetScript("OnUpdate", nil) end
    --self.NUM_LINES = 0
    -- Taken from CowTip
    local lastLine = 2
    local text2 = self.leftLines[2]:GetText()
    if not text2 then
        lastLine = lastLine - 1
    elseif not text2:find("^"..LEVEL) then
        lastLine = lastLine + 1
    end
    if not UnitPlayerControlled(StarTip.unit) and not UnitIsPlayer(StarTip.unit) then
        local factionText = self.leftLines[lastLine + 1]:GetText()
        if factionText == PVP then
            factionText = nil
        end
        if factionText and (factionList[factionText] or UnitFactionGroup(StarTip.unit)) then
            lastLine = lastLine + 1
        end
    end
    if not UnitIsConnected(StarTip.unit) or not UnitIsVisible(StarTip.unit) or UnitIsPVP(StarTip.unit) then
        lastLine = lastLine + 1
    end
    lastLine = lastLine + 1
    for i = lastLine, GameTooltip:NumLines() do
        local left = self.leftLines[i]
        local right = self.rightLines[i]
        local txt1 = left:GetText()
        local r1, g1, b1 = left:GetTextColor()
	local txt2
	local r2, g2, b2
	if right:IsShown() then
		txt2 = right:GetText()
		r2, g2, b2 = right:GetTextColor()
	end
	StarTip:TrunkAdd(txt1, r1, g1, b1, txt2, r2, g2, b2)
    end

    mod:StopLines()
    lines()
    mod:AppendTrunk()

    if mod.db.profile.refreshRate ~= 0 then
	self.timer:Start()
    end
    self.trunkTimer:Start()
end

--[[
function mod:RefixEndLines()
    -- Another part taken from CowTip
    for i, left in ipairs(linesToAdd) do
        local left = linesToAdd[i]
        local right = linesToAddRight[i]
        StarTip.addingLine = true
        if right then
            GameTooltip:AddDoubleLine(left, right, linesToAddR[i], linesToAddG[i], linesToAddB[i], linesToAddRightR[i], linesToAddRightG[i], linesToAddRightB[i])
        else
            GameTooltip:AddLine(left, linesToAddR[i], linesToAddG[i], linesToAddB[i], true)
        end
        StarTip.addingLine = false
    end
    wipe(linesToAdd)
    wipe(linesToAddR)
    wipe(linesToAddG)
    wipe(linesToAddB)
    wipe(linesToAddRight)
    wipe(linesToAddRightR)
    wipe(linesToAddRightG)
    wipe(linesToAddRightB)
end
]]