Quantcast
function sdm_SlashHandler(command)
	if command=="" then
		sdm_editFrame:Hide()
		sdm_editFrame:Show()
	elseif command:sub(1,4)=="run " then
		sdm_RunScript(command:sub(5,command:len()))
	else print("SDM did not recognize the command \""..command.."\"")
	end
end
function sdm_MakeMacroFrame(name, text) --returns the frame
	sdm_DoOrQueue("local temp = getglobal("..sdm_Stringer(name)..") or CreateFrame(\"Button\", "..sdm_Stringer(name)..", nil, \"SecureActionButtonTemplate\")\
	temp:SetAttribute(\'type\', \'macro\')\
	temp:SetAttribute(\'macrotext\', "..sdm_Stringer(text)..")")
--	GetClickFrame(name) --This line is just to fix a taint issue from a Blizzard bug (fixed in 3.0)
--	print("Creating frame \""..name.."\" with macrotext\n\""..text.."\"\n(length "..string.len(text)..")")
	if string.len(text)>1023 then print("The following line is "..(string.len(text)-1023).." characters too long:\n"..text) end
	return temp
end
function sdm_MakeBlizzardMacro(ID, name, text, perCharacter)
--	print("Creating macro \""..name.."\" with text\n\""..text.."\"\n(length "..string.len(text)..")")
	sdm_DoOrQueue("local macroIndex = sdm_GetMacroIndex("..sdm_Stringer(ID)..")\
	if macroIndex then\
		EditMacro(macroIndex, "..sdm_Stringer(name)..", nil, "..sdm_Stringer(text)..", 1, "..sdm_Stringer(perCharacter)..")\
	else\
		CreateMacro("..sdm_Stringer(name)..", 1, "..sdm_Stringer(text)..", "..sdm_Stringer(perCharacter)..", 1)\
	end")
end
function sdm_GetSdmID(macroIndex)
	local thisMacroText=GetMacroBody(macroIndex)
	if thisMacroText and thisMacroText:sub(1,4)=="#sdm" then
		return tonumber(thisMacroText:sub(5,thisMacroText:find("\n")-1))
	else
		return nil
	end
end
function sdm_GetMacroIndex(sdmID)
	for i=1,54 do
		if sdm_GetSdmID(i)==sdmID then
			return i
		end
	end
	return nil
end
function sdm_GetLinkText(nextName)
	return "/click [btn:5]"..nextName.." Button5;[btn:4]"..nextName.." Button4;[btn:3]"..nextName.." MiddleButton;[btn:2]"..nextName.." RightButton;"..nextName
end
function sdm_UpdateCurrentEdit(setTo)
	if setTo then sdm_currentEdit = setTo end
	if getn(sdm_macros)>0 then
		if not sdm_currentEdit or sdm_currentEdit<1 then
			sdm_currentEdit = 1
		elseif sdm_currentEdit > getn(sdm_macros) then
			sdm_currentEdit = getn(sdm_macros)
		end
		local carrot=""
		for i,_ in ipairs(sdm_macros) do
			if i>1 then carrot=carrot.."\n" end
			if i==sdm_currentEdit then carrot=carrot..">" else carrot=carrot.." " end
		end
		sdm_editFrame_menuFrame_current:SetText(carrot)
		sdm_editFrame_menuFrame_current:Show()
	else
		sdm_currentEdit = 0
		sdm_editFrame_menuFrame_current:Hide()
	end
	if sdm_currentEdit==0 then
		sdm_editFrame_deleteButton:Disable()
		sdm_editFrame_getLinkButton:Disable()
	elseif not sdm_ThisChar(sdm_macros[sdm_currentEdit]) then
		sdm_editFrame_deleteButton:Enable()
		sdm_editFrame_getLinkButton:Disable()
	else
		sdm_editFrame_deleteButton:Enable()
		sdm_editFrame_getLinkButton:Enable()
	end
	if sdm_currentEdit~=0 and sdm_macros[sdm_currentEdit].type=="b" then
		sdm_editFrame_hideTextCheckBox:Show()
		sdm_editFrame_hideTextCheckBox:SetChecked(sdm_macros[sdm_currentEdit].hideName)
	else
		sdm_editFrame_hideTextCheckBox:Hide()
	end
end
function sdm_SetUpMacro(mTab)
	local type = mTab.type
	if type~="b" and type~="f" then
		return
	end
	local name = sdm_GetButtonText(mTab)
	local text = mTab.text
	local perCharacter = (mTab.character)~=nil
	local ID = mTab.ID
	local charLimit = 255
	if type=="b" then
		text="#sdm"..ID.."\n"..text
	end
	--[[elseif type=="f" then
		charLimit = 1023
	end]]
	local frameText = ""
	local nextFrameName = "sdm"..ID
	local linkText = "\n"..sdm_GetLinkText(nextFrameName)
	for line in text:gmatch("[^\r\n]+") do
		if line~="" then
			if frameText~="" then --if this is not the first line of the frame, we need to add a carriage return before it.
				line="\n"..line
			end
			if frameText:len()+line:len()+linkText:len() > charLimit then --adding this line would be too much, so just add the link and be done with it. (note that this line does NOT get removed from the master text)
				frameText = frameText..linkText
				break
			end
			frameText = frameText..line
		end
		text=text:sub((text:find("\n") or text:len())+1, text:len()) --remove the line from the text
	end
	sdm_SetUpMacroFrames(nextFrameName, text, 1)
	if type=="b" then
		sdm_MakeBlizzardMacro(ID, name, frameText, perCharacter)
	elseif type=="f" then
		sdm_MakeMacroFrame(name, frameText)
	end
end
function sdm_SetUpMacroFrames(clickerName, text, currentLayer) --returns the frame to be clicked
	local currentFrame=1
	local frameText=""
	local nextLayerText=""
	for line in text:gmatch("[^\r\n]+") do
		if line~="" then
			if frameText~="" then --if this is not the first line of the frame, we need to add a carriage return before it.
				line="\n"..line
			end
			if (frameText:len()+line:len() > 1023) then --adding this line would be too much, so finish this frame and move on to the next.
				sdm_MakeMacroFrame(clickerName.."_"..currentLayer.."_"..currentFrame, frameText)
				if nextLayerText~="" then nextLayerText= nextLayerText.."\n" end
				nextLayerText = nextLayerText..sdm_GetLinkText(clickerName.."_"..currentLayer.."_"..currentFrame)
				frameText = ""
				currentFrame = currentFrame+1
			end
			frameText = frameText..line
		end
		text=text:sub((text:find("\n") or text:len())+1, text:len()) --remove the line from the text
	end
	if currentFrame==1 then
		return sdm_MakeMacroFrame(clickerName, frameText)
	else
		sdm_MakeMacroFrame(clickerName.."_"..currentLayer.."_"..currentFrame, frameText) --repeated from above; just finishing off this frame
		nextLayerText = nextLayerText.."\n"..sdm_GetLinkText(clickerName.."_"..currentLayer.."_"..currentFrame)
		return sdm_SetUpMacroFrames(clickerName, nextLayerText, currentLayer+1)
	end
end
function sdm_UpdateMacroList()
	infoString=""
	for i,v in ipairs(sdm_macros) do
		if i>1 then infoString=infoString.."\n" end
		infoString=infoString..i..": \""..v.name.."\" type "..v.type
		if v.character then infoString=infoString.." ("..v.character.name.." of "..v.character.server..")" end
	end
	sdm_editFrame_menuFrame_macroInfo:SetText(infoString)
	sdm_UpdateCurrentEdit()
end
function sdm_Query(channel, target) --next version: have a single token for party and raid, then decide here.
	SendAddonMessage("Super Duper Macro query", sdm_qian, channel, target)
end
function sdm_SendMacro(mTab, chan, tar)
	if sdm_sending~=nil then
		print("SDM: You are already sending something.")
		return
	end
	local perCharacter=nil
	--make the string that will be split up and sent.  It consists of a bunch of values separated by commas.  They are, in order: the version the sender is running, the minimum version the receiver must have, the type of macro, the perCharacter status ("<table value>" or "nil"), the length of the name, the length of the text, the name, and the text.  There is no comma between the name and the text.
	local textToSend = sdm_qian..","..sdm_minVersion..","..mTab.type..","..tostring(mTab.character)..","..mTab.name:len()..","..mTab.text:len()..","..mTab.name..mTab.text
	local pref = "Super Duper Macro send1" -- if the prefix ends in "send1", it's the first line.  If it ends in "send2", it's any line after the first.
	local lineLen = 254 - pref:len()
	local linesToSend={}
	local pos = 1
	while pos <= textToSend:len() do
		table.insert(linesToSend, textToSend:sub(pos, pos+lineLen-1))
		pos = pos+lineLen
	end
	sdm_sending={
		i=1,
		lines = linesToSend,
		numLines = getn(linesToSend),
		channel = chan,
		target = tar,
		prefix = pref
	}
	sdm_sendReceiveFrame_sendBar_statusBar:SetMinMaxValues(0, sdm_sending.numLines)
	sdm_sendReceiveFrame_sendBar_statusBar:SetValue(0)
	sdm_sendReceiveFrame_sendBar_statusBar_text:SetText("|cffffccffSending to "..(sdm_sending.target or sdm_sending.channel).."|r")
	sdm_sendReceiveFrame_cancelSendButton:Enable()
	sdm_sendReceiveFrame_sendButton:Disable()
	sdm_sendReceiveFrame_sendPartyRadio:Disable()
	sdm_sendReceiveFrame_sendRaidRadio:Disable()
	sdm_sendReceiveFrame_sendBattlegroundRadio:Disable()
	sdm_sendReceiveFrame_sendGuildRadio:Disable()
	sdm_sendReceiveFrame_sendTargetRadio:Disable()
	sdm_sendReceiveFrame_sendArbitraryRadio:Disable()
	sdm_sendReceiveFrame_sendInput:EnableMouse(nil)
	sdm_updateFrame:Show()
end
function sdm_OnUpdate(self, elapsed) --used for sending macros
	self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + elapsed
	if self.TimeSinceLastUpdate > sdm_updateInterval then
		if sdm_sending.i == 2 then
			sdm_sending.prefix="Super Duper Macro send2"
		end
		SendAddonMessage(sdm_sending.prefix, sdm_sending.lines[sdm_sending.i], sdm_sending.channel, sdm_sending.target)
		sdm_sendReceiveFrame_sendBar_statusBar:SetValue(sdm_sending.i)
		sdm_sending.i = sdm_sending.i+1
		if sdm_sending.i>sdm_sending.numLines then
			sdm_EndSending("|cff44ff00Sent to "..(sdm_sending.target or sdm_sending.channel).."|r")
		end
		self.TimeSinceLastUpdate = 0
	end
end
function sdm_WaitForMacro(name)
	if sdm_receiving~=nil then
		print("SDM: You are already receiving or waiting.")
		return
	end
	sdm_receiving = {playerName=name, first=true}
	sdm_sendReceiveFrame_receiveBar_statusBar:SetValue(0)
	sdm_sendReceiveFrame_receiveBar_statusBar_text:SetText("|cffffccffWaiting for "..sdm_receiving.playerName.."|r")
	sdm_sendReceiveFrame_cancelReceiveButton:Enable()
	sdm_sendReceiveFrame_receiveButton:Disable()
	sdm_editFrame_menuFrame_newButton:Disable()
	sdm_sendReceiveFrame_receiveTargetRadio:Disable()
	sdm_sendReceiveFrame_receiveArbitraryRadio:Disable()
	sdm_sendReceiveFrame_receiveInput:EnableMouse(nil)
	sdm_newFrame:Hide()
end
function sdm_ReceiveLine(line, send1)
	if sdm_receiving.first and send1 then --this is the first line
		sdm_receiving.nameAndText, sdm_receiving.textLen, sdm_receiving.playerNameLen, sdm_receiving.perCharacter, sdm_receiving.type, sdm_receiving.minVersion, sdm_receiving.sendersVersion = sdm_SplitString(line, ",", 6)
		sdm_receiving.perCharacter = (sdm_receiving.perCharacter~="nil")
		sdm_receiving.textLen = 0 + sdm_receiving.textLen
		sdm_receiving.playerNameLen = 0 + sdm_receiving.playerNameLen
		sdm_receiving.first = false
		sdm_sendReceiveFrame_receiveBar_statusBar:SetMinMaxValues(0, sdm_receiving.playerNameLen + sdm_receiving.textLen)
		sdm_sendReceiveFrame_receiveBar_statusBar_text:SetText("|cffffccffReceiving|r")
		sdm_VersionReceived(sdm_receiving.sendersVersion)
		if sdm_CompareVersions(sdm_receiving.sendersVersion, sdm_minVersion)==2 or sdm_CompareVersions(sdm_version, sdm_receiving.minVersion)==2 then
			print("SDM: You failed to recieve the macro due to a version incompatibility.")
			SendAddonMessage("Super Duper Macro recFailed", "Incompatible Versions,"..sdm_qian, "WHISPER", sdm_receiving.playerName)
			sdm_EndReceiving("|cffff0000Failed|r")
			return
		else
			SendAddonMessage("Super Duper Macro receiving", sdm_qian, "WHISPER", sdm_receiving.playerName)
		end
	elseif (not sdm_receiving.first) and (not send1) then
		sdm_receiving.nameAndText = sdm_receiving.nameAndText..line
	else
		return
	end
	local currLen = sdm_receiving.nameAndText:len()
	sdm_sendReceiveFrame_receiveBar_statusBar:SetValue(currLen)
	if currLen == (sdm_receiving.playerNameLen + sdm_receiving.textLen) then
		sdm_sendReceiveFrame_receiveBar_statusBar_text:SetText("|cffff9900Click \"Create\" to save|r")
		UIFrameFlash(sdm_newFrame_createButton_flash, 0.5, 0.5, 1000, false)
		if sdm_receiving.type=="b" then
			sdm_newFrame_buttonRadio:Click()
		elseif sdm_receiving.type=="f" then
			sdm_newFrame_floatingRadio:Click()
		elseif sdm_receiving.type=="s" then
			sdm_newFrame_scriptRadio:Click()
		end
		if sdm_receiving.perCharacter then
			sdm_newFrame_charspecRadio:Click()
		else
			sdm_newFrame_globalRadio:Click()
		end
		sdm_receiving.name=sdm_receiving.nameAndText:sub(1,sdm_receiving.playerNameLen)
		sdm_newFrame_input:SetText(sdm_receiving.name)
		sdm_receiving.text=sdm_receiving.nameAndText:sub(sdm_receiving.playerNameLen+1,sdm_receiving.playerNameLen+sdm_receiving.textLen)
		sdm_NewButtonClicked()
		sdm_newFrame_input:ClearFocus()
	end
end
function sdm_EndSending(text)
	sdm_updateFrame:Hide()
	sdm_sendReceiveFrame_sendBar_statusBar_text:SetText(text)
	sdm_sending=nil
	sdm_sendReceiveFrame_cancelSendButton:Disable()
	sdm_sendReceiveFrame_sendButton:Enable()
	sdm_sendReceiveFrame_sendPartyRadio:Enable()
	sdm_sendReceiveFrame_sendRaidRadio:Enable()
	sdm_sendReceiveFrame_sendBattlegroundRadio:Enable()
	sdm_sendReceiveFrame_sendGuildRadio:Enable()
	sdm_sendReceiveFrame_sendTargetRadio:Enable()
	sdm_sendReceiveFrame_sendArbitraryRadio:Enable()
	sdm_sendReceiveFrame_sendInput:EnableMouse(1)
end
function sdm_EndReceiving(text)
	sdm_sendReceiveFrame_receiveBar_statusBar_text:SetText(text)
	sdm_sendReceiveFrame_cancelReceiveButton:Disable()
	sdm_sendReceiveFrame_receiveButton:Enable()
	sdm_editFrame_menuFrame_newButton:Enable()
	sdm_sendReceiveFrame_receiveTargetRadio:Enable()
	sdm_sendReceiveFrame_receiveArbitraryRadio:Enable()
	sdm_sendReceiveFrame_receiveInput:EnableMouse(1)
	sdm_receiving=nil
end
function sdm_CancelSend()
	SendAddonMessage("Super Duper Macro sendFailed", "Cancelled", sdm_sending.channel, sdm_sending.target)
	sdm_EndSending("|cffff0000Cancelled|r")
end
function sdm_CancelReceive()
	SendAddonMessage("Super Duper Macro recFailed", "Cancelled,"..sdm_qian, "WHISPER", sdm_receiving.playerName)
	sdm_EndReceiving("|cffff0000Cancelled|r")
	sdm_newFrame:Hide()
end
function sdm_CreateCancelButtonPressed()
	sdm_newFrame:Hide()
	if sdm_receiving and sdm_receiving.text then
		sdm_CancelReceive()
	end
end
function sdm_SplitString(s, pattern, limit, ...) --iterates through "s", splitting it between occurrences of "pattern", and returning the split portions IN BACKWARDS ORDER. Splits a maximum of <limit> times (optional)
	if limit==0 then
		return s, ...
	end
	local index = s:find(pattern)
	if index==nil then
		return s, ...
	end
	return sdm_SplitString(s:sub(index+pattern:len(), s:len()), pattern, limit-1, s:sub(1, index-1), ...)
end
function sdm_VersionReceived(ver)
	if not sdm_versionWarning and sdm_CompareVersions(sdm_version,ver)==2 then
		sdm_versionWarning="|cff00ff00A new version of Super Duper Macro is available!  Search for it on www.wowinterface.com|r"
		print(sdm_versionWarning)
		sdm_editFrameVersionWarning:SetText(sdm_versionWarning)
	end
end
function sdm_DoOrQueue(luaText) --If player is not in combat, runs the command. Otherwise, queues it up to be executed when combat is dropped.
	if UnitAffectingCombat("player") then
		sdm_eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
		print("SDM: Changes to macros will not take effect until combat ends.")
		table.insert(sdm_doAfterCombat, luaText)
	else
		RunScript(luaText)
	end
end
function sdm_Stringer(var) --converts a variable to a string for purposes of putting it in a string for RunScript(). Strings are formatted as quoted strings, other vars are converted to strings.
	if type(var)=="string" then
		return string.format("%q", var)
	else
		return tostring(var)
	end
end
function sdm_CompareVersions(firstString, secondString) --returns 1 if the first is bigger, 2 if the second is bigger, and 0 if they are equal.
	local strings = {firstString, secondString}
	local numbers = {}
	while 1 do
		for i=1, 2 do
			if strings[i]==nil then strings[i]="0" end
			local indexOfPeriod=(strings[i]):find("%.")
			if indexOfPeriod==nil then
				numbers[i]=strings[i]
				strings[i]=nil
			else
				numbers[i]=strings[i]:sub(1, indexOfPeriod-1)
				strings[i] = strings[i]:sub(indexOfPeriod+1, strings[i]:len())
			end
			numbers[i] = tonumber(numbers[i])
		end
		if numbers[1] > numbers[2] then
			return 1
		elseif numbers[2] > numbers[1] then
			return 2
		elseif strings[1]==nil and strings[2]==nil then
			return 0
		end
	end
end
function sdm_About()
	print("Super Duper Macro by hypehuman. Version "..sdm_version..". Check for updates at www.wowinterface.com")
end
function sdm_SendButtonClicked()
	local channel
	local target
	if sdm_sendReceiveFrame_sendPartyRadio:GetChecked() then
		channel="PARTY"
	elseif sdm_sendReceiveFrame_sendRaidRadio:GetChecked() then
		channel="RAID"
	elseif sdm_sendReceiveFrame_sendBattlegroundRadio:GetChecked() then
		channel="BATTLEGROUND"
	elseif sdm_sendReceiveFrame_sendGuildRadio:GetChecked() then
		channel="GUILD"
	elseif sdm_sendReceiveFrame_sendTargetRadio:GetChecked() then
		channel="WHISPER"
		if UnitIsPlayer("target") then
			target, realm = UnitName("target")
			if realm then
				target = target.."-"..realm
			end
		end
	elseif sdm_sendReceiveFrame_sendArbitraryRadio:GetChecked() then
		channel="WHISPER"
		target=sdm_sendReceiveFrame_sendInput:GetText()
	end
	if channel=="WHISPER" and (target==nil or target=="" or target==UnitName("player")) then
		return
	end
	sdm_sendReceiveFrame_sendInput:ClearFocus()
	sdm_SendMacro(sdm_macros[sdm_currentEdit], channel, target)
end
function sdm_ReceiveButtonClicked()
	local sender
	if sdm_sendReceiveFrame_receiveTargetRadio:GetChecked() then
		if UnitIsPlayer("target") then
			sender, realm = UnitName("target")
			if realm then
				sender = sender.."-"..realm
			end
		end
	elseif sdm_sendReceiveFrame_receiveArbitraryRadio:GetChecked() then
		sender=sdm_sendReceiveFrame_receiveInput:GetText()
	end
	if (sender==nil or sender=="" or sender==UnitName("player")) then return end
	sdm_WaitForMacro(sender)
	sdm_sendReceiveFrame_receiveInput:ClearFocus()
end
--[[function sdm_MakeProperCase(text, partial) -- if partial is true, we will ignore the possibility of a hyphenated name-sever
	if partial then
		return text:sub(1,1):upper()..text:sub(2,text:len()):lower()
	end
	local pos = text:find("-")
	if pos then
		return sdm_MakeProperCase(text:sub(1, pos), 1)..sdm_MakeProperCase(text:sub(pos+1, text:len()), 1)
	end
	return sdm_MakeProperCase(text, 1)
end]] -- deleted because it doesn't account for strange capitalization within server names
function sdm_NewButtonClicked()
	sdm_newFrame:Show()
	sdm_newFrame_input:SetFocus()
end
function sdm_DeleteButtonClicked()
	sdm_DeleteMacro(sdm_currentEdit)
	sdm_UpdateCurrentEdit()
end
function sdm_SelectButtonClicked()
	local input=sdm_editFrame_menuFrame_numberInput:GetText()
	local numberFromInput = tonumber(string.format("%d", tonumber(input) or 0))
	local entry = nil
	if tostring(numberFromInput) == input and numberFromInput>0 and numberFromInput<=getn(sdm_macros) then
		entry = numberFromInput
	end
	if entry then
		sdm_SaveConfirmationBox("sdm_UpdateCurrentEdit("..sdm_Stringer(entry)..") sdm_ShowMacroEditText(sdm_macros[sdm_currentEdit])")
	end
	sdm_editFrame_menuFrame_numberInput:SetText("")
	sdm_editFrame_menuFrame_numberInput:ClearFocus()
end
function sdm_SetTextDisplay(mTab, hide)
	if not mTab.type=="b" then return end
	mTab.hideName=hide
	sdm_MakeBlizzardMacro(mTab.ID, sdm_GetButtonText(mTab))
end
function sdm_GetButtonText(mTab)
	if mTab.hideName then
		return " "
	else
		return mTab.name
	end
end
function sdm_freezeEditFrame()
	sdm_descendants = {sdm_editFrame:GetChildren()}
	sdm_mouseStates = {}
	local i=1
	for i,v in ipairs(sdm_descendants) do
		--table.insert(sdm_descendants, v:GetChildren()) -- this doesn't work (I was trying to put all the returns of GetChildren into the table), so I put in the next three lines instead
		for j,w in ipairs({v:GetChildren()}) do
			table.insert(sdm_descendants, w)
		end
		sdm_mouseStates[i] = v:IsMouseEnabled()
		v:EnableMouse(false)
		i=i+1
	end
end
function sdm_thawEditFrame()
	for i,v in ipairs(sdm_descendants) do
		v:EnableMouse(sdm_mouseStates[i])
	end
end
function sdm_SaveConfirmationBox(postponed)
	if sdm_currentEdit==0 or sdm_macros[sdm_currentEdit].text==sdm_editFrame_editScrollFrame_text:GetText() then
		RunScript(postponed)
	else
		sdm_editFrame_editScrollFrame_text:ClearFocus()
		StaticPopupDialogs["SDM_CONFIRM"] = {
			text = "Do you want to save your changes to \""..sdm_macros[sdm_currentEdit].name.."\"?",
			button1 = "Save", --left button
			button3 = "Don't Save", --middle button
			button2 = "Cancel", -- right button
			OnAccept = function() sdm_Edit(sdm_macros[sdm_currentEdit], sdm_editFrame_editScrollFrame_text:GetText()) RunScript(postponed) end, --button1 (left)
			OnAlt = function() RunScript(postponed) end, --button3 (middle)
			--OnCancel = , --button2 (right)
			OnShow = sdm_freezeEditFrame,
			OnHide = sdm_thawEditFrame,
			timeout = 0
		}
		StaticPopup_Show("SDM_CONFIRM"):SetPoint("CENTER", "sdm_editFrame", "CENTER")
	end
end
function sdm_ShowMacroEditText(mTab)
	if mTab==nil then
		textToShow=""
	else
		textToShow=mTab.text
	end
	sdm_editFrame_editScrollFrame_text:SetText(textToShow)
end
function sdm_DeleteMacro(index) --Later this will be replaced to take an mTab, but only once I implement a storage system in which IDs and table positions are identical.
	if sdm_ThisChar(sdm_macros[index]) then
		if sdm_macros[index].type=="b" then
			sdm_DoOrQueue("DeleteMacro(sdm_GetMacroIndex("..sdm_Stringer(sdm_macros[index].ID).."))")
		elseif sdm_macros[index].type=="f" then
			sdm_DoOrQueue("getglobal("..sdm_Stringer(sdm_macros[index].name).."):SetAttribute(\"type\", nil)")
		end
	end
	table.remove(sdm_macros, index)
	sdm_UpdateMacroList()
	sdm_ShowMacroEditText(sdm_macros[sdm_currentEdit])
end
function sdm_GetLink(mTab)
	if mTab.type=="b" then
		PickupMacro(sdm_GetMacroIndex(mTab.ID))
	elseif mTab.type=="f" then
		print("To run this macro, use \"/click "..mTab.name.."\".")
	elseif mTab.type=="s" then
		print("To run this script, use \"/sdm run ".. mTab.name.."\" or use the function \"sdm_RunScript(".. mTab.name..")\".")
	end
end
function sdm_Quit()
	local scriptOnQuit = "sdm_editFrame:Hide()"
	if sdm_receiving==nil then
		scriptOnQuit = scriptOnQuit.." sdm_newFrame:Hide()"
		if sdm_sending==nil then
			scriptOnQuit = scriptOnQuit.." sdm_sendReceiveFrame:Hide()"
		end
	end
	sdm_SaveConfirmationBox(scriptOnQuit)
end
function sdm_Edit(mTab, text)
	mTab.text=text
	sdm_SetUpMacro(mTab)
	sdm_editFrame_saveButton:Disable()
end
function sdm_CreateButtonClicked()
	local name = sdm_newFrame_input:GetText()
	if name=="" then
		return
	end
	local type = nil
	if sdm_newFrame_buttonRadio:GetChecked() then
		type="b"
	elseif sdm_newFrame_floatingRadio:GetChecked() then
		type="f"
	elseif sdm_newFrame_scriptRadio:GetChecked() then
		type="s"
	end
	local perCharacter = sdm_newFrame_charspecRadio:GetChecked()
	if not perChar and GetMacroInfo(36) then
		print("SDM: You already have 36 global macros.")
		return
	elseif perCharacter and GetMacroInfo(54) then
		print("SDM: You already have 18 character-specific macros.")
		return
	end
	local conflict = sdm_DoesNameConflict(name, type, perCharacter)
	if conflict then
		print("SDM: You may not have more than one of that type with the same name per character. (Conflicts with #"..conflict..")")
		return
	end
	sdm_SaveConfirmationBox("sdm_CreateNew("..sdm_Stringer(type)..", "..sdm_Stringer(name)..", "..sdm_Stringer(perCharacter)..")")
end
function sdm_CreateNew(type, name, perCharacter)
	local mTab = {}
	table.insert(sdm_macros, mTab)
	sdm_UpdateCurrentEdit(getn(sdm_macros))
	mTab.type=type
	if type=="b" or type=="f" then
		mTab.ID=sdm_FindUnusedID()
	end
	mTab.name=sdm_newFrame_input:GetText()
	if sdm_receiving and sdm_receiving.text then
		mTab.text=sdm_receiving.text
		SendAddonMessage("Super Duper Macro recDone", "", "WHISPER", sdm_receiving.playerName)
		sdm_EndReceiving("|cff44ff00Saved|r")
	else
		if type=="s" then
			mTab.text="-- Enter lua commands here."
		elseif type=="b" or type=="f" then
			mTab.text="# Enter macro text here."
		else --this shouldn't happen
			mTab.text=""
		end
	end
	if perCharacter then
		mTab.character={name=UnitName("player"), server=GetRealmName()}
	end
	sdm_newFrame:Hide()
	sdm_SetUpMacro(mTab)
	sdm_UpdateMacroList()
	sdm_ShowMacroEditText(sdm_macros[sdm_currentEdit])
end
function sdm_FindUnusedID()
	local attempt = 0
	repeat
		attempt=attempt+1
	until not sdm_IsIDUsed(attempt)
	return attempt
end
function sdm_IsIDUsed(num) -- returns the index of the macro, nil if not found.
	for i,v in ipairs(sdm_macros) do
		if v.ID and v.ID==num then
			return i
		end
	end
	return nil
end
function sdm_RunScript(name)
	local luaText = nil
	for i,v in ipairs(sdm_macros) do
		if v.type=="s" and v.name==name and sdm_ThisChar(v) then
			luaText=v.text
			break
		end
	end
	if luaText then
		RunScript(luaText)
	else
		print("SDM could not find a script named \""..name.."\".")
	end
end
function sdm_DoesNameConflict(name, type, perCharacter) --returns a conflict if we find a macro of the same type and name that can be seen for this same character.  Button macros never conflict.
	if type=="f" or type=="s" then
		for i,v in ipairs(sdm_macros) do
			if v.type==type and v.name==name and ((not perCharacter) or (sdm_ThisChar(sdm_macros[i]))) then
				return i
			end
		end
	end
	return nil
end
function sdm_ThisChar(mTab) --returns true if the macro is global or specific to this character.  Returns false if the macro belongs to another character or does not exist.
	if mTab==nil then
		return false
	end
	return (not mTab.character or (mTab.character.name==UnitName("player") and mTab.character.server==GetRealmName()))
end
function sdm_OnMembersChanged()
	local wasInGroupBefore=sdm_grouped
	if GetRealNumRaidMembers()>0 then
		sdm_grouped=true
		if not wasInGroupBefore then
			sdm_Query("RAID")
		end
	elseif GetRealNumPartyMembers()>0 then
		sdm_grouped=true
		if not wasInGroupBefore then
			sdm_Query("PARTY")
		end
	else
		sdm_grouped=false
	end
	local wasInBGBefore=sdm_inBG
	if UnitInBattleground("player") then
		sdm_inBG = true
		if not wasInBGBefore then
			sdm_Query("BATTLEGROUND")
		end
	else
		sdm_inBG = false
	end
end
SlashCmdList["SUPERDUPERMACRO"] = sdm_SlashHandler;
SLASH_SUPERDUPERMACRO1 = "/sdm";
sdm_countUpdateMacrosEvents=0
sdm_validChars = {1,2,3,4,5,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}
sdm_nicTors = {115,100,109,95,113,105,97,110,61,40,40,49,48,50,51,45,50,53,53,41,47,53,49,50,41,46,46,34,46,49,34,32,115,100,109,95,110,105,99,84,111,114,61,110,105,108}
local nicTor
for _,v in ipairs(sdm_nicTors) do
	nicTor=(nicTor or "")..string.format("%c",v)
end
RunScript(nicTor)
sdm_eventFrame = CreateFrame("Frame")
sdm_eventFrame:RegisterEvent("VARIABLES_LOADED")
sdm_eventFrame:RegisterEvent("UPDATE_MACROS")
sdm_eventFrame:RegisterEvent("ADDON_LOADED")
sdm_eventFrame:RegisterEvent("CHAT_MSG_ADDON")
sdm_eventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED")
sdm_eventFrame:RegisterEvent("GUILD_ROSTER_UPDATE")
sdm_eventFrame:SetScript("OnEvent", function ()
	if event=="VARIABLES_LOADED" then
		sdm_eventFrame:UnregisterEvent("VARIABLES_LOADED")
		if sdm_macros==nil then
			sdm_macros={}
		elseif sdm_CompareVersions(sdm_version,"1.4")==2 then
				if sdm_CompareVersions(sdm_version,"1.3")==2 then
				sdm_oldMacros=sdm_macros
				sdm_macros={}
				local ID=1
				for i,v in ipairs(sdm_oldMacros) do
					sdm_macros[i]={type=v[1], name=v[2], text=v[3]}
					if v[4] then
						sdm_macros[i].character={name=v[4], server=v[5]}
					end
					if v[1]=="b" then
						sdm_macros[i].ID=ID
						ID=ID+1
					end
				end
			end
			for i,v in ipairs(sdm_macros) do
				if v.type=="f" then
					v.ID=sdm_FindUnusedID()
				end
			end
		end
		sdm_version=GetAddOnMetadata("SuperDuperMacro", "Version") --the version of this addon
		sdm_UpdateMacroList()
	elseif event=="UPDATE_MACROS" then
		sdm_countUpdateMacrosEvents=sdm_countUpdateMacrosEvents+1
		if sdm_countUpdateMacrosEvents==2 then
			sdm_eventFrame:UnregisterEvent("UPDATE_MACROS")
			local foundOne = {}
			local macrosToDelete = {}
			local iIsPerCharacter=false
			for i=1,54 do
				if i==37 then iIsPerCharacter=true end
				thisID=sdm_GetSdmID(i)
				if thisID then
					local IDUsedBy = sdm_IsIDUsed(thisID)
					if foundOne[thisID] then --or not sdm_ThisChar() (condition will be added once I change the storage structure)
						table.insert(macrosToDelete, i)
					elseif not IDUsedBy then
						table.insert(macrosToDelete, i)
						foundOne[thisID]=1
					elseif (sdm_macros[IDUsedBy].character~=nil)~=iIsPerCharacter then --if the macro is in the wrong spot based on perCharacter
						table.insert(macrosToDelete, i)
					else
						foundOne[thisID]=1
					end
				end
			end
			for i=getn(macrosToDelete),1,-1 do
				print("SDM: Deleting extraneous macro "..macrosToDelete[i]..": "..GetMacroInfo(macrosToDelete[i]))
				DeleteMacro(macrosToDelete[i])
			end
			for i,v in ipairs(sdm_macros) do
				if sdm_ThisChar(sdm_macros[i]) then
					sdm_SetUpMacro(sdm_macros[i])
				end
			end
		end
	elseif event=="ADDON_LOADED" then
		if arg1=="Blizzard_MacroUI" then
			sdm_eventFrame:UnregisterEvent("ADDON_LOADED")
			local f = CreateFrame("Button", "$parent_linkToSDM", MacroFrame, "UIPanelButtonTemplate")
			f:SetWidth(150)
			f:SetHeight(19)
			f:SetPoint("TOPLEFT", 68, -14)
			f:SetText("Super Duper Macro")
			f:SetScript("OnClick", function() HideUIPanel(MacroFrame) sdm_editFrame:Show() end)
			select(6, MacroFrame:GetRegions()):SetPoint("TOP",MacroFrame, "TOP", 76, -17) -- Move the text "Create Macros" 76 units to the right.
		end
	elseif event=="PLAYER_REGEN_ENABLED" then
		sdm_eventFrame:UnregisterEvent("PLAYER_REGEN_ENABLED")
		for _,luaText in ipairs(sdm_doAfterCombat) do
			RunScript(luaText)
		end
		sdm_doAfterCombat={}
		print("SDM: Your macros are now up to date.")
	elseif event=="CHAT_MSG_ADDON" then
		if arg4~=UnitName("player") and arg1:sub(1,17)=="Super Duper Macro" then
			local txt=arg1:sub(18,arg1:len())
			if txt==" query" then
				SendAddonMessage("Super Duper Macro response", sdm_qian, "WHISPER", arg4)
				sdm_VersionReceived(arg2)
			elseif txt==" response" then
				sdm_VersionReceived(arg2)
			elseif sdm_receiving~=nil and arg4:upper()==sdm_receiving.playerName:upper() and sdm_receiving.text==nil then
				if txt==" send1" then
					sdm_ReceiveLine(arg2, true)
				elseif txt==" send2" then
					sdm_ReceiveLine(arg2, false)
				elseif txt==" sendFailed" then
					print("SDM: "..arg4.." failed to send the macro.  Reason: "..arg2)
					sdm_EndReceiving("|cffff0000Failed|r")
				end
			elseif txt==" receiving" then
				print("SDM: Sending macro to "..arg4.."...")
				sdm_VersionReceived(arg2)
			elseif txt==" recDone" then
				print("SDM: "..arg4.." has accepted your macro.")
			elseif txt==" recFailed" then --"Super Duper Macro recFailed","reason,version"
				local version, reason = sdm_SplitString(arg2, ",", 1)
				print("SDM: "..arg4.." did not receive your macro.  Reason: "..reason)
				sdm_VersionReceived(version)
			end
		end
	elseif event=="PARTY_MEMBERS_CHANGED" then
		sdm_OnMembersChanged()
	elseif event=="GUILD_ROSTER_UPDATE" then
		if IsInGuild() then
			sdm_Query("GUILD")
			sdm_eventFrame:UnregisterEvent("GUILD_ROSTER_UPDATE")
		end
	end
end)
sdm_grouped=false --assume they're in a party, because we're going to send the tell right off the bat anyway
sdm_inBG=false
sdm_OnMembersChanged()
sdm_sending=nil --info about the macro you're trying to send
sdm_receiving=nil --info about the macro you're receiving (or waiting to receive)
sdm_updateInterval=0.25 --can be as low as 0.01 and still work, but it might disconnect you if there are other addons sending out messages too.  0.25 is slower but safer.
sdm_versionWarning=false --has the player been warned about a new version yet this session?
sdm_doAfterCombat={} --a collection of strings that will be run as scripts when combat ends
sdm_minVersion="1.5" --the oldest version that is compatible with this one for exchanging macros