Quantcast

* Holy Shitballs.. way too much to list.. I need to commit more often.

James Whitehead II [01-01-07 - 09:00]
* Holy Shitballs.. way too much to list.. I need to commit more often.
Filename
PerfectRaid.lua
PerfectRaid.toc
PerfectRaid.xml
PerfectRaid_Aggro.lua
PerfectRaid_Buffs.lua
PerfectRaid_Range.lua
diff --git a/PerfectRaid.lua b/PerfectRaid.lua
index 67cdf96..f6fe613 100644
--- a/PerfectRaid.lua
+++ b/PerfectRaid.lua
@@ -53,14 +53,14 @@ function PerfectRaid:Initialize()
 		ColorSeverity = true,
 	}

-	self.db = self:InitializeDB("PerfectRaidDB", self.defaults)
-
 	if not ClickCastFrames then
 		ClickCastFrames = {}
 	end
 end

 function PerfectRaid:Enable()
+	self.db = self:InitializeDB("PerfectRaidDB", self.defaults)
+
 	self:RegisterEvent("RAID_ROSTER_UPDATE")
 	self:RegisterEvent("UNIT_HEALTH")
 	self:RegisterEvent("UNIT_MAXHEALTH")
diff --git a/PerfectRaid.toc b/PerfectRaid.toc
index b0dff01..69c2f8a 100644
--- a/PerfectRaid.toc
+++ b/PerfectRaid.toc
@@ -11,6 +11,7 @@ Localization.enUS.lua
 PerfectRaid.xml
 PerfectRaid.lua

+PerfectRaid_Options.lua
 PerfectRaid_Buffs.lua
 PerfectRaid_Range.lua
 PerfectRaid_Aggro.lua
diff --git a/PerfectRaid.xml b/PerfectRaid.xml
index e770db0..95ad56a 100644
--- a/PerfectRaid.xml
+++ b/PerfectRaid.xml
@@ -57,7 +57,7 @@
 						<Anchor point="BOTTOMRIGHT" relativeTo="$parentBotRight" relativePoint="TOPLEFT"/>
 					</Anchors>
 				</Texture>
-				<FontString name="$parentTitle" inherits="GameFontHighlightSmall" text="Sample Options Frame">
+				<FontString name="$parentTitle" inherits="GameFontHighlightSmall" text="PerfectRaid Configuration">
 					<Anchors>
 						<Anchor point="CENTER" relativeTo="$parentTop" relativePoint="CENTER">
 							<Offset><AbsDimension x="0" y="7"/></Offset>
@@ -68,6 +68,7 @@
 		</Layers>
 		<Scripts>
 			<OnLoad>
+				table.insert(UISpecialFrames,self:GetName())
 				self.Title = getglobal(self:GetName().."Title")
 				local title = self:CreateTitleRegion()
 				title:SetPoint("TOPLEFT", self, "TOPLEFT", 5, 0)
@@ -120,30 +121,17 @@
 				self.Name = getglobal(name.."Name")
 			</OnLoad>
 			<OnClick>
-				local parent = self:GetParent()
-				local old = parent.TabSelected
-				if old then
-					old.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabLeftInactive")
-					old.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabMiddleInactive")
-					old.Right:SetTexture("Interface\\AddOns\\Perfectraid\\images\\OrangeTheme\\TabRightInactive")
-				end
-				self.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabLeft")
-				self.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\TabMiddle")
-				self.Right:SetTexture("Interface\\AddOns\\Perfectraid\\images\\OrangeTheme\\TabRight")
-
-				parent.TabSelected = self
-				--parent:OnTabSelected()
+				PerfectRaid:HasModule("PerfectRaid-Options"):TabOnClick(self)
 			</OnClick>
 		</Scripts>
 	</Button>
 	<CheckButton name="PRCheckTemplate" hidden="true" virtual="true">
-		<Size><AbsDimension x="32" y="32"/></Size>
+		<Size><AbsDimension x="20" y="20"/></Size>
 		<NormalTexture file="Interface\AddOns\PerfectRaid\images\OrangeTheme\CheckButton"/>
 		<CheckedTexture file="Interface\AddOns\PerfectRaid\images\OrangeTheme\CheckButtonChecked"/>
-		<HighlightTexture file="Interface\AddOns\PerfectRaid\images\OrangeTheme\CheckButtonChecked"/>
 		<Layers>
 			<Layer level="ARTWORK">
-				<FontString name="$parentTitle" inherits="GameFontHighlightSmall" text="Check Label">
+				<FontString name="$parentLabel" inherits="GameFontHighlightSmall" text="Check Label">
 					<Anchors>
 						<Anchor point="LEFT" relativePoint="RIGHT">
 							<Offset><AbsDimension x="5" y="0"/></Offset>
@@ -152,34 +140,21 @@
 				</FontString>
 			</Layer>
 		</Layers>
+		<Scripts>
+			<OnLoad>
+				self.Label = getglobal(self:GetName().."Label")
+			</OnLoad>
+		</Scripts>
 	</CheckButton>
-	<Button name="PRButtonTemplate" hidden="true" virtual="true">
-		<Size><AbsDimension x="96" y="32"/></Size>
+	<Slider name="PRSliderTemplate" inherits="OptionsSliderTemplate" hidden="true" virtual="true">
+		<Size><AbsDimension x="160" y="16"/></Size>
 		<Layers>
-			<Layer level="BACKGROUND">
-				<Texture name="$parentLeft" file="Interface\AddOns\PerfectRaid\images\OrangeTheme\ButtonNormalLeft">
-					<Size><AbsDimension x="32" y="32"/></Size>
-					<Anchors>
-						<Anchor point="BOTTOMLEFT"/>
-					</Anchors>
-				</Texture>
-				<Texture name="$parentRight" file="Interface\AddOns\PerfectRaid\images\OrangeTheme\ButtonNormalRight">
-					<Size><AbsDimension x="32" y="32"/></Size>
-					<Anchors>
-						<Anchor point="BOTTOMRIGHT"/>
-					</Anchors>
-				</Texture>
-				<Texture name="$parentMiddle" file="Interface\AddOns\PerfectRaid\images\OrangeTheme\ButtonNormalMiddle">
-					<Anchors>
-						<Anchor point="TOPLEFT" relativeTo="$parentLeft" relativePoint="TOPRIGHT"/>
-						<Anchor point="BOTTOMRIGHT" relativeTo="$parentRight" relativePoint="BOTTOMLEFT"/>
-					</Anchors>
-				</Texture>
-			</Layer>
 			<Layer level="ARTWORK">
-				<FontString name="$parentLabel" inherits="GameFontHighlightSmall" text="Button Label">
+				<FontString name="$parentCurrent" inherits="GameFontHighlightSmall">
 					<Anchors>
-						<Anchor point="CENTER"/>
+						<Anchor point="LEFT" relativePoint="RIGHT">
+							<Offset><AbsDimension x="5" y="0"/></Offset>
+						</Anchor>
 					</Anchors>
 				</FontString>
 			</Layer>
@@ -187,21 +162,32 @@
 		<Scripts>
 			<OnLoad>
 				local name = self:GetName()
-				self.Left = getglobal(name.."Left")
-				self.Middle = getglobal(name.."Middle")
-				self.Right = getglobal(name.."Right")
-				self.Label = getglobal(name.."Label")
+				self.Text = getglobal(name.."Text")
+				self.High = getglobal(name.."High")
+				self.Low = getglobal(name.."Low")
+				self.Current = getglobal(name.."Current")
+			</OnLoad>
+			<OnValueChanged>
+				self.Current:SetText(string.format("%.2f", self:GetValue()))
+			</OnValueChanged>
+		</Scripts>
+	</Slider>
+	<Button name="PRButtonTemplate" virtual="true">
+		<Size><AbsDimension x="96" y="32"/></Size>
+		<ButtonText name="$parentText"/>
+		<NormalFont inherits="GameFontHighlight"/>
+		<HighlightFont inherits="GameFontNormal"/>
+		<DisabledFont inherits="GameFontDisable"/>
+		<NormalTexture inherits="UIPanelButtonDisabledTexture"/>
+		<PushedTexture inherits="UIPanelButtonDisabledDownTexture"/>
+		<DisabledTexture inherits="UIPanelButtonDisabledTexture"/>
+		<HighlightTexture inherits="UIPanelButtonHighlightTexture"/>
+		<Scripts>
+			<OnLoad>
+				self:GetNormalTexture():SetVertexColor(1, .415, .027)
+				local name = self:GetName()
+				self.Label = getglobal(name.."Text")
 			</OnLoad>
-			<OnEnter>
-				self.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonHighlightLeft")
-				self.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonHighlightMiddle")
-				self.Right:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonHighlightRight")
-			</OnEnter>
-			<OnLeave>
-				self.Left:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonNormalLeft")
-				self.Middle:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonNormalMiddle")
-				self.Right:SetTexture("Interface\\AddOns\\PerfectRaid\\images\\OrangeTheme\\ButtonNormalRight")
-			</OnLeave>
 		</Scripts>
 	</Button>
 </Ui>
\ No newline at end of file
diff --git a/PerfectRaid_Aggro.lua b/PerfectRaid_Aggro.lua
index 072135d..c6aa998 100644
--- a/PerfectRaid_Aggro.lua
+++ b/PerfectRaid_Aggro.lua
@@ -37,13 +37,26 @@ local targets = {}
 local marked = {}

 function Aggro:Initialize()
+	PerfectRaid.defaults.profile.AggroCheck = true
+	PerfectRaid.defaults.profile.AggroRate = 0.2
+
 	frames = PerfectRaid.frames
 	aggro = PerfectRaid.aggro
 end

 function Aggro:Enable()
-	local frame = CreateFrame("Frame")
-	frame:SetScript("OnUpdate", self.OnUpdate)
+	if not PerfectRaid.defaults.profile.AggroCheck then return end
+	if not self.frame then
+		self.frame = CreateFrame("Frame")
+	end
+	self.frame:SetScript("OnUpdate", self.OnUpdate)
+end
+
+function Aggro:Disable()
+	if self.frame then
+		self.frame:SetScript("OnUpdate", nil)
+	end
+	for k,v in pairs(aggro) do aggro[k] = 0 end
 end

 local elapsed = 0
@@ -95,3 +108,67 @@ function Aggro.OnUpdate()
 		elapsed = 0
 	end
 end
+
+local options
+function Aggro:CreateOptions(opt)
+	options = CreateFrame("Frame", "PROptions_Aggro", PROptions)
+	options:SetScript("OnShow", function() self:OnShow() end)
+
+	opt:AddOptionsTab("Aggro", options)
+
+	options.widgets = {}
+
+	local check = CreateFrame("CheckButton", "PRAggro_Enable", options, "PRCheckTemplate")
+	check.Label:SetText("Perform aggro checking")
+	table.insert(options.widgets, check)
+
+	local slider = CreateFrame("Slider", "PRAggro_Rate", options, "PRSliderTemplate")
+	slider.Text:SetText("Update delay")
+	slider.High:SetText("2.0")
+	slider.Low:SetText("0")
+	slider:SetMinMaxValues(0,2.0)
+	slider:SetValueStep(0.1)
+	table.insert(options.widgets, slider)
+
+	local cancel = CreateFrame("Button", "PRAggro_Cancel", options, "PRButtonTemplate")
+	cancel.Label:SetText("Cancel")
+	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
+	cancel:SetScript("OnClick", function() self:OnShow() end)
+	cancel:Show()
+
+	local save = CreateFrame("Button", "PRAggro_Save", options, "PRButtonTemplate")
+	save.Label:SetText("Save")
+	save:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -10, 0)
+	save:SetScript("OnClick", function() self:SaveOptions() end)
+	save:Show()
+
+	for idx,widget in ipairs(options.widgets) do
+		widget:Show()
+		if idx == 1 then
+			widget:SetPoint("TOPLEFT", 0, 0)
+		else
+			widget:SetPoint("TOPLEFT", options.widgets[idx - 1], "BOTTOMLEFT", 0, -15)
+		end
+	end
+end
+
+function Aggro:OnShow()
+	local profile = PerfectRaid.db.profile
+
+	PRAggro_Enable:SetChecked(profile.AggroCheck)
+	PRAggro_Rate:SetValue(profile.AggroRate)
+end
+
+function Aggro:SaveOptions()
+	local profile = PerfectRaid.db.profile
+
+	profile.AggroCheck = PRAggro_Enable:GetChecked() or false
+	profile.AggroRate = PRAggro_Rate:GetValue()
+
+	if not profile.AggroCheck then
+		self:Disable()
+	else
+		self:Enable()
+	end
+end
+
diff --git a/PerfectRaid_Buffs.lua b/PerfectRaid_Buffs.lua
index 3354d1d..906d194 100644
--- a/PerfectRaid_Buffs.lua
+++ b/PerfectRaid_Buffs.lua
@@ -30,14 +30,22 @@
 ---------------------------------------------------------------------------]]

 local Buffs = PerfectRaid:NewModule("PerfectRaid-Buffs")
+local utils = DongleStub("DongleUtils")
 local frames

 function Buffs:Initialize()
+	PerfectRaid.defaults.profile.buffs = {}
+
 	frames = PerfectRaid.frames
 end

 function Buffs:Enable()
 	self:RegisterEvent("UNIT_AURA")
+	self:UpdateBuffTable()
+
+	for unit in pairs(frames) do
+		self:UNIT_AURA(nil, unit)
+	end
 end

 function Buffs:ConfigureButton(button)
@@ -51,19 +59,7 @@ function Buffs:ShowButton(button)
 	self:UNIT_AURA(nil, unit)
 end

-local buffs = {
-	{
-		buffname = "Seal of Righteousness",
-		colortext = "|cFF339911SoR|r",
-		cond = {"ALL"},
-	},
-	{
-		buffname = "Perception",
-		colortext = "|cFFFF1111Perc|r",
-		cond = {"ALL"},
-	},
-}
-
+local buffs = {}
 local buffcache = {}
 local work = {}
 function Buffs:UNIT_AURA(event, unit)
@@ -89,11 +85,11 @@ function Buffs:UNIT_AURA(event, unit)
 		local checkcond = false
 		local num = buffcache[entry.buffname]
 		if entry.missing then
-			if not buffcache[entry.buffname] and not buffcache[entry.groupbuff or "nil"] then
+			if not buffcache[entry.buffname] and not buffcache[entry.groupname or "nil"] then
 				checkcond = true
 			end
 		else
-			if buffcache[entry.buffname] or buffcache[entry.groupbuff or "nil"] then
+			if buffcache[entry.buffname] or buffcache[entry.groupname or "nil"] then
 				checkcond = true
 			end
 		end
@@ -120,4 +116,497 @@ end

 Buffs.conditions = {
 	["ALL"] = function() return true end,
+}
+
+function Buffs:CreateOptions(opt)
+	local options = CreateFrame("Frame", "PROptions_Buffs", PROptions)
+	opt:AddOptionsTab("Buffs/Debuffs", options)
+
+	self:CreateListFrame(options, 10)
+
+	local delete = CreateFrame("Button", "PRBuffs_Delete", options, "PRButtonTemplate")
+	delete.Label:SetText("Delete")
+	delete:SetPoint("BOTTOMRIGHT", 0, 5)
+	delete:SetScript("OnClick", function() self:DeleteEntry() end)
+	delete:Show()
+
+	local edit = CreateFrame("Button", "PRBuffs_Edit", options, "PRButtonTemplate")
+	edit.Label:SetText("Edit")
+	edit:SetPoint("BOTTOMRIGHT", delete, "BOTTOMLEFT", -10, 0)
+	edit:SetScript("OnClick", function() self:EditEntry() end)
+	edit:Show()
+
+	local add = CreateFrame("Button", "PRBuffs_Add", options, "PRButtonTemplate")
+	add.Label:SetText("Add")
+	add:SetPoint("BOTTOMRIGHT", edit, "BOTTOMLEFT", -10, 0)
+	add:SetScript("OnClick", function() self:AddEntry() end)
+	add:Show()
+
+	self:CreateEditFrame(options)
+end
+
+function Buffs:CreateListFrame(parent, num)
+	local function OnClick(self)
+		local scrollframe = getglobal(parent:GetName().."ScrollFrame")
+		local parent = self:GetParent()
+
+		local offset = FauxScrollFrame_GetOffset(scrollframe)
+		local idx = offset + self.idx
+
+		if parent.selected then
+			parent.entries[parent.selected - offset]:SetChecked(nil)
+		end
+		parent.selected = idx
+		Buffs:EnableButtons()
+	end
+
+	local function MakeEntry(name, parent)
+		local entry = CreateFrame("CheckButton", name, parent)
+		entry:SetScript("OnClick", OnClick)
+		entry:SetHeight(20)
+		entry:SetWidth(400)
+		entry:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+		entry:SetCheckedTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
+		entry:GetHighlightTexture():SetBlendMode("ADD")
+		entry.line1 = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
+		entry.line1:SetPoint("LEFT", 0 ,0)
+		entry.line1:SetPoint("RIGHT", -5, 0)
+		entry.line1:SetText("Buff Name")
+		entry.line1:SetJustifyH("LEFT")
+		return entry
+	end
+
+	parent.entries = {}
+	local entries = parent.entries
+	for i=1,num do
+		entries[i] = MakeEntry(parent:GetName().."Entry"..i, parent)
+		entries[i].idx = i
+	end
+
+	entries[1]:SetPoint("TOPLEFT",0,0)
+
+	for i=2,num do
+		 entries[i]:SetPoint("TOPLEFT", entries[i-1], "BOTTOMLEFT", 0, 0)
+	end
+
+	local scrollframe = CreateFrame("ScrollFrame", parent:GetName().."ScrollFrame", parent, "FauxScrollFrameTemplate")
+	scrollframe:SetPoint("TOPLEFT", entries[1], "TOPLEFT", 0, 0)
+	scrollframe:SetPoint("BOTTOMRIGHT", entries[num], "BOTTOMRIGHT", 0, 0)
+
+	local texture = scrollframe:CreateTexture(nil, "BACKGROUND")
+	texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
+	texture:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 14, 0)
+	texture:SetPoint("BOTTOMRIGHT", scrollframe, "BOTTOMRIGHT", 23, 0)
+	texture:SetGradientAlpha("HORIZONTAL", 0.5, 0.25, 0.05, 0, 0.15,0.15, 0.15, 1)
+
+	local texture = scrollframe:CreateTexture(nil, "BACKGROUND")
+	texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
+	texture:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, 0)
+	texture:SetPoint("BOTTOMRIGHT", scrollframe, "BOTTOMRIGHT", 14, 0)
+	texture:SetGradientAlpha("HORIZONTAL", 0.15, 0.15, 0.15, 0.15, 1, 0.5, 0.25, 0.05, 0)
+
+	local update = function()
+		local list = PerfectRaid.db.profile.buffs
+
+		local idx,button
+		local offset = FauxScrollFrame_GetOffset(scrollframe)
+		FauxScrollFrame_Update(scrollframe, #list, num, 20)
+		for i=1,num do
+			idx = offset + i
+			button = entries[i]
+			if idx <= #list then
+				local entry = list[idx]
+				button.line1:SetText(entry.buffname)
+				button:Show()
+				if parent.selected == idx then
+					button:SetChecked(true)
+				else
+					button:SetChecked(false)
+				end
+			else
+				button:Hide()
+			end
+		end
+	end
+
+	scrollframe.update = update
+	scrollframe:SetScript("OnVerticalScroll", function()
+		FauxScrollFrame_OnVerticalScroll(20, update)
+		self:EnableButtons()
+	end)
+	scrollframe:SetScript("OnShow", function()
+		update()
+		self:EnableButtons()
+	end)
+end
+
+function Buffs:CreateEditFrame(parent)
+	local frame = CreateFrame("Frame", "PROptions_Buffs_Edit", PROptions_Buffs)
+	local name = "PROptions_Buffs_Edit"
+--	frame:SetPoint("TOPLEFT", 15, -25)
+--	frame:SetPoint("BOTTOMRIGHT", -15, 15)
+	frame:SetAllPoints()
+	frame:SetFrameLevel(frame:GetFrameLevel() + 1)
+	frame:Hide()
+
+	local bg = frame:CreateTexture(nil, "ARTWORK")
+	bg:SetAllPoints()
+	bg:SetTexture(0.2, 0.2, 0.2)
+
+	PROptions_Buffs:SetScript("OnHide", function() self:CancelEntry() end)
+
+	local buffname = CreateFrame("EditBox", name.."BuffName", frame, "InputBoxTemplate")
+	local font = buffname:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText("Buff Name:")
+	font:SetPoint("BOTTOMLEFT", buffname, "TOPLEFT", 0, 3)
+	buffname:SetPoint("TOPLEFT", 0, -10)
+	buffname:SetAutoFocus(nil)
+	buffname:SetWidth(180)
+	buffname:SetHeight(20)
+	buffname:Show()
+	frame.buffname = buffname
+
+	local groupname = CreateFrame("EditBox", name.."GroupName", frame, "InputBoxTemplate")
+	local font = groupname:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText("Group Buff Name:")
+	font:SetPoint("BOTTOMLEFT", groupname, "TOPLEFT", 0, 3)
+	groupname:SetPoint("TOPLEFT", 220, -10)
+	groupname:SetAutoFocus(nil)
+	groupname:SetWidth(180)
+	groupname:SetHeight(20)
+	groupname:Show()
+	frame.groupname = groupname
+
+	local disptext = CreateFrame("EditBox", name.."DispText", frame, "InputBoxTemplate")
+	local font = disptext:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText("Display Text:")
+	font:SetPoint("BOTTOMLEFT", disptext, "TOPLEFT", 0, 3)
+	disptext:SetPoint("TOPLEFT", 0, -50)
+	disptext:SetAutoFocus(nil)
+	disptext:SetWidth(180)
+	disptext:SetHeight(20)
+	disptext:Show()
+	frame.disptext = disptext
+
+	-- Color Swatch
+	local function colorSwatchCancel()
+		local self = ColorPickerFrame.object;
+		local r, g, b = self.r, self.g, self.b;
+		self.normalTexture:SetVertexColor(r, g, b);
+	end
+
+	local function colorSwatchColor()
+		local self = ColorPickerFrame.object;
+		local r, g, b = ColorPickerFrame:GetColorRGB();
+		self.normalTexture:SetVertexColor(r, g, b);
+		self.color[1] = r
+		self.color[2] = g
+		self.color[3] = b
+		disptext:SetTextColor(r,g,b)
+	end
+
+	local function colorSwatchOpacity()
+		local self = ColorPickerFrame.object;
+		local a = OpacitySliderFrame:GetValue();
+	end
+
+	local function colorSwatchShow(self)
+		local r, g, b, a;
+		self.color = self.color or {1,1,1}
+
+		local color = self.color;
+		if ( color ) then
+			r, g, b, a = unpack(color);
+		end
+
+		self.r, self.g, self.b = r, g, b
+		self.opacityFunc = colorSwatchOpacity;
+		self.swatchFunc = colorSwatchColor;
+		self.cancelFunc = colorSwatchCancel;
+
+		ColorPickerFrame.object = self;
+		UIDropDownMenuButton_OpenColorPicker(self);
+		ColorPickerFrame:SetFrameStrata("TOOLTIP");
+		ColorPickerFrame:Raise();
+	end
+
+	local function colorSwatchOnClick(self)
+		CloseMenus();
+		colorSwatchShow(self);
+	end
+
+	local function colorSwatchOnEnter(self)
+		self.bg:SetVertexColor(1, 0.82, 0);
+	end
+
+	local function colorSwatchOnLeave(self)
+		self.bg:SetVertexColor(1, 1, 1);
+	end
+
+	local swatch = CreateFrame("Button", "PRColorSelect", frame);
+	swatch:SetHeight(20) swatch:SetWidth(20)
+	swatch:SetPoint("LEFT", disptext, "RIGHT", 5, 0)
+	local bg = swatch:CreateTexture(nil, "BACKGROUND");
+	local normalTexture = swatch:CreateTexture(nil, "ARTWORK");
+	frame.color = swatch
+
+	normalTexture:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch");
+	normalTexture:SetAllPoints(swatch);
+	swatch:SetNormalTexture(normalTexture);
+	bg:SetTexture(.2,.2,.2);
+	bg:SetPoint("TOPLEFT", swatch, 1, -1);
+	bg:SetPoint("BOTTOMRIGHT", swatch, 0, 1);
+
+	normalTexture:SetVertexColor(1,1,1)
+
+	swatch.bg, swatch.normalTexture = bg, normalTexture;
+	swatch.object, swatch.hasAlpha = self, 1
+
+	swatch:SetScript("OnLeave", colorSwatchOnLeave);
+	swatch:SetScript("OnEnter", colorSwatchOnEnter);
+	swatch:SetScript("OnClick", colorSwatchOnClick);
+
+	buffname:SetScript("OnTabPressed", function() if IsShiftKeyDown() then disptext:SetFocus() else groupname:SetFocus() end end)
+	groupname:SetScript("OnTabPressed", function() if IsShiftKeyDown() then buffname:SetFocus() else disptext:SetFocus() end end)
+	disptext:SetScript("OnTabPressed", function() if IsShiftKeyDown() then groupname:SetFocus() else buffname:SetFocus() end end)
+
+	function makeCheck(value)
+		local button = CreateFrame("CheckButton", "PRBuffCond_"..value, PROptions_Buffs_Edit, "PRCheckTemplate")
+		button.Label:SetText(value)
+		button.value = value
+		return button
+	end
+
+	PROptions_Buffs_Edit.checks = {}
+	local checks = PROptions_Buffs_Edit.checks
+
+	-- Build condition checkboxes
+
+	checks[1] = makeCheck(self.conditions[1])
+	checks[1]:SetPoint("TOPLEFT", disptext, "BOTTOMLEFT", 0, -20)
+	checks[1]:Show()
+
+	for i=2,5 do
+		checks[i] = makeCheck(self.conditions[i])
+		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
+		checks[i]:Show()
+	end
+
+	checks[6] = makeCheck(self.conditions[6])
+	checks[6]:SetPoint("TOPLEFT", checks[1], "BOTTOMLEFT", 0, -20)
+	checks[6]:Show()
+
+	for i=7,10 do
+		checks[i] = makeCheck(self.conditions[i])
+		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
+		checks[i]:Show()
+	end
+
+	checks[11] = makeCheck(self.conditions[11])
+	checks[11]:SetPoint("TOPLEFT", checks[6], "BOTTOMLEFT", 0, -20)
+	checks[11]:Show()
+
+	for i=12,13 do
+		checks[i] = makeCheck(self.conditions[i])
+		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
+		checks[i]:Show()
+	end
+
+	local cancel = CreateFrame("Button", "PRBuffs_EditCancel", PROptions_Buffs_Edit, "PRButtonTemplate")
+	cancel.Label:SetText("Cancel")
+	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
+	cancel:SetScript("OnClick", function() self:CancelEntry() end)
+	cancel:Show()
+
+	local save = CreateFrame("Button", "PRBuffs_EditSave", PROptions_Buffs_Edit, "PRButtonTemplate")
+	save.Label:SetText("Save")
+	save:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -10, 0)
+	save:SetScript("OnClick", function() self:SaveEntry() end)
+	save:Show()
+
+	local missing = CreateFrame("CheckButton", "PRBuffs_Missing", PROptions_Buffs_Edit, "PRCheckTemplate")
+	missing.Label:SetText("Only show if this buff is missing")
+	missing:SetPoint("BOTTOMLEFT", 0, 10)
+	missing:Show()
+	frame.missing = missing
+end
+
+function Buffs:EditEntry()
+	local scrollframe = PROptions_BuffsScrollFrame
+	local offset = FauxScrollFrame_GetOffset(scrollframe)
+	local selected = PROptions_Buffs.selected
+	local idx = offset + selected
+	local entry = PerfectRaid.db.profile.buffs[idx]
+
+	local options = PROptions_Buffs_Edit
+	options.editEntry = entry
+
+	options:Show()
+	options.buffname:SetText(entry.buffname)
+	options.groupname:SetText(entry.groupname)
+	options.disptext:SetText(entry.disptext)
+	options.disptext:SetTextColor(utils.HexToRGBPerc(entry.color))
+	PRColorSelect.normalTexture:SetVertexColor(utils.HexToRGBPerc(entry.color))
+
+	local cond = {string.split(",", entry.conds)}
+	for k,v in ipairs(cond) do
+		cond[v] = true
+	end
+	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
+		if cond[button.value] then
+			button:SetChecked()
+		end
+	end
+
+	options.missing:SetChecked(entry.missing)
+end
+
+function Buffs:AddEntry()
+	local options = PROptions_Buffs_Edit
+	options:Show()
+	options.buffname:SetText("")
+	options.groupname:SetText("")
+	options.disptext:SetText("")
+	options.disptext:SetTextColor(1,1,1)
+	PRColorSelect.normalTexture:SetVertexColor(1,1,1)
+
+	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
+		button:SetChecked(false)
+	end
+	options.missing:SetChecked(false)
+	options.buffname:SetFocus()
+end
+
+function Buffs:DeleteEntry()
+	local scrollframe = PROptions_BuffsScrollFrame
+	local offset = FauxScrollFrame_GetOffset(scrollframe)
+	local selected = PROptions_Buffs.selected
+	local idx = offset + selected
+	table.remove(PerfectRaid.db.profile.buffs, idx)
+	PROptions_Buffs.selected = nil
+	scrollframe.update()
+	self:EnableButtons()
+	self:UpdateBuffTable()
+end
+
+function Buffs:UpdateBuffTable()
+	for k,v in pairs(buffs) do buffs[k] = nil end
+
+	for idx,entry in ipairs(PerfectRaid.db.profile.buffs) do
+		local tbl = {}
+		tbl.buffname = entry.buffname
+		tbl.groupname = entry.groupname
+		tbl.colortext = "|cFF"..entry.color..entry.disptext.."|r"
+		tbl.missing = entry.missing
+		tbl.cond = {string.split(",", entry.conds)}
+		table.insert(buffs, tbl)
+	end
+
+	for unit in pairs(frames) do
+		self:UNIT_AURA(nil, unit)
+	end
+end
+
+function Buffs:SaveEntry()
+	local frame = PROptions_Buffs_Edit
+
+	local buffname = frame.buffname:GetText()
+	local groupname = frame.groupname:GetText()
+	local disptext = frame.disptext:GetText()
+
+	local err
+	if buffname == "" then
+		err = "You must supply a buff name"
+	elseif disptext == "" then
+		err = "You must supply a display text"
+	end
+
+	if err then
+		StaticPopupDialogs["PR_BUFF_SAVE_ERROR"].text = err
+		StaticPopup_Show("PR_BUFF_SAVE_ERROR")
+		return
+	end
+
+	local options = PROptions_Buffs_Edit
+	local entry = options.editEntry or {}
+
+	entry.buffname = buffname
+	entry.groupname = groupname
+	entry.disptext = disptext
+
+	local work = {}
+	for idx,button in ipairs(PROptions_Buffs_Edit.checks) do
+		if button:GetChecked() then
+			table.insert(work, button.value)
+		end
+	end
+
+	local conds = strjoin(",", unpack(work))
+	entry.conds = conds
+	entry.missing = frame.missing:GetChecked()
+
+	local color = utils.RGBPercToHex(frame.disptext:GetTextColor())
+	entry.color = color
+	if not options.editEntry then
+		table.insert(PerfectRaid.db.profile.buffs, entry)
+	end
+
+	options.editEntry = nil
+
+	self:CancelEntry()
+	PROptions_BuffsScrollFrame.update()
+	self:UpdateBuffTable()
+end
+
+function Buffs:CancelEntry()
+	local options = PROptions_Buffs_Edit
+	options:Hide()
+	options.editEntry = nil
+end
+
+function Buffs:EnableButtons()
+	local selected = PROptions_Buffs.selected
+	if selected then
+		PRBuffs_Edit:Enable()
+		PRBuffs_Delete:Enable()
+	else
+		PRBuffs_Edit:Disable()
+		PRBuffs_Delete:Disable()
+	end
+end
+
+Buffs.conditions = {
+	["All"] = function(u,c) return true end,
+	["Warrior"] = function(u,c) return c == "WARRIOR" end,
+	["Priest"] = function(u,c) return c == "PRIEST" end,
+	["Druid"] = function(u,c) return c == "DRUID" end,
+	["Paladin"] = function(u,c) return c == "PALADIN" end,
+	["Shaman"] = function(u,c) return c == "SHAMAN" end,
+	["Hunter"] = function(u,c) return c == "HUNTER" end,
+	["Rogue"] = function(u,c) return c == "ROGUE" end,
+	["Warlock"] = function(u,c) return c == "WARLOCK" end,
+	["Mage"] = function(u,c) return c == "MAGE" end,
+	["Mana"] = function(u,c) return c == "DRUID" or c == "PRIEST" or c == "PALADIN" or c == "SHAMAN" or c == "MAGE" or c == "WARLOCK" or c == "HUNTER" end,
+	["Caster"] = function(u,c) return c == "MAGE" or c == "WARLOCK" or c == "SHAMAN" or c == "DRUID" end,
+	["Melee"] = function(u,c) return c == "WARRIOR" or c == "ROGUE" end,
+	["Healer"] = function(u,c) return c == "PRIEST" or c == "SHAMAN" or c == "DRUID" or c == "PALADIN" end,
+}
+
+local work = {}
+for k,v in pairs(Buffs.conditions) do
+	table.insert(work, k)
+end
+table.sort(work)
+
+for k,v in ipairs(work) do
+	Buffs.conditions[k] = v
+end
+
+StaticPopupDialogs["PR_BUFF_SAVE_ERROR"] = {
+	text = "",
+	button1 = TEXT(OKAY),
+	OnAccept = function()
+	end,
+	timeout = 0,
+	hideOnEscape = 1
 }
\ No newline at end of file
diff --git a/PerfectRaid_Range.lua b/PerfectRaid_Range.lua
index 5fa8ce6..bd92537 100644
--- a/PerfectRaid_Range.lua
+++ b/PerfectRaid_Range.lua
@@ -33,10 +33,15 @@ local Range = PerfectRaid:NewModule("PerfectRaid-Range")
 local frames, rangespell

 function Range:Initialize()
+	PerfectRaid.defaults.profile.RangeCheck = true
+	PerfectRaid.defaults.profile.RangeRate = 0.2
+	PerfectRaid.defaults.profile.RangeAlpha = 0.3
+
 	frames = PerfectRaid.frames
 end

 function Range:Enable()
+	if not PerfectRaid.defaults.profile.RangeCheck then return end
 	local class = select(2, UnitClass("player"))
 	local spells = {
 		DRUID = "Healing Touch",
@@ -46,9 +51,22 @@ function Range:Enable()
 	}

 	rangespell = spells[class]
-
-	local frame = CreateFrame("Frame")
-	frame:SetScript("OnUpdate", self.OnUpdate)
+
+	if not self.frame then
+		self.frame = CreateFrame("Frame")
+	end
+	self.frame:SetScript("OnUpdate", self.OnUpdate)
+end
+
+function Range:Disable()
+	if self.frame then
+		self.frame:SetScript("OnUpdate", nil)
+	end
+	for unit,tbl in pairs(frames) do
+		for frame in pairs(tbl) do
+			frame:SetAlpha(1.0)
+		end
+	end
 end

 local elapsed = 0
@@ -65,3 +83,77 @@ function Range.OnUpdate()
 		elapsed = 0
 	end
 end
+
+local options
+function Range:CreateOptions(opt)
+	options = CreateFrame("Frame", "PROptions_Range", PROptions)
+	options:SetScript("OnShow", function() self:OnShow() end)
+
+	opt:AddOptionsTab("Range Check", options)
+
+	options.widgets = {}
+
+	local check = CreateFrame("CheckButton", "PRRange_Enable", options, "PRCheckTemplate")
+	check.Label:SetText("Perform range checking")
+	table.insert(options.widgets, check)
+
+	local slider = CreateFrame("Slider", "PRRange_Rate", options, "PRSliderTemplate")
+	slider.Text:SetText("Update delay")
+	slider.High:SetText("2.0")
+	slider.Low:SetText("0")
+	slider:SetMinMaxValues(0,2.0)
+	slider:SetValueStep(0.1)
+	table.insert(options.widgets, slider)
+
+	local slider = CreateFrame("Slider", "PRRange_Alpha", options, "PRSliderTemplate")
+	slider.Text:SetText("Out-of-Range Alpha")
+	slider.High:SetText("1")
+	slider.Low:SetText("0")
+	slider:SetMinMaxValues(0,1)
+	slider:SetValueStep(0.05)
+	table.insert(options.widgets, slider)
+
+	local cancel = CreateFrame("Button", "PRRange_Cancel", options, "PRButtonTemplate")
+	cancel.Label:SetText("Cancel")
+	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
+	cancel:SetScript("OnClick", function() self:OnShow() end)
+	cancel:Show()
+
+	local save = CreateFrame("Button", "PRRange_Save", options, "PRButtonTemplate")
+	save.Label:SetText("Save")
+	save:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -10, 0)
+	save:SetScript("OnClick", function() self:SaveOptions() end)
+	save:Show()
+
+	for idx,widget in ipairs(options.widgets) do
+		widget:Show()
+		if idx == 1 then
+			widget:SetPoint("TOPLEFT", 0, 0)
+		else
+			widget:SetPoint("TOPLEFT", options.widgets[idx - 1], "BOTTOMLEFT", 0, -15)
+		end
+	end
+end
+
+function Range:OnShow()
+	local profile = PerfectRaid.db.profile
+
+	PRRange_Enable:SetChecked(profile.RangeCheck)
+	PRRange_Rate:SetValue(profile.RangeRate)
+	PRRange_Alpha:SetValue(profile.RangeAlpha)
+end
+
+function Range:SaveOptions()
+	local profile = PerfectRaid.db.profile
+
+	profile.RangeCheck = PRRange_Enable:GetChecked() or false
+	profile.RangeRate = PRRange_Rate:GetValue()
+	profile.RangeAlpha = PRRange_Alpha:GetValue()
+
+	if profile.RangeCheck then
+		self:Enable()
+	else
+		self:Disable()
+	end
+end
+