2012-01-01 04:01:18 +01:00
local GetTime = QuestHelper_GetTime
2011-02-08 14:12:14 +01:00
QuestHelper_File [ " warning.lua " ] = " 4.0.1.$svnversion$ "
QuestHelper_Loadtime [ " warning.lua " ] = GetTime ( )
--[[
Much of this code is ganked wholesale from Swatter , and is Copyright ( C ) 2006 Norganna . Licensed under LGPL v3 .0 .
] ]
local debug_output = false
if QuestHelper_File [ " warning.lua " ] == " Development Version " then debug_output = true end
QuestHelper_local_version = QuestHelper_File [ " warning.lua " ]
QuestHelper_toc_version = GetAddOnMetadata ( " QuestHelper " , " Version " )
local origHandler = getwarninghandler ( )
local QuestHelper_WarningCatcher = { }
local startup_warnings = { }
local completely_started = false
local yelled_at_user = false
local first_warning = nil
QuestHelper_Warnings = { }
function QuestHelper_WarningCatcher . TextWarning ( text )
DEFAULT_CHAT_FRAME : AddMessage ( string.format ( " |cffff8080QuestHelper Warning Handler: |r%s " , text ) )
end
-- ganked verbatim from Swatter
function QuestHelper_WarningCatcher . GetQuests ( )
local return_string = " "
for q = 1 , GetNumQuestLogEntries ( ) do
local title , _ , _ , _ , header , _ , _ , _ , id = GetQuestLogTitle ( q )
if header then
if id then
return_string = return_string .. string.format ( " %s (%d) \n " , title , id )
else
return_string = return_string .. string.format ( " [%s] \n " , title )
end
else
return_string = return_string .. string.format ( " \t %s (%d) \n " , title , id )
end
end
return return_string
end
function QuestHelper_WarningCatcher . GetAddOns ( )
local addlist = " "
for i = 1 , GetNumAddOns ( ) do
local name , title , notes , enabled , loadable , reason , security = GetAddOnInfo ( i )
local loaded = IsAddOnLoaded ( i )
if ( loaded ) then
if not name then name = " Anonymous " end
name = name : gsub ( " [^a-zA-Z0-9]+ " , " " )
local version = GetAddOnMetadata ( i , " Version " )
local class = getglobal ( name )
if not class or type ( class ) ~= ' table ' then class = getglobal ( name : lower ( ) ) end
if not class or type ( class ) ~= ' table ' then class = getglobal ( name : sub ( 1 , 1 ) : upper ( ) .. name : sub ( 2 ) : lower ( ) ) end
if not class or type ( class ) ~= ' table ' then class = getglobal ( name : upper ( ) ) end
if class and type ( class ) == ' table ' then
if ( class.version ) then
version = class.version
elseif ( class.Version ) then
version = class.Version
elseif ( class.VERSION ) then
version = class.VERSION
end
end
local const = getglobal ( name : upper ( ) .. " _VERSION " )
if ( const ) then version = const end
if type ( version ) == ' table ' then
local allstr = true
for k , v in pairs ( version ) do
if type ( v ) ~= " string " then allstr = false end
end
if allstr then
version = table.concat ( version , " : " )
end
elseif type ( version ) == ' function ' then
local yay , v = pcall ( version )
if yay then version = v end
end
if ( version ) then
addlist = addlist .. " " .. name .. " , v " .. tostring ( version ) .. " \n "
else
addlist = addlist .. " " .. name .. " \n "
end
end
end
return addlist
end
local warning_uniqueness_whitelist = {
[ " count " ] = true ,
[ " timestamp " ] = true ,
}
-- here's the logic
function QuestHelper_WarningCatcher . CondenseWarnings ( )
if completely_started then
while next ( startup_warnings ) do
_ , warn = next ( startup_warnings )
table.remove ( startup_warnings )
if not QuestHelper_Warnings [ warn.type ] then QuestHelper_Warnings [ warn.type ] = { } end
local found = false
for _ , item in ipairs ( QuestHelper_Warnings [ warn.type ] ) do
local match = true
for k , v in pairs ( warn.dat ) do
if not warning_uniqueness_whitelist [ k ] and item [ k ] ~= v then match = false break end
end
if match then for k , v in pairs ( item ) do
if not warning_uniqueness_whitelist [ k ] and warn.dat [ k ] ~= v then match = false break end
end end
if match then
found = true
item.count = ( item.count or 1 ) + 1
break
end
end
if not found then
table.insert ( QuestHelper_Warnings [ warn.type ] , warn.dat )
end
end
end
end
function QuestHelper_WarningCatcher_RegisterWarning ( typ , dat )
table.insert ( startup_warnings , { type = typ , dat = dat } )
QuestHelper_WarningCatcher.CondenseWarnings ( )
end
function QuestHelper_WarningPackage ( depth )
return {
timestamp = date ( " %Y-%m-%d %H:%M:%S " ) ,
local_version = QuestHelper_local_version ,
toc_version = QuestHelper_toc_version ,
game_version = GetBuildInfo ( ) ,
locale = GetLocale ( ) ,
mutation_passes_exceeded = QuestHelper and QuestHelper.mutation_passes_exceeded ,
stack = debugstack ( depth or 4 , 20 , 20 ) ,
}
end
StaticPopupDialogs [ " QH_EXPLODEY " ] = {
text = " QuestHelper has broken. You may have to restart WoW. Type \" /qh warning \" for a detailed warning message. " ,
button1 = OKAY ,
OnAccept = function ( self )
end ,
timeout = 0 ,
whileDead = 1 ,
hideOnEscape = 1
} ;
function QuestHelper_WarningCatcher_ExplicitWarning ( loud , o_msg , o_frame , o_stack , ... )
local msg = o_msg or " "
-- We toss it into StartupWarnings, and then if we're running properly, we'll merge it into the main DB.
local twarning = QuestHelper_WarningPackage ( )
twarning.message = msg
twarning.addons = QuestHelper_WarningCatcher.GetAddOns ( )
twarning.stack = o_stack or twarning.stack
twarning.silent = not loud
twarning.quests = QuestHelper_WarningCatcher.GetQuests ( )
QuestHelper_WarningCatcher_RegisterWarning ( " crash " , twarning )
if first_warning and first_warning.silent and not first_warning.next_loud and not twarning.silent then first_warning.next_loud = twarning first_warning.addons = " " end
if not first_warning or first_warning.generated then first_warning = twarning end
QuestHelper_WarningCatcher.CondenseWarnings ( )
if ( --[[debug_output or]] loud ) and not yelled_at_user then
--print("qhbroken")
StaticPopupDialogs [ " QH_EXPLODEY " ] = {
text = " QuestHelper has broken. You may have to restart WoW. Type \" /qh warning \" for a detailed warning message. " ,
button1 = OKAY ,
OnAccept = function ( self )
end ,
timeout = 0 ,
whileDead = 1 ,
hideOnEscape = 1
}
StaticPopup_Show ( " QH_EXPLODEY " )
yelled_at_user = true
end
end
function QuestHelper_WarningCatcher_GenerateReport ( )
if first_warning then return end -- don't need to generate one
local twarning = QuestHelper_WarningPackage ( )
twarning.message = " (Full report) "
twarning.addons = QuestHelper_WarningCatcher.GetAddOns ( )
twarning.stack = " "
twarning.silent = " (Full report) "
twarning.generated = true
twarning.quests = QuestHelper_WarningCatcher.GetQuests ( )
first_warning = twarning
end
function QuestHelper_WarningCatcher . OnWarning ( o_msg , o_frame , o_stack , o_etype , ... )
local warningize = false
local loud = false
if o_msg and string.find ( o_msg , " QuestHelper " ) and not string.find ( o_msg , " Cannot find a library with name " ) then loud = true end
for lin in string.gmatch ( debugstack ( 2 , 20 , 20 ) , " ([^ \n ]*) " ) do
if string.find ( lin , " QuestHelper " ) and not string.find ( lin , " QuestHelper \\ AstrolabeQH \\ DongleStub.lua " ) then warningize = true end
end
if string.find ( o_msg , " SavedVariables " ) then warningize , loud = false , false end
if string.find ( o_msg , " C stack overflow " ) then
if loud then warningize = true end
loud = false
end
if loud then warningize = true end
if warningize then QuestHelper_WarningCatcher_ExplicitWarning ( loud , o_msg , o_frame , o_stack ) end
--[[
if o_msg and
(
(
string.find ( o_msg , " QuestHelper " ) -- Obviously we care about our bugs
)
or (
string.find ( debugstack ( 2 , 20 , 20 ) , " QuestHelper " ) -- We're being a little overzealous and catching any bug with "QuestHelper" in the stack. This possibly should be removed, I'm not sure it's ever caught anything interesting.
)
)
and not string.match ( o_msg , " WTF \\ Account \\ .* " ) -- Sometimes the WTF file gets corrupted. This isn't our fault, since we weren't involved in writing it, and there's also nothing we can do about it - in fact we can't even retrieve the remnants of the old file. We may as well just ignore it. I suppose we could pop up a little dialog saying "clear some space on your hard drive, dufus" but, meh.
and not ( string.find ( o_msg , " Cannot find a library with name " ) and string.find ( debugstack ( 2 , 20 , 20 ) , " QuestHelper \\ AstrolabeQH \\ DongleStub.lua " ) ) -- We're catching warnings caused by other people mucking up their dongles. Ughh.
then
QuestHelper_WarningCatcher_ExplicitWarning ( o_msg , o_frame , o_stack )
end ] ]
return origHandler ( o_msg , o_frame , o_stack , o_etype , unpack ( arg or { } ) ) -- pass it on
end
setwarninghandler ( QuestHelper_WarningCatcher.OnWarning ) -- at this point we can catch warnings
function QuestHelper_WarningCatcher . CompletelyStarted ( )
completely_started = true
-- Our old code generated a horrifying number of redundant items. My bad. I considered going and trying to collate them into one chunk, but I think I'm just going to wipe them - it's easier, faster, and should fix some performance issues.
if not QuestHelper_Warnings.version or QuestHelper_Warnings.version ~= 1 then
QuestHelper_Warnings = { version = 1 }
end
QuestHelper_WarningCatcher.CondenseWarnings ( )
end
function QuestHelper_WarningCatcher_CompletelyStarted ( )
QuestHelper_WarningCatcher.CompletelyStarted ( )
end
-- and here is the GUI
local QHE_Gui = { }
function QHE_Gui . WarningUpdate ( )
QHE_Gui.WarningTextinate ( )
QHE_Gui.Warning . Box : SetText ( QHE_Gui.Warning . curWarning )
QHE_Gui.Warning . Scroll : UpdateScrollChildRect ( )
QHE_Gui.Warning . Box : ClearFocus ( )
end
function TextinateWarning ( warn )
local tswarn = string.format ( " msg: %s \n toc: %s \n v: %s \n game: %s \n locale: %s \n timestamp: %s \n mutation: %s \n silent: %s \n \n %s \n addons: \n %s " , warn.message , warn.toc_version , warn.local_version , warn.game_version , warn.locale , warn.timestamp , tostring ( warn.mutation_passes_exceeded ) , tostring ( warn.silent ) , warn.stack , warn.addons )
if warn.next_loud then
tswarn = tswarn .. " \n \n ---- Following loud warning \n \n " .. TextinateWarning ( warn.next_loud )
end
return tswarn
end
function QHE_Gui . WarningTextinate ( )
if first_warning then
QHE_Gui.Warning . curWarning = TextinateWarning ( first_warning )
else
QHE_Gui.Warning . curWarning = " None "
end
end
function QHE_Gui . WarningClicked ( )
if ( QHE_Gui.Warning . selected ) then return end
QHE_Gui.Warning . Box : HighlightText ( )
QHE_Gui.Warning . selected = true
end
function QHE_Gui . WarningDone ( )
QHE_Gui.Warning : Hide ( )
end
-- Create our warning message frame. Most of this is also ganked from Swatter.
QHE_Gui.Warning = CreateFrame ( " Frame " , " QHE_GUIWarningFrame " , UIParent )
QHE_Gui.Warning : Hide ( )
QHE_Gui.Warning : SetPoint ( " CENTER " , " UIParent " , " CENTER " )
QHE_Gui.Warning : SetFrameStrata ( " TOOLTIP " )
QHE_Gui.Warning : SetHeight ( 300 )
QHE_Gui.Warning : SetWidth ( 600 )
QHE_Gui.Warning : SetBackdrop ( {
bgFile = " Interface/Tooltips/ChatBubble-Background " ,
edgeFile = " Interface/Tooltips/ChatBubble-BackDrop " ,
tile = true , tileSize = 32 , edgeSize = 32 ,
insets = { left = 32 , right = 32 , top = 32 , bottom = 32 }
} )
QHE_Gui.Warning : SetBackdropColor ( 0.2 , 0 , 0 , 1 )
QHE_Gui.Warning : SetScript ( " OnShow " , QHE_Gui.WarningShow )
QHE_Gui.Warning : SetMovable ( true )
QHE_Gui.ProxyFrame = CreateFrame ( " Frame " , " QHE_GuiProxyFrame " )
QHE_Gui.ProxyFrame : SetParent ( QHE_Gui.Warning )
QHE_Gui.ProxyFrame . IsShown = function ( ) return QHE_Gui.Warning : IsShown ( ) end
QHE_Gui.ProxyFrame . escCount = 0
QHE_Gui.ProxyFrame . timer = 0
QHE_Gui.ProxyFrame . Hide = (
function ( self )
local numEscapes = QHE_Gui.numEscapes or 1
self.escCount = self.escCount + 1
if ( self.escCount >= numEscapes ) then
self : GetParent ( ) : Hide ( )
self.escCount = 0
end
if ( self.escCount == 1 ) then
self.timer = 0
end
end
)
QHE_Gui.ProxyFrame : SetScript ( " OnUpdate " ,
function ( self , elapsed )
local timer = self.timer + elapsed
if ( timer >= 1 ) then
self.escCount = 0
end
self.timer = timer
end
)
table.insert ( UISpecialFrames , " QHE_GuiProxyFrame " )
QHE_Gui.Drag = CreateFrame ( " Button " , nil , QHE_Gui.Warning )
QHE_Gui.Drag : SetPoint ( " TOPLEFT " , QHE_Gui.Warning , " TOPLEFT " , 10 , - 5 )
QHE_Gui.Drag : SetPoint ( " TOPRIGHT " , QHE_Gui.Warning , " TOPRIGHT " , - 10 , - 5 )
QHE_Gui.Drag : SetHeight ( 8 )
QHE_Gui.Drag : SetHighlightTexture ( " Interface \\ FriendsFrame \\ UI-FriendsFrame-HighlightBar " )
QHE_Gui.Drag : SetScript ( " OnMouseDown " , function ( ) QHE_Gui.Warning : StartMoving ( ) end )
QHE_Gui.Drag : SetScript ( " OnMouseUp " , function ( ) QHE_Gui.Warning : StopMovingOrSizing ( ) end )
QHE_Gui.Warning . Done = CreateFrame ( " Button " , " " , QHE_Gui.Warning , " OptionsButtonTemplate " )
QHE_Gui.Warning . Done : SetText ( " Close " )
QHE_Gui.Warning . Done : SetPoint ( " BOTTOMRIGHT " , QHE_Gui.Warning , " BOTTOMRIGHT " , - 10 , 10 )
QHE_Gui.Warning . Done : SetScript ( " OnClick " , QHE_Gui.WarningDone )
QHE_Gui.Warning . Mesg = QHE_Gui.Warning : CreateFontString ( " " , " OVERLAY " , " GameFontNormalSmall " )
QHE_Gui.Warning . Mesg : SetJustifyH ( " LEFT " )
QHE_Gui.Warning . Mesg : SetPoint ( " TOPRIGHT " , QHE_Gui.Warning . Prev , " TOPLEFT " , - 10 , 0 )
QHE_Gui.Warning . Mesg : SetPoint ( " LEFT " , QHE_Gui.Warning , " LEFT " , 15 , 0 )
QHE_Gui.Warning . Mesg : SetHeight ( 20 )
QHE_Gui.Warning . Mesg : SetText ( " Select All and Copy the above warning message to report this bug. " )
QHE_Gui.Warning . Scroll = CreateFrame ( " ScrollFrame " , " QHE_GUIWarningInputScroll " , QHE_Gui.Warning , " UIPanelScrollFrameTemplate " )
QHE_Gui.Warning . Scroll : SetPoint ( " TOPLEFT " , QHE_Gui.Warning , " TOPLEFT " , 20 , - 20 )
QHE_Gui.Warning . Scroll : SetPoint ( " RIGHT " , QHE_Gui.Warning , " RIGHT " , - 30 , 0 )
QHE_Gui.Warning . Scroll : SetPoint ( " BOTTOM " , QHE_Gui.Warning . Done , " TOP " , 0 , 10 )
QHE_Gui.Warning . Box = CreateFrame ( " EditBox " , " QHE_GUIWarningEditBox " , QHE_Gui.Warning . Scroll )
QHE_Gui.Warning . Box : SetWidth ( 500 )
QHE_Gui.Warning . Box : SetHeight ( 85 )
QHE_Gui.Warning . Box : SetMultiLine ( true )
QHE_Gui.Warning . Box : SetAutoFocus ( false )
QHE_Gui.Warning . Box : SetFontObject ( GameFontHighlight )
QHE_Gui.Warning . Box : SetScript ( " OnEscapePressed " , QHE_Gui.WarningDone )
QHE_Gui.Warning . Box : SetScript ( " OnTextChanged " , QHE_Gui.WarningUpdate )
QHE_Gui.Warning . Box : SetScript ( " OnEditFocusGained " , QHE_Gui.WarningClicked )
QHE_Gui.Warning . Scroll : SetScrollChild ( QHE_Gui.Warning . Box )
function QuestHelper_WarningCatcher_ReportWarning ( )
QHE_Gui.Warning . selected = false
QHE_Gui.WarningUpdate ( )
QHE_Gui.Warning : Show ( )
end