Quantcast

* Updated to Dongle-1.1

James Whitehead II [11-12-07 - 20:12]
* Updated to Dongle-1.1
Filename
Clique.lua
Dongle.lua
diff --git a/Clique.lua b/Clique.lua
index e1a3cee..90511cd 100644
--- a/Clique.lua
+++ b/Clique.lua
@@ -5,7 +5,7 @@
 Clique = {Locals = {}}

 assert(DongleStub, string.format("Clique requires DongleStub."))
-DongleStub("Dongle-1.0"):New("Clique", Clique)
+DongleStub("Dongle-1.1"):New("Clique", Clique)
 Clique.rev = tonumber(string.match("$Revision$", "(%d+)") or 1)

 local L = Clique.Locals
diff --git a/Dongle.lua b/Dongle.lua
index 28fd344..804eb98 100644
--- a/Dongle.lua
+++ b/Dongle.lua
@@ -154,12 +154,8 @@ end
   Begin Library Implementation
 ---------------------------------------------------------------------------]]

-local major = "Dongle-1.0"
-local minor = tonumber(string.match("$Revision: 371 $", "(%d+)") or 1) + 500
--- ** IMPORTANT NOTE **
--- Due to some issues we had previously with Dongle revision numbers
--- we need to artificially inflate the minor revision number, to ensure
--- we load sequentially.
+local major = "Dongle-1.1"
+local minor = tonumber(string.match("$Revision: 647 $", "(%d+)") or 1)

 assert(DongleStub, string.format("%s requires DongleStub.", major))

@@ -169,6 +165,7 @@ local Dongle = {}
 local methods = {
 	"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered",
 	"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered",
+	"ScheduleTimer", "ScheduleRepeatingTimer", "CancelTimer", "IsTimerScheduled",
 	"EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF",
 	"InitializeDB",
 	"InitializeSlashCommand",
@@ -183,6 +180,8 @@ local events = {}
 local databases = {}
 local commands = {}
 local messages = {}
+local timers = {}
+local heap = {}

 local frame

@@ -471,6 +470,145 @@ function Dongle:IsMessageRegistered(msg)
 end

 --[[-------------------------------------------------------------------------
+	Timer System
+---------------------------------------------------------------------------]]
+
+local function HeapSwap(i1, i2)
+	heap[i1], heap[i2] = heap[i2], heap[i1]
+end
+
+local function HeapBubbleUp(index)
+	while index > 1 do
+		local parentIndex = math.floor(index / 2)
+		if heap[index].timeToFire < heap[parentIndex].timeToFire then
+			HeapSwap(index, parentIndex)
+			index = parentIndex
+		else
+			break
+		end
+	end
+end
+
+local function HeapBubbleDown(index)
+	while 2 * index <= heap.lastIndex do
+		local leftIndex = 2 * index
+		local rightIndex = leftIndex + 1
+		local current = heap[index]
+		local leftChild = heap[leftIndex]
+		local rightChild = heap[rightIndex]
+
+		if not rightChild then
+			if leftChild.timeToFire < current.timeToFire then
+				HeapSwap(index, leftIndex)
+				index = leftIndex
+			else
+				break
+			end
+		else
+			if leftChild.timeToFire < current.timeToFire or
+			   rightChild.timeToFire < current.timeToFire then
+				if leftChild.timeToFire < rightChild.timeToFire then
+					HeapSwap(index, leftIndex)
+					index = leftIndex
+				else
+					HeapSwap(index, rightIndex)
+					index = rightIndex
+				end
+			else
+				break
+			end
+		end
+	end
+end
+
+local function OnUpdate(frame, elapsed)
+	local schedule = heap[1]
+	while schedule and schedule.timeToFire < GetTime() do
+		if schedule.cancelled then
+			HeapSwap(1, heap.lastIndex)
+			heap[heap.lastIndex] = nil
+			heap.lastIndex = heap.lastIndex - 1
+			HeapBubbleDown(1)
+		else
+			if schedule.args then
+				safecall(schedule.func, schedule.name, unpack(schedule.args))
+			else
+				safecall(schedule.func, schedule.name)
+			end
+
+			if schedule.repeating then
+				schedule.timeToFire = schedule.timeToFire + schedule.repeating
+				HeapBubbleDown(1)
+			else
+				HeapSwap(1, heap.lastIndex)
+				heap[heap.lastIndex] = nil
+				heap.lastIndex = heap.lastIndex - 1
+				HeapBubbleDown(1)
+				timers[schedule.name] = nil
+			end
+		end
+		schedule = heap[1]
+	end
+	if not schedule then frame:Hide() end
+end
+
+function Dongle:ScheduleTimer(name, func, delay, ...)
+	argcheck(self, 1, "table")
+	argcheck(name, 2, "string")
+	argcheck(func, 3, "function")
+	argcheck(delay, 4, "number")
+
+	if Dongle:IsTimerScheduled(name) then
+		Dongle:CancelTimer(name)
+	end
+
+	local schedule = {}
+	timers[name] = schedule
+	schedule.timeToFire = GetTime() + delay
+	schedule.func = func
+	schedule.name = name
+	if select('#', ...) ~= 0 then
+		schedule.args = { ... }
+	end
+
+	if heap.lastIndex then
+		heap.lastIndex = heap.lastIndex + 1
+	else
+		heap.lastIndex = 1
+	end
+	heap[heap.lastIndex] = schedule
+	HeapBubbleUp(heap.lastIndex)
+	if not frame:IsShown() then
+		frame:Show()
+	end
+end
+
+function Dongle:ScheduleRepeatingTimer(name, func, delay, ...)
+	Dongle:ScheduleTimer(name, func, delay, ...)
+	timers[name].repeating = delay
+end
+
+function Dongle:IsTimerScheduled(name)
+	argcheck(self, 1, "table")
+	argcheck(name, 2, "string")
+	local schedule = timers[name]
+	if schedule then
+		return true, schedule.timeToFire - GetTime()
+	else
+		return false
+	end
+end
+
+function Dongle:CancelTimer(name)
+	argcheck(self, 1, "table")
+	argcheck(name, 2, "string")
+	local schedule = timers[name]
+	if not schedule then return end
+	schedule.cancelled = true
+	timers[name] = nil
+end
+
+--[[-------------------------------------------------------------------------
 	Debug and Print utility functions
 ---------------------------------------------------------------------------]]

@@ -884,21 +1022,34 @@ function Dongle.SetProfile(db, name)
 	Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile)
 end

-function Dongle.GetProfiles(db, t)
+function Dongle.GetProfiles(db, tbl)
 	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetProfiles"))
 	argcheck(t, 2, "table", "nil")

-	t = t or {}
-	local i = 1
-	for profileKey in pairs(db.sv.profiles) do
-		t[i] = profileKey
+	-- Clear the container table
+	if tbl then
+		for k,v in pairs(tbl) do tbl[k] = nil end
+	else
+		tbl = {}
+	end
+
+	local i = 0
+	for profileKey in pairs(db.profiles) do
 		i = i + 1
+		tbl[i] = profileKey
 	end
-	return t, i - 1
+
+	-- Add the current profile, if it hasn't been created yet
+	if rawget(db, "profile") == nil then
+		i = i + 1
+		tbl[i] = db.keys.profile
+	end
+
+	return tbl, i
 end

 function Dongle.GetCurrentProfile(db)
-	assert(e, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile"))
+	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile"))
 	return db.keys.profile
 end

@@ -966,8 +1117,8 @@ end

 function Dongle.RegisterNamespace(db, name, defaults)
 	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterNamespace"))
-		argcheck(name, 2, "string")
-	argcheck(defaults, 3, "nil", "string")
+	argcheck(name, 2, "string")
+	argcheck(defaults, 3, "nil", "table")

 	local sv = db.sv
 	if not sv.namespaces then sv.namespaces = {} end
@@ -1147,44 +1298,41 @@ local function PLAYER_LOGOUT(event)
 	end
 end

-local function PLAYER_LOGIN()
-	Dongle.initialized = true
-	for i=1, #loadorder do
-		local obj = loadorder[i]
-		if type(obj.Enable) == "function" then
-			safecall(obj.Enable, obj)
+local PLAYER_LOGIN
+do
+	local lockPlayerLogin = false
+
+	function PLAYER_LOGIN()
+		if lockPlayerLogin then return end
+
+		lockPlayerLogin = true
+
+		local obj = table.remove(loadorder, 1)
+		while obj do
+			if type(obj.Enable) == "function" then
+				safecall(obj.Enable, obj)
+			end
+			obj = table.remove(loadorder, 1)
 		end
-		loadorder[i] = nil
+
+		lockPlayerLogin = false
 	end
 end

 local function ADDON_LOADED(event, ...)
-	for i=1, #loadqueue do
-		local obj = loadqueue[i]
+	local obj = table.remove(loadqueue, 1)
+	while obj do
 		table.insert(loadorder, obj)
-
+
 		if type(obj.Initialize) == "function" then
 			safecall(obj.Initialize, obj)
 		end
-		loadqueue[i] = nil
-	end

-	if not Dongle.initialized then
-		if type(IsLoggedIn) == "function" then
-			Dongle.initialized = IsLoggedIn()
-		else
-			Dongle.initialized = ChatFrame1.defaultLanguage
-		end
+		obj = table.remove(loadqueue, 1)
 	end

-	if Dongle.initialized then
-		for i=1, #loadorder do
-			local obj = loadorder[i]
-			if type(obj.Enable) == "function" then
-				safecall(obj.Enable, obj)
-			end
-			loadorder[i] = nil
-		end
+	if IsLoggedIn() then
+		PLAYER_LOGIN()
 	end
 end

@@ -1223,6 +1371,8 @@ local function Activate(self, old)
 		commands = old.commands or commands
 		messages = old.messages or messages
 		frame = old.frame or CreateFrame("Frame")
+		timers = old.timers or timers
+		heap = old.heap or heap
 	else
 		frame = CreateFrame("Frame")
 		local reg = {obj = self, name = "Dongle"}
@@ -1240,19 +1390,21 @@ local function Activate(self, old)
 	self.commands = commands
 	self.messages = messages
 	self.frame = frame
+	self.timers = timers
+	self.heap = heap

 	frame:SetScript("OnEvent", OnEvent)
+	frame:SetScript("OnUpdate", OnUpdate)

-	local lib = old or self
-
-	-- Lets make sure the lookup table has us.
-	lookup[lib] = lookup[major]
+	-- Lets ensure the lookup table has our entry
+	-- This fixes an issue with upgrades
+	lookup[self] = lookup[major]

 	-- Register for events using Dongle itself
-	lib:RegisterEvent("ADDON_LOADED", ADDON_LOADED)
-	lib:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN)
-	lib:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT)
-	lib:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED)
+	self:RegisterEvent("ADDON_LOADED", ADDON_LOADED)
+	self:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN)
+	self:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT)
+	self:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED)

 	-- Convert all the modules handles
 	for name,obj in pairs(registry) do
@@ -1276,11 +1428,4 @@ local function Activate(self, old)
 	end
 end

--- Lets nuke any Dongle deactivate functions, please
--- I hate nasty hacks.
-if DongleStub.versions and DongleStub.versions[major] then
-	local reg = DongleStub.versions[major]
-	reg.deactivate = nil
-end
-
 Dongle = DongleStub:Register(Dongle, Activate)