Quantcast

Initial commit of frame editor

James Whitehead II [01-15-07 - 23:19]
Initial commit of frame editor
Allow multiple range checks spells, and allow the user to customize each level, and the spells used
A number of changes elsewhere.
Filename
Localization.enUS.lua
PerfectRaid.lua
PerfectRaid_Aggro.lua
PerfectRaid_Buffs.lua
PerfectRaid_Frames.lua
PerfectRaid_Hacks.lua
PerfectRaid_Range.lua
diff --git a/Localization.enUS.lua b/Localization.enUS.lua
index 6951988..a2a1438 100644
--- a/Localization.enUS.lua
+++ b/Localization.enUS.lua
@@ -4,6 +4,8 @@ PerfectRaidLocals = {
 	["Add"] = "Add",
 	["Adding defaults to new profile \"%s\""] = "Adding defaults to new profile \"%s\"",
 	["Aggro"] = "Aggro",
+	["Align frames to BOTTOM"] = "Align frames to BOTTOM",
+	["Align frames to RIGHT"] = "Align frames to RIGHT",
 	["Arcane Brilliance"] = "Arcane Brilliance",
 	["Arcane Intellect"] = "Arcane Intellect",
 	["Auto-fill Default"] = "Auto-fill Default",
@@ -16,14 +18,18 @@ PerfectRaidLocals = {
 	["Buff Name:"] = "Buff Name:",
 	["Buffs/Debuffs"] = "Buffs/Debuffs",
 	["Cancel"] = "Cancel",
+	["Color bars by class"] = "Color bars by class",
+	["Column Anchor Point:"] = "Column Anchor Point:",
 	["Dead"] = "Dead",
 	["Delete"] = "Delete",
 	["Display Text:"] = "Display Text:",
+	["Display a header backdrop"] = "Display a header backdrop",
 	["Divine Spirit"] = "Divine Spirit",
 	["Do not check this buff (Disable)"] = "Do not check this buff (Disable)",
+	["Druid"] = "Druid",
 	["Edit"] = "Edit",
 	["Fear Ward"] = "Fear Ward",
-	["Frame Scale"] = "Frame Scale",
+	["Frame Scale:"] = "Frame Scale:",
 	["Frames have been locked"] = "Frames have been locked",
 	["Frames have been unlocked"] = "Frames have been unlocked",
 	["Ghost"] = "Ghost",
@@ -34,17 +40,34 @@ PerfectRaidLocals = {
 	["Greater Blessing of Salvation"] = "Greater Blessing of Salvation",
 	["Greater Blessing of Sanctuary"] = "Greater Blessing of Sanctuary",
 	["Greater Blessing of Wisdom"] = "Greater Blessing of Wisdom",
+	["Group 1"] = "Group 1",
+	["Group 2"] = "Group 2",
+	["Group 3"] = "Group 3",
+	["Group 4"] = "Group 4",
+	["Group 5"] = "Group 5",
+	["Group 6"] = "Group 6",
+	["Group 7"] = "Group 7",
+	["Group 8"] = "Group 8",
 	["Group Buff Name:"] = "Group Buff Name:",
+	["Group frames by:"] = "Group frames by:",
 	["Healing Touch"] = "Healing Touch",
 	["Healing Wave"] = "Healing Wave",
 	["Holy Light"] = "Holy Light",
+	["Hunter"] = "Hunter",
+	["If you choose any of the column options, all of them become required fields.  Please choose the number of columns, max units, column spacing and column anchor."] = "If you choose any of the column options, all of them become required fields.  Please choose the number of columns, max units, column spacing and column anchor.",
 	["Innervate"] = "Innervate",
 	["Lesser Heal"] = "Lesser Heal",
+	["Mage"] = "Mage",
+	["Make filters strict"] = "Make filters strict",
 	["Mark of the Wild"] = "Mark of the Wild",
+	["Max Units per Column:"] = "Max Units per Column:",
 	["Mortal Strike"] = "Mortal Strike",
+	["No filters selected"] = "No filters selected",
+	["Number of Columns:"] = "Number of Columns:",
 	["Offline"] = "Offline",
 	["Only show if this buff is missing"] = "Only show if this buff is missing",
 	["Out-of-Range Alpha"] = "Out-of-Range Alpha",
+	["Paladin"] = "Paladin",
 	["PerfectRaid Options"] = "PerfectRaid Options",
 	["Perform aggro checking"] = "Perform aggro checking",
 	["Perform range checking"] = "Perform range checking",
@@ -54,11 +77,15 @@ PerfectRaidLocals = {
 	["Prayer of Fortitude"] = "Prayer of Fortitude",
 	["Prayer of Shadow Protection"] = "Prayer of Shadow Protection",
 	["Prayer of Spirit"] = "Prayer of Spirit",
-	["Put a backdrop behind PerfectRaid"] = "Put a backdrop behind PerfectRaid",
+	["Priest"] = "Priest",
+	["Primary In-Range Alpha"] = "Primary In-Range Alpha",
+	["Primary Spell:"] = "Primary Spell:",
 	["Raid Frames"] = "Raid Frames",
 	["Regrowth"] = "Regrowth",
 	["Rejuvenation"] = "Rejuvenation",
 	["Renew"] = "Renew",
+	["Reverse HP bars"] = "Reverse HP bars",
+	["Rogue"] = "Rogue",
 	["STATUS_ARCANEINT"] = "Int",
 	["STATUS_BLESSINGKINGS"] = "BoK",
 	["STATUS_BLESSINGLIGHT"] = "BoL",
@@ -86,12 +113,22 @@ PerfectRaidLocals = {
 	["STATUS_THORNS"] = "Th",
 	["STATUS_WEAKENEDSOUL"] = "Ws",
 	["Save"] = "Save",
+	["Secondary In-Range Alpha"] = "Secondary In-Range Alpha",
+	["Secondary Spell:"] = "Secondary Spell:",
 	["Shadow Protection"] = "Shadow Protection",
+	["Shaman"] = "Shaman",
+	["Show HP deficit"] = "Show HP deficit",
 	["Show options GUI"] = "Show options GUI",
+	["Sort Frames By:"] = "Sort Frames By:",
 	["Soulstone Resurrection"] = "Soulstone Resurrection",
+	["Spacing between columns:"] = "Spacing between columns:",
 	["Thorns"] = "Thorns",
+	["Title:"] = "Title:",
 	["Update delay"] = "Update delay",
+	["Warlock"] = "Warlock",
+	["Warrior"] = "Warrior",
 	["Weakened Soul"] = "Weakened Soul",
+	["You must select at least one class or group to display."] = "You must select at least one class or group to display.",
 	["lock - Lock frames"] = "lock - Lock frames",
 	["unlock - Unlock frames"] = "unlock - Unlock frames",
 }
diff --git a/PerfectRaid.lua b/PerfectRaid.lua
index 73fa0fd..c33adef 100644
--- a/PerfectRaid.lua
+++ b/PerfectRaid.lua
@@ -54,12 +54,6 @@ function PerfectRaid:Initialize()
 		},
 	}

-	self.headerDefaults = {
-		Reverse = false,
-		ColorClass = true,
-		ColorSeverity = false,
-	}
-
 	if not ClickCastFrames then
 		ClickCastFrames = {}
 	end
@@ -81,36 +75,30 @@ function PerfectRaid:Enable()
 	self:RegisterEvent("UNIT_MAXFOCUS", "UNIT_MAXMANA")
 	self:RegisterEvent("CHAT_MSG_SYSTEM")

-	--TODO: Make this so we can actually instantiate frames as the users wants
-	for i=1,8 do
-		local name = "PRHeader"..i
-		self.db.profile.headers[name] = setmetatable({}, {__index=self.headerDefaults})
-		local header = self:CreateRaidFrame(name, "Group "..i, tostring(i), nil, PRHeader1)
+	self:UpdateRaidFrames()
+end
+
+function PerfectRaid:UpdateRaidFrames()
+	local list = self.db.profile.headers
+
+	for idx=1,40 do
+		local name = "PRHeader"..idx
+		local frame = getglobal(name)
+		if not frame then break end
+		if not list[idx] then
+			for i=1,frame:GetNumChildren() do
+				local button = frame:GetAttribute("child"..i)
+				button:SetAttribute("unit", nil)
+				button:Hide()
+			end
+		end
+		frame:Hide()
 	end

---[[
-	PRHeader1:ClearAllPoints()
-	self:RestorePosition("PRHeader1")
-	PRHeader2:SetPoint("TOP", PRHeader1, "BOTTOM", 0, -20)
-	PRHeader3:SetPoint("TOP", PRHeader2, "BOTTOM", 0, -20)
-	PRHeader4:SetPoint("TOP", PRHeader3, "BOTTOM", 0, -20)
-	PRHeader5:SetPoint("TOPLEFT", PRHeader1, "TOPRIGHT", 10, 0)
-	PRHeader6:SetPoint("TOP", PRHeader5, "BOTTOM", 0, -20)
-	PRHeader7:SetPoint("TOP", PRHeader6, "BOTTOM", 0, -20)
-	PRHeader8:SetPoint("TOP", PRHeader7, "BOTTOM", 0, -20)
---]]
-
-	self.db.profile.headers["PRHeaderColumn"] = setmetatable({}, {__index=self.headerDefaults})
-	local column = self:CreateRaidFrame("PRHeaderColumn", nil, "1,2,3,4,5,6,7,8", nil)
-	column:Hide()
-	column:SetAttribute("groupBy", "CLASS")
-	column:SetAttribute("groupingOrder", "WARRIOR,PRIEST,DRUID,SHAMAN,PALADIN,MAGE,ROGUE,WARLOCK,HUNTER")
-	column:SetAttribute("maxColumns", 2)
-	column:SetAttribute("unitsPerColumn", 20)
-	column:SetAttribute("columnSpacing", 10)
-	column:SetAttribute("columnAnchorPoint", "LEFT")
-	self:RestorePosition("PRHeaderColumn")
-	column:Show()
+	--name, title, filter, sortOrder, strict, group, order, columns, coloffset, colanchor)
+	for idx,entry in ipairs(list) do
+		self:CreateRaidFrame(idx)
+	end
 end

 function PerfectRaid:SavePosition(name)
@@ -169,28 +157,62 @@ local function OnDragStop(frame)
 	PerfectRaid.moving = nil
 end

-function PerfectRaid:CreateRaidFrame(name, title, filter, strict, dragparent, group, order, columns, coloffset, colanchor)
-	local frame = CreateFrame("Frame", name, UIParent, "SecureRaidGroupHeaderTemplate")
-	frame.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
-	frame.title:SetPoint("BOTTOM", frame, "TOP", 0, 3)
-	frame.title:SetText(title or "")
-	frame:SetAttribute("point", "TOP")
-	frame:SetAttribute("groupFilter", filter)
+function PerfectRaid:CreateRaidFrame(idx)
+	local options = self.db.profile.headers[idx]
+	local name = "PRHeader"..idx
+	local frame = getglobal(name)
+
+	if not frame then
+		frame = CreateFrame("Frame", name, UIParent, "SecureRaidGroupHeaderTemplate")
+		frame.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
+		frame.title:SetPoint("BOTTOM", frame, "TOP", 0, 3)
+
+		frame.backdrop = {
+			bgFile = "Interface\\Tooltips\\UI-Tooltip-Background";
+			tileSize =16;
+			tile = 1;
+			insets = {top = -5; bottom = -5; left = -5; right = -5};
+		}
+	end
+
+	frame.idx = idx
+	frame.title:SetText(options.title or "")
+
+	if options.hBackdrop then
+		frame:SetBackdrop(frame.backdrop)
+		frame:SetBackdropColor(0,0,0,1)
+	else
+		frame:SetBackdrop(nil)
+	end
+
+	local yoffset = options.alignbottom and 1 or -1
+
+	frame:SetAttribute("point", options.alignbottom and "BOTTOM" or "TOP")
+	frame:SetAttribute("groupFilter", options.filter or "")
 	frame:SetAttribute("template", "SecureUnitButtonTemplate")
 	frame:SetAttribute("templateType", "Button")
-	frame:SetAttribute("yOffset", -2)
-	frame:SetAttribute("sortMethod", "NAME")
-	frame:SetAttribute("strictFiltering", strict)
-	frame:SetAttribute("groupBy", group)
-	frame:SetAttribute("groupingOrder", order)
-	frame:SetAttribute("maxColumns", columns)
-	frame:SetAttribute("columnSpacing", coloffset)
-	frame:SetAttribute("columnAnchorPoint", colanchor)
+	frame:SetAttribute("yOffset", yoffset)
+	frame:SetAttribute("sortMethod", options.sortType)
+	frame:SetAttribute("strictFiltering", options.strict)
+	frame:SetAttribute("groupBy", options.groupBy)
+	local groupOrder
+	if options.groupBy == "CLASS" then
+		groupOrder="WARRIOR,PRIEST,DRUID,SHAMAN,PALADIN,MAGE,ROGUE,WARLOCK,HUNTER"
+	elseif options.groupBy == "GROUP" then
+		groupOrder="1,2,3,4,5,6,7,8"
+	end
+	frame:SetAttribute("groupingOrder", groupOrder)
+	frame:SetAttribute("maxColumns", options.numColumns)
+	frame:SetAttribute("unitsPerColumn", options.maxUnits)
+	frame:SetAttribute("columnSpacing", options.colSpacing)
+	frame:SetAttribute("columnAnchorPoint", options.colAnchor)
 	frame.dragparent = dragparent or frame
 	frame.initialConfigFunction = PerfectRaid.ConfigureButton
 	frame:SetMovable(true)
 	frame:SetClampedToScreen(true)
+	frame:SetScale(options.scale)
 	frame:Show()
+	self:RestorePosition(name)
 	return frame
 end

@@ -205,7 +227,7 @@ function PerfectRaid:CHAT_MSG_SYSTEM(event, msg)
 		for unit,tbl in pairs(frames) do
 			for frame in pairs(tbl) do
 				frame.unit = nil
-				frame.name = nil
+				frame.unitname = nil
 			end
 		end
 	end
@@ -262,16 +284,16 @@ function PerfectRaid:UNIT_HEALTH(event, unit)
 	else
 		if unavail[unit] then
 			for frame in pairs(frames[unit]) do
-				local options = self.db.profile.headers[frame.headerName]
-				if options.ColorClass then
+				local options = self.db.profile.headers[frame.header.idx]
+				if options.colorclass then
 					local c = frame.classcolor
 					frame.healthbar:SetStatusBarColor(c.r,c.g,c.b)
-				elseif options.ColorSeverity then
+				else
 					frame.healthbar:SetStatusBarColor(utils.GetHPSeverity(perc))
 				end

 				local value = health
-				if options.Reverse then
+				if options.reverse then
 					value = max - health
 				end

@@ -283,18 +305,25 @@ function PerfectRaid:UNIT_HEALTH(event, unit)
 		end

 		for frame in pairs(frames[unit]) do
-			local options = self.db.profile.headers[frame.headerName]
-			if options.ColorSeverity then
-				frame.healthbar:SetStatusBarColor(utils.GetHPSeverity(perc))
-			end
+				local options = self.db.profile.headers[frame.header.idx]
+				if options.colorclass then
+					local c = frame.classcolor
+					frame.healthbar:SetStatusBarColor(c.r,c.g,c.b)
+				else
+					frame.healthbar:SetStatusBarColor(utils.GetHPSeverity(perc))
+				end

 			local value = health
-			if options.Reverse then
+			if options.reverse then
 				value = max - health
 			end

 			frame.healthbar:SetValue(value)
-			frame.status:SetText(deficit)
+			if options.deficit then
+				frame.status:SetText(deficit)
+			else
+				frame.status:SetText(nil)
+			end
 		end
 	end
 end
@@ -331,53 +360,14 @@ end
 local function OnShow(frame)
 	local self = PerfectRaid
 	local unit = frame:GetAttribute("unit")
-	local name = UnitName(unit)
-	if not frame.name then
-		frame.name = name
+	local unitname = UnitName(unit)
+	if not frame.unitname then
+		frame.unitname = unitname
 	else
-		if frame.name == name then
+		if frame.unitname == unitname then
 			return
 		end
 	end
-
-	frame.name:SetText(self:GetColoredName(unit))
---	frame.name:SetText(UnitName(unit))
-
-	local idx = tonumber(string.match(unit, "^raid(%d+)$"))
-	if idx then
-		local group = select(3, GetRaidRosterInfo(idx))
-		frame.group:SetText(group)
-	end
-
-	local class = select(2, UnitClass(unit)) or "WARRIOR"
-	frame.class = class
-	frame.classcolor = RAID_CLASS_COLORS[class]
-	frame.manacolor = ManaBarColor[UnitPowerType(unit)]
-
-	local color = frame.classcolor
-	frame.healthbar:SetStatusBarColor(color.r, color.g, color.b)
-	frame.healthbar:SetMinMaxValues(0, UnitHealthMax(unit))
-	frame.healthbar:SetValue(UnitHealth(unit))
-	frame:SetBackdropBorderColor(color.r, color.g, color.b)
-
-    local color = frame.manacolor
-    frame.manabar:SetStatusBarColor(color.r, color.g, color.b)
-	frame.manabar:SetMinMaxValues(0, UnitManaMax(unit))
-	frame.manabar:SetValue(UnitMana(unit))
-
-	frame.status:SetText(nil)
-
-	frames[unit] = frames[unit] or {}
-	frames[unit][frame] = true
-
-	for name,module in self:IterateModules() do
-		if type(module.ShowButton) == "function" then
-			module:ShowButton(frame)
-		end
-	end
-
-	self:UNIT_HEALTH(nil, unit)
-	self:UNIT_MANA(nil, unit)
 end

 local function OnHide(frame)
@@ -388,6 +378,7 @@ end
 local function OnAttributeChanged(frame, name, value)
 	if name ~= "unit" then return end

+	local self = PerfectRaid
 	for unit,tbl in pairs(frames) do
 		if tbl[frame] and frame:GetAttribute("unit") ~= unit then
 			tbl[frame] = nil
@@ -402,6 +393,45 @@ local function OnAttributeChanged(frame, name, value)
 		end
 	end

+	if value then
+		-- Do Visual Configuration here
+		local unit = value
+		frame.name:SetText(self:GetColoredName(unit))
+		--	frame.name:SetText(UnitName(unit))
+
+		local class = select(2, UnitClass(unit)) or "WARRIOR"
+		frame.class = class
+		frame.classcolor = RAID_CLASS_COLORS[class]
+		frame.manacolor = ManaBarColor[UnitPowerType(unit)]
+
+		local color = frame.classcolor
+		frame.healthbar:SetStatusBarColor(color.r, color.g, color.b)
+		frame.healthbar:SetMinMaxValues(0, UnitHealthMax(unit))
+		frame.healthbar:SetValue(UnitHealth(unit))
+		--	frame:SetBackdropBorderColor(color.r, color.g, color.b)
+
+		local color = frame.manacolor
+		frame.manabar:SetStatusBarColor(color.r, color.g, color.b)
+		frame.manabar:SetMinMaxValues(0, UnitManaMax(unit))
+		frame.manabar:SetValue(UnitMana(unit))
+
+		frame.status:SetText(nil)
+
+		frames[unit] = frames[unit] or {}
+		frames[unit][frame] = true
+
+		for name,module in self:IterateModules() do
+			if type(module.ShowButton) == "function" then
+				module:ShowButton(frame)
+			end
+		end
+
+		self:UNIT_HEALTH(nil, unit)
+		self:UNIT_MANA(nil, unit)
+	end
+
+	-- Hide any empty parents
+
 	local parent = frame:GetParent()
 	if value == nil then
 		local num = parent:GetNumChildren()
@@ -445,70 +475,24 @@ function PerfectRaid.ConfigureButton(button)
 	button.header = parent
 	button.headerName = parent:GetName()

+	local options = self.db.profile.headers[parent.idx]
+
 	local leftbox = CreateFrame("Frame", nil, button)
-	leftbox:SetPoint("TOPLEFT", 0, 0)
-	leftbox:SetPoint("BOTTOMRIGHT", button, "BOTTOMLEFT", 70, 0)
 	button.leftbox = leftbox

 	local rightbox = CreateFrame("Frame", nil, button)
-	rightbox:SetPoint("TOPRIGHT", 0, 0)
-	rightbox:SetPoint("BOTTOMLEFT", button, "BOTTOMRIGHT", -70, 0)
 	button.rightbox = rightbox

 	local bar = CreateFrame("StatusBar", nil, button)
-	bar:SetPoint("TOPLEFT", leftbox, "TOPRIGHT", 0, -1)
-	bar:SetPoint("BOTTOMRIGHT", rightbox, "BOTTOMLEFT", 0, 1)
-	bar:SetStatusBarTexture("Interface\\AddOns\\PerfectRaid\\images\\smooth")
 	button.healthbar = bar

 	local bar = CreateFrame("StatusBar", nil, button.healthbar)
-	bar:SetPoint("BOTTOMLEFT", 0, 0)
-	bar:SetPoint("BOTTOMRIGHT", 0, 0)
-	bar:SetHeight(2)
-	bar:SetStatusBarTexture("Interface\\AddOns\\PerfectRaid\\images\\smooth")
 	button.manabar = bar
-	bar:Hide()
-
-	local group = leftbox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-	group:SetPoint("LEFT", 0, 0)
-	group:SetHeight(14)
-	group:SetJustifyH("LEFT")
-	button.group = group
-	button.group:SetWidth(0.1)
-	button.group:Hide()

 	local font = button.healthbar:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-	font:SetPoint("RIGHT", leftbox, "RIGHT", -2, 0)
-	font:SetPoint("LEFT", group, "RIGHT", 0, 0)
-	font:SetJustifyH("RIGHT")
 	button.name = font

-	local raidicon = button:CreateTexture(nil, "ARTWORK")
-	raidicon:SetTexture("Interface\\TargetingFrame\\UI-RaidTargetingIcons")
-	raidicon:SetHeight(16)
-	raidicon:SetWidth(0.1)
-	raidicon:SetPoint("RIGHT", leftbox, "LEFT", 0, 0)
-	button.raidicon = raidicon
-	raidicon:Hide()
-
-	local leadericon = button:CreateTexture(nil, "ARTWORK")
-	leadericon:SetTexture("Interface\\GroupFrame\\UI-Group-LeaderIcon")
-	leadericon:SetHeight(16)
-	leadericon:SetWidth(0.1)
-	leadericon:SetPoint("RIGHT", raidicon, "LEFT", 0, 0)
-	button.leadericon = leadericon
-	leadericon:Hide()
-
-	local looticon = button:CreateTexture(nil, "ARTWORK")
-	looticon:SetTexture("Interface\\GroupFrame\\UI-Group-MasterLooter")
-	looticon:SetHeight(16)
-	looticon:SetWidth(0.1)
-	looticon:SetPoint("RIGHT", leadericon, "LEFT", 0, 0)
-	button.looticon = looticon
-	looticon:Hide()
-
 	local font = button.healthbar:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-	font:SetPoint("RIGHT", -2, 0)
 	button.status = font

 	button:SetClampedToScreen(true)
@@ -520,12 +504,60 @@ function PerfectRaid.ConfigureButton(button)
 		end
 	end

+	self:UpdateButtonLayout(button)
+
 	button:SetScript("OnShow", OnShow)
 	button:SetScript("OnDragStart", OnDragStart)
 	button:SetScript("OnDragStop", OnDragStop)
 	button:SetScript("OnAttributeChanged", OnAttributeChanged)
 end

+function PerfectRaid:UpdateButtonLayout(button)
+	local parent = button:GetParent()
+	local options = self.db.profile.headers[parent.idx]
+
+	button.leftbox:ClearAllPoints()
+	button.leftbox:SetPoint("TOPLEFT", 0, 0)
+	button.leftbox:SetPoint("BOTTOMRIGHT", button, "BOTTOMLEFT", 70, 0)
+
+	button.rightbox:ClearAllPoints()
+	button.rightbox:SetPoint("TOPRIGHT", 0, 0)
+	button.rightbox:SetPoint("BOTTOMLEFT", button, "BOTTOMRIGHT", -70, 0)
+
+	button.healthbar:ClearAllPoints()
+	button.healthbar:SetPoint("TOPLEFT", button.leftbox, "TOPRIGHT", 0, -1)
+	button.healthbar:SetPoint("BOTTOMRIGHT", button.rightbox, "BOTTOMLEFT", 0, 1)
+	button.healthbar:SetStatusBarTexture("Interface\\AddOns\\PerfectRaid\\images\\smooth")
+
+	button.manabar:ClearAllPoints()
+	button.manabar:SetPoint("BOTTOMLEFT", 0, 0)
+	button.manabar:SetPoint("BOTTOMRIGHT", 0, 0)
+	button.manabar:SetHeight(2)
+	button.manabar:SetStatusBarTexture("Interface\\AddOns\\PerfectRaid\\images\\smooth")
+	button.manabar:Hide()
+
+	button.status:ClearAllPoints()
+	button.status:SetPoint("RIGHT", -2, 0)
+
+	if options.alignright then
+		button.name:ClearAllPoints()
+		button.name:SetPoint("RIGHT", button.rightbox, "RIGHT", 0, 0)
+		button.name:SetPoint("LEFT", button.rightbox, "LEFT", 2, 0)
+		button.name:SetJustifyH("LEFT")
+	else
+		button.name:ClearAllPoints()
+		button.name:SetPoint("RIGHT", button.leftbox, "RIGHT", -2, 0)
+		button.name:SetPoint("LEFT", button.leftbox, "LEFT", 0, 0)
+		button.name:SetJustifyH("RIGHT")
+	end
+
+	for name,module in self:IterateModules() do
+		if type(module.UpdateButtonLayout) == "function" then
+			module:UpdateButtonLayout(button, options)
+		end
+	end
+end
+
 function PerfectRaid:RAID_ROSTER_UPDATE()
 	if self.moving then
 		self.moving:StopMovingOrSizing()
diff --git a/PerfectRaid_Aggro.lua b/PerfectRaid_Aggro.lua
index 28c1d8a..ca5b3bc 100644
--- a/PerfectRaid_Aggro.lua
+++ b/PerfectRaid_Aggro.lua
@@ -45,6 +45,7 @@ function Aggro:Initialize()
 	frames = PerfectRaid.frames
 	aggro = PerfectRaid.aggro
 	self:RegisterMessage("DONGLE_PROFILE_CHANGED")
+	self:RegisterMessage("RAID_ROSTER_UPDATE")
 end

 function Aggro:Enable()
@@ -62,7 +63,7 @@ function Aggro:Disable()
 	end
 	for k,v in pairs(aggro) do aggro[k] = 0 end
 end
-
+
 function Aggro:DONGLE_PROFILE_CHANGED(event, addon, svname, name)
 	if svname == "PerfectRaidDB" then
 		rate = PerfectRaid.db.profile.AggroRate
@@ -81,6 +82,8 @@ function Aggro.OnUpdate()
 	if elapsed >= rate then
 		for unit,tbl in pairs(frames) do
 			-- Aggro check
+
+
 			for unit,tbl in pairs(frames) do
 				if not targets[unit] then
 					targets[unit] = unit.."target"
diff --git a/PerfectRaid_Buffs.lua b/PerfectRaid_Buffs.lua
index 8546e96..8508d2d 100644
--- a/PerfectRaid_Buffs.lua
+++ b/PerfectRaid_Buffs.lua
@@ -55,8 +55,9 @@ function Buffs:Enable()
 		self:UNIT_AURA(nil, unit)
 	end
 end
-
+
 function Buffs:DONGLE_PROFILE_CREATED(event, db, addon, svname, profileKey)
+	if db ~= PerfectRaid.db then return end
 	self:PrintF(L["Adding defaults to new profile \"%s\""], profileKey)
 	local buffs = db.profile.buffs
 	for k,v in ipairs(self.defaults) do
@@ -89,13 +90,17 @@ end

 function Buffs:ConfigureButton(button)
 	local font = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-	font:SetPoint("LEFT", button.rightbox, "LEFT", 3, 0)
 	button.aura = font
+end

-	local bg = button:CreateTexture(nil, "BACKGROUND")
-	bg:SetPoint("TOPLEFT", 0, 0)
-	bg:SetPoint("BOTTOMRIGHT", 0, 0)
-	button.bg = bg
+function Buffs:UpdateButtonLayout(button, options)
+	button.aura:ClearAllPoints()
+
+	if options.alignright then
+		button.aura:SetPoint("RIGHT", button.leftbox, "RIGHT", -2, 0)
+	else
+		button.aura:SetPoint("LEFT", button.rightbox, "LEFT", 2, 0)
+	end
 end

 function Buffs:ShowButton(button)
diff --git a/PerfectRaid_Frames.lua b/PerfectRaid_Frames.lua
index cf88092..bca9c41 100644
--- a/PerfectRaid_Frames.lua
+++ b/PerfectRaid_Frames.lua
@@ -38,7 +38,8 @@ function Frames:Initialize()
 	frames = PerfectRaid.frames
 end

-function Frames:Enable()
+function Frames:Enable()
+	self:RegisterMessage("PERFECTRAID_TAB_CHANGED")
 end

 function Frames:ConfigureButton(button)
@@ -47,6 +48,13 @@ end
 function Frames:ShowButton(button)
 end

+function Frames:PERFECTRAID_TAB_CHANGED(old, new)
+	if PROptions_Frames_Edit and PROptions_Frames_Edit:IsVisible() then
+		self:CancelEntry()
+		PROptions_Frames:Hide()
+	end
+end
+
 function Frames:CreateOptions(opt)
 	self.options = opt
 	local options = CreateFrame("Frame", "PROptions_Frames", PROptions)
@@ -66,7 +74,12 @@ function Frames:CreateOptions(opt)
 			button = scrollframe.entries[i]
 			if idx <= #list then
 				local entry = list[idx]
-				local display = entry.name
+				local display
+				if entry.title then
+					display = string.format("%s: %s", entry.title, entry.filter or L["No filters selected"])
+				else
+					display = string.format("%s", entry.filter or L["No filters selected"])
+				end
 				button.line1:SetText(display)
 				button:Show()
 				if scrollframe.selected == idx then
@@ -78,6 +91,7 @@ function Frames:CreateOptions(opt)
 				button:Hide()
 			end
 		end
+		self:EnableButtons()
 	end

 	scrollframe.update = update
@@ -99,6 +113,8 @@ function Frames:CreateOptions(opt)
 		frame:SetScript("PostClick", postClick)
 	end

+	self.scrollframe = scrollframe
+
 	local delete = CreateFrame("Button", "PRFrames_Delete", options, "PRButtonTemplate")
 	delete:SetText(L["Delete"])
 	delete:SetPoint("BOTTOMRIGHT", 0, 5)
@@ -117,359 +133,412 @@ function Frames:CreateOptions(opt)
 	add:SetScript("OnClick", function() self:AddEntry() end)
 	add:Show()

-	--self:CreateEditFrame(options)
+	self:CreateEditFrame(options)
 end

-
 function Frames:CreateEditFrame(parent)
-	local frame = CreateFrame("Frame", "PROptions_Buffs_Edit", PROptions)
-	local name = "PROptions_Buffs_Edit"
-	frame:SetAllPoints(PROptions_Buffs)
+	local frame = CreateFrame("Frame", "PROptions_Frames_Edit", PROptions)
+	local name = "PROptions_Frames_Edit"
+	frame:SetAllPoints(PROptions_Frames)
+	frame:SetFrameLevel(frame:GetFrameLevel() + 1)
 	frame:Hide()

-	local buffname = CreateFrame("EditBox", name.."BuffName", frame, "InputBoxTemplate")
-	local font = buffname:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-	font:SetText(L["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(L["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(L["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);
+	local title = CreateFrame("EditBox", name.."Title", frame, "InputBoxTemplate")
+	local font = title:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Title:"])
+	font:SetPoint("BOTTOMLEFT", title, "TOPLEFT", 0, 3)
+	title:SetPoint("TOPLEFT", 0, -10)
+	title:SetAutoFocus(nil)
+	title:SetWidth(180)
+	title:SetHeight(20)
+	title:Show()
+	frame.title = title
+
+	local groupByDropDown = CreateFrame("Frame", "PRFrames_GroupByDropDown", frame, "UIDropDownMenuTemplate")
+	local clickFunc = function() UIDropDownMenu_SetSelectedValue(groupByDropDown, this.value) end
+	groupByDropDown.Initialize = function()
+		UIDropDownMenu_AddButton{text = "None", value = -1, func = clickFunc}
+		UIDropDownMenu_AddButton{text = "Group", value = "GROUP", func = clickFunc}
+		UIDropDownMenu_AddButton{text = "Class", value = "CLASS", func = clickFunc}
 	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);
+	groupByDropDown:SetScript("OnShow", function(f)
+		UIDropDownMenu_Initialize(PRFrames_GroupByDropDown, groupByDropDown.Initialize)
+	end)
+	groupByDropDown:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -125, -10)
+	local font = groupByDropDown:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Group frames by:"])
+	font:SetPoint("BOTTOMLEFT", groupByDropDown, "TOPLEFT", 0, 0)
+
+
+	local sortDropDown = CreateFrame("Frame", "PRFrames_SortDropDown", frame, "UIDropDownMenuTemplate")
+	local clickFunc = function() UIDropDownMenu_SetSelectedValue(sortDropDown, this.value) end
+	sortDropDown.Initialize = function()
+		UIDropDownMenu_AddButton{text = "None", value = -1, func = clickFunc}
+		UIDropDownMenu_AddButton{text = "Name", value = "NAME", func = clickFunc}
+		UIDropDownMenu_AddButton{text = "Index", value = "INDEX", func = clickFunc}
 	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)
-
-	local function makeCheck(value)
-		local button = CreateFrame("CheckButton", "PRBuffCond_"..value, PROptions_Buffs_Edit, "PRCheckTemplate")
-		button.Label:SetText(value)
+	sortDropDown:SetScript("OnShow", function(f)
+		UIDropDownMenu_Initialize(sortDropDown, sortDropDown.Initialize)
+	end)
+	sortDropDown:SetPoint("RIGHT", groupByDropDown, "LEFT", -150, 0)
+	local font = sortDropDown:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Sort Frames By:"])
+	font:SetPoint("BOTTOMLEFT", sortDropDown, "TOPLEFT", 0, 0)
+
+	self.filters = {}
+	local function makeCheck(label, value)
+		local button = CreateFrame("CheckButton", "PRFrameFilter_"..value, PROptions_Frames_Edit, "PRCheckTemplate")
+		button.Label:SetText(label)
 		button.value = value
+		table.insert(self.filters, button)
+		button:Show()
 		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,6 do
-		checks[i] = makeCheck(self.conditions[i])
-		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
-		checks[i]:Show()
-	end
-
-	checks[7] = makeCheck(self.conditions[7])
-	checks[7]:SetPoint("TOPLEFT", checks[1], "BOTTOMLEFT", 0, -10)
-	checks[7]:Show()
-
-	for i=8,12 do
-		checks[i] = makeCheck(self.conditions[i])
-		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
-		checks[i]:Show()
-	end
-
-	checks[13] = makeCheck(self.conditions[13])
-	checks[13]:SetPoint("TOPLEFT", checks[7], "BOTTOMLEFT", 0, -10)
-	checks[13]:Show()
-
-	for i=14,18 do
-		checks[i] = makeCheck(self.conditions[i])
-		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
-		checks[i]:Show()
+	makeCheck(L["Warrior"], "WARRIOR")
+	makeCheck(L["Priest"], "PRIEST")
+	makeCheck(L["Druid"], "DRUID")
+	makeCheck(L["Shaman"], "SHAMAN")
+	makeCheck(L["Paladin"], "PALADIN")
+	makeCheck(L["Mage"], "MAGE")
+	makeCheck(L["Rogue"], "ROGUE")
+	makeCheck(L["Warlock"], "WARLOCK")
+	makeCheck(L["Hunter"], "HUNTER")
+	makeCheck(L["Group 1"], "1")
+	makeCheck(L["Group 2"], "2")
+	makeCheck(L["Group 3"], "3")
+	makeCheck(L["Group 4"], "4")
+	makeCheck(L["Group 5"], "5")
+	makeCheck(L["Group 6"], "6")
+	makeCheck(L["Group 7"], "7")
+	makeCheck(L["Group 8"], "8")
+
+	for idx,button in ipairs(self.filters) do
+		if idx == 1 then
+			button:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -15)
+		elseif math.fmod(idx - 1, 6) == 0 then
+			button:SetPoint("TOPLEFT", self.filters[idx-6], "BOTTOMLEFT", 0, -15)
+		else
+			button:SetPoint("LEFT", self.filters[idx-1], "RIGHT", 60, 0)
+		end
 	end

-	checks[19] = makeCheck(self.conditions[19])
-	checks[19]:SetPoint("TOPLEFT", checks[13], "BOTTOMLEFT", 0, -10)
-	checks[19]:Show()
-
-	for i=20,21 do
-		checks[i] = makeCheck(self.conditions[i])
-		checks[i]:SetPoint("TOPLEFT", checks[i-1], "TOPRIGHT", 60, 0)
-		checks[i]:Show()
+	local numCols = CreateFrame("EditBox", name.."NumCols", frame, "InputBoxTemplate")
+	local font = numCols:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Number of Columns:"])
+	font:SetPoint("RIGHT", numCols, "LEFT", -10, 0)
+	numCols:SetPoint("BOTTOMRIGHT", -5, 140)
+	numCols:SetAutoFocus(nil)
+	numCols:SetWidth(50)
+	numCols:SetHeight(20)
+	numCols:Show()
+	frame.numCols = numCols
+
+	local maxUnits = CreateFrame("EditBox", name.."MaxUnits", frame, "InputBoxTemplate")
+	local font = maxUnits:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Max Units per Column:"])
+	font:SetPoint("RIGHT", maxUnits, "LEFT", -10, 0)
+	maxUnits:SetPoint("TOPLEFT", numCols, "BOTTOMLEFT", 0, -5)
+	maxUnits:SetAutoFocus(nil)
+	maxUnits:SetWidth(50)
+	maxUnits:SetHeight(20)
+	maxUnits:Show()
+	frame.maxUnits = maxUnits
+
+	local colSpacing = CreateFrame("EditBox", name.."ColSpacing", frame, "InputBoxTemplate")
+	local font = colSpacing:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Spacing between columns:"])
+	font:SetPoint("RIGHT", colSpacing, "LEFT", -10, 0)
+	colSpacing:SetPoint("TOPLEFT", maxUnits, "BOTTOMLEFT", 0, -5)
+	colSpacing:SetAutoFocus(nil)
+	colSpacing:SetWidth(50)
+	colSpacing:SetHeight(20)
+	colSpacing:Show()
+	frame.colSpacing = colSpacing
+
+	local colAnchorDropDown = CreateFrame("Frame", "PRFrames_ColAnchorDropDown", frame, "UIDropDownMenuTemplate")
+	local clickFunc = function() UIDropDownMenu_SetSelectedValue(colAnchorDropDown, this.value) end
+	local points = {"TOP", "TOPLEFT", "TOPRIGHT", "LEFT", "RIGHT", "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT"}
+	colAnchorDropDown.Initialize = function()
+		UIDropDownMenu_AddButton{text = "None", value = -1, func = clickFunc}
+		for k,v in pairs(points) do
+			UIDropDownMenu_AddButton{text = v, value = v, func = clickFunc}
+		end
 	end
-
-	local cancel = CreateFrame("Button", "PRBuffs_EditCancel", PROptions_Buffs_Edit, "PRButtonTemplate")
+	colAnchorDropDown:SetScript("OnShow", function(f)
+		UIDropDownMenu_Initialize(colAnchorDropDown, colAnchorDropDown.Initialize)
+	end)
+	colAnchorDropDown:SetPoint("TOPRIGHT", colSpacing, "BOTTOMRIGHT", -100, -25)
+	local font = sortDropDown:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Column Anchor Point:"])
+	font:SetPoint("RIGHT", colSpacing, "BOTTOMLEFT", -10, -15)
+
+	local hbackdrop = CreateFrame("CheckButton", "PRFrame_HeaderBackdrop", PROptions_Frames_Edit, "PRCheckTemplate")
+	hbackdrop.Label:SetText(L["Display a header backdrop"])
+	hbackdrop:SetPoint("BOTTOMLEFT", 0, 140)
+	hbackdrop:Show()
+
+	local strict = CreateFrame("CheckButton", "PRFrame_Strict", PROptions_Frames_Edit, "PRCheckTemplate")
+	strict.Label:SetText(L["Make filters strict"])
+	strict:SetPoint("TOPLEFT", hbackdrop, "BOTTOMLEFT", 0, 0)
+	strict:Show()
+
+	local classColor = CreateFrame("CheckButton", "PRFrame_ColorClass", PROptions_Frames_Edit, "PRCheckTemplate")
+	classColor.Label:SetText(L["Color bars by class"])
+	classColor:SetPoint("TOPLEFT", strict, "BOTTOMLEFT", 0, 0)
+	classColor:Show()
+
+	local reverseBar = CreateFrame("CheckButton", "PRFrame_ReverseBar", PROptions_Frames_Edit, "PRCheckTemplate")
+	reverseBar.Label:SetText(L["Reverse HP bars"])
+	reverseBar:SetPoint("TOPLEFT", hbackdrop, "TOPRIGHT", 160, 0)
+	reverseBar:Show()
+
+	local showDeficit = CreateFrame("CheckButton", "PRFrame_Deficit", PROptions_Frames_Edit, "PRCheckTemplate")
+	showDeficit.Label:SetText(L["Show HP deficit"])
+	showDeficit:SetPoint("TOPLEFT", reverseBar, "BOTTOMLEFT", 0, 0)
+	showDeficit:Show()
+
+	local alignRight = CreateFrame("CheckButton", "PRFrame_AlignRight", PROptions_Frames_Edit, "PRCheckTemplate")
+	alignRight.Label:SetText(L["Align frames to RIGHT"])
+	alignRight:SetPoint("TOPLEFT", showDeficit, "BOTTOMLEFT", 0, 0)
+	alignRight:Show()
+
+	local alignBottom = CreateFrame("CheckButton", "PRFrame_AlignBottom", PROptions_Frames_Edit, "PRCheckTemplate")
+	alignBottom.Label:SetText(L["Align frames to BOTTOM"])
+	alignBottom:SetPoint("TOPLEFT", alignRight, "BOTTOMLEFT", 0, 0)
+	alignBottom:Show()
+
+--[[
+	local hborderswatch = self:CreateSwatch("PRFrame_HBorder_Color", PROptions_Frames_Edit)
+	local fborderswatch = self:CreateSwatch("PRFrame_FBorder_Color", PROptions_Frames_Edit)
+	local fbackdropswatch = self:CreateSwatch("PRFrame_FBackdrop_Color", PROptions_Frames_Edit)
+	local hbackdropswatch = self:CreateSwatch("PRFrame_HBackdrop_Color", PROptions_Frames_Edit)
+	hborderswatch:SetPoint("LEFT", hborder, "RIGHT", 175, 0)
+	fborderswatch:SetPoint("LEFT", fborder, "RIGHT", 175, 0)
+	fbackdropswatch:SetPoint("LEFT", fbackdrop, "RIGHT", 175, 0)
+	hbackdropswatch:SetPoint("LEFT", hbackdrop, "RIGHT", 175, 0)
+--]]
+
+	local slider = CreateFrame("Slider", "PRFrame_Scale", PROptions_Frames_Edit, "PRSliderTemplate")
+	slider.Text:SetText(L["Frame Scale:"])
+	slider.High:SetText("2.0")
+	slider.Low:SetText("0.1")
+	slider:SetMinMaxValues(0.1,2.0)
+	slider:SetValueStep(0.05)
+	slider:SetPoint("BOTTOMLEFT", 10, 15)
+	slider:SetValue(1.0)
+	slider:Show()
+
+	local cancel = CreateFrame("Button", "PRFrames_Cancel", PROptions_Frames_Edit, "PRButtonTemplate")
 	cancel:SetText(L["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")
+	local save = CreateFrame("Button", "PRFrames_Save", PROptions_Frames_Edit, "PRButtonTemplate")
 	save:SetText(L["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(L["Only show if this buff is missing"])
-	missing:SetPoint("BOTTOMLEFT", 0, 10)
-	missing:Show()
-	frame.missing = missing
-
-	local disabled = CreateFrame("CheckButton", "PRBuffs_Disabled", PROptions_Buffs_Edit, "PRCheckTemplate")
-	disabled.Label:SetText(L["Do not check this buff (Disable)"])
-	disabled:SetPoint("BOTTOMLEFT", missing, "TOPLEFT", 0, 10)
-	disabled:Show()
-	frame.disabled = disabled
+	local function onTabPressed(self)
+		if IsShiftKeyDown() then
+			self.prev:SetFocus()
+		else
+			self.next:SetFocus()
+		end
+	end
+	PROptions_Frames_EditTitle.next = PROptions_Frames_EditNumCols
+	PROptions_Frames_EditTitle.prev = PROptions_Frames_EditColSpacing
+	PROptions_Frames_EditTitle:SetScript("OnTabPressed", onTabPressed)
+
+	PROptions_Frames_EditNumCols.next = PROptions_Frames_EditMaxUnits
+	PROptions_Frames_EditNumCols.prev = PROptions_Frames_EditTitle
+	PROptions_Frames_EditNumCols:SetScript("OnTabPressed", onTabPressed)
+
+	PROptions_Frames_EditMaxUnits.next = PROptions_Frames_EditColSpacing
+	PROptions_Frames_EditMaxUnits.prev = PROptions_Frames_EditNumCols
+	PROptions_Frames_EditMaxUnits:SetScript("OnTabPressed", onTabPressed)
+
+	PROptions_Frames_EditColSpacing.next = PROptions_Frames_EditTitle
+	PROptions_Frames_EditColSpacing.prev = PROptions_Frames_EditMaxUnits
+	PROptions_Frames_EditColSpacing:SetScript("OnTabPressed", onTabPressed)
 end

 function Frames:EditEntry()
-	local scrollframe = PROptions_BuffsScrollFrame
-	local offset = FauxScrollFrame_GetOffset(scrollframe)
-	local selected = PROptions_Buffs.selected
-	local entry = PerfectRaid.db.profile.buffs[selected]
-
-	local options = PROptions_Buffs_Edit
-	options.editEntry = entry
-
-	self.options:FadeOut(PROptions_Buffs)
-	self.options:FadeIn(options)
-	options.buffname:SetText(entry.buffname)
-	options.groupname:SetText(entry.groupname or "")
-	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(true)
-		else
-			button:SetChecked(false)
+	local idx = PROptions_Frames.selected
+	local list = PerfectRaid.db.profile.headers
+	local entry = list[idx]
+	self.editEntry = entry
+
+	self.options:FadeIn(PROptions_Frames_Edit)
+	self.options:FadeOut(PROptions_Frames)
+	PROptions_Frames_EditTitle:SetFocus()
+
+	PROptions_Frames_EditTitle:SetText(entry.title or "")
+	UIDropDownMenu_SetSelectedValue(PRFrames_GroupByDropDown, entry.groupBy)
+	UIDropDownMenu_SetSelectedValue(PRFrames_SortDropDown, entry.sortType)
+	UIDropDownMenu_SetText(entry.groupBy or "", PRFrames_GroupByDropDown)
+	UIDropDownMenu_SetText(entry.sortType or "", PRFrames_SortDropDown)
+	local filterTbl = {strsplit(",", entry.filter or "")}
+	for k,v in pairs(filterTbl) do filterTbl[v] = true end
+
+	for k,v in pairs(self.filters) do
+		if filterTbl[v.value] then
+			v:SetChecked(true)
 		end
 	end

-	options.missing:SetChecked(entry.missing)
-	options.disabled:SetChecked(entry.disabled)
+	PROptions_Frames_EditNumCols:SetText(entry.numColumns or "")
+	PROptions_Frames_EditMaxUnits:SetText(entry.maxUnits or "")
+	PROptions_Frames_EditColSpacing:SetText(entry.colSpacing or "")
+	UIDropDownMenu_SetSelectedValue(PRFrames_ColAnchorDropDown, entry.colAnchor)
+	PRFrame_HeaderBackdrop:SetChecked(entry.hBackdrop)
+	PRFrame_Scale:SetValue(entry.scale)
+	PRFrame_Strict:SetChecked(entry.strict)
+
+	PRFrame_ColorClass:SetChecked(entry.colorclass)
+	PRFrame_ReverseBar:SetChecked(entry.reverse)
+	PRFrame_Deficit:SetChecked(entry.deficit)
+	PRFrame_AlignRight:SetChecked(entry.alignright)
+	PRFrame_AlignBottom:SetChecked(entry.alignbottom)
 end

 function Frames:AddEntry()
-	local options = PROptions_Buffs_Edit
-	self.options:FadeOut(PROptions_Buffs)
-	self.options:FadeIn(options)
-	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.disabled:SetChecked(false)
-	options.buffname:SetFocus()
+	self.options:FadeIn(PROptions_Frames_Edit)
+	self.options:FadeOut(PROptions_Frames)
+	PROptions_Frames_EditTitle:SetFocus()
 end

 function Frames: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()
+	local idx = PROptions_Frames.selected
+	local list = PerfectRaid.db.profile.headers
+	table.remove(list, idx)
+	PROptions_Frames.selected = nil
+	self.editEntry = nil
+	self.scrollframe.update()
+	PerfectRaid:UpdateRaidFrames()
 end

-function Frames:UpdateBuffTable()
-	for k,v in pairs(buffs) do buffs[k] = nil end
-
-	for idx,entry in ipairs(PerfectRaid.db.profile.buffs) do
-		if not entry.disabled then
-			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
+function Frames:EnableButtons()
+	if PROptions_Frames.selected then
+		PRFrames_Edit:Enable()
+		PRFrames_Delete:Enable()
+	else
+		PRFrames_Edit:Disable()
+		PRFrames_Delete:Disable()
 	end
-
-	for unit in pairs(frames) do
-		self:UNIT_AURA(nil, unit)
-	end
 end

 function Frames:SaveEntry()
-	local frame = PROptions_Buffs_Edit
-
-	local buffname = frame.buffname:GetText()
-	local groupname = frame.groupname:GetText()
-	local disptext = frame.disptext:GetText()
-
+	local title = PROptions_Frames_EditTitle:GetText()
+	local groupBy = UIDropDownMenu_GetSelectedValue(PRFrames_GroupByDropDown)
+	local sortType = UIDropDownMenu_GetSelectedValue(PRFrames_SortDropDown)
+
+	local filterTbl = {}
+	for k,v in pairs(self.filters) do
+		if v:GetChecked() then
+			table.insert(filterTbl, v.value)
+		end
+	end
+	local filter = strjoin(",", unpack(filterTbl))
+	local numColumns = tonumber(PROptions_Frames_EditNumCols:GetText())
+	local maxUnits = tonumber(PROptions_Frames_EditMaxUnits:GetText())
+	local colSpacing = tonumber(PROptions_Frames_EditColSpacing:GetText())
+	local colAnchor = UIDropDownMenu_GetSelectedValue(PRFrames_ColAnchorDropDown)
+	local hBackdrop = PRFrame_HeaderBackdrop:GetChecked()
+	local scale = PRFrame_Scale:GetValue()
+	local strict = PRFrame_Strict:GetChecked()
+	local colorclass = PRFrame_ColorClass:GetChecked()
+	local reverse = PRFrame_ReverseBar:GetChecked()
+	local deficit = PRFrame_Deficit:GetChecked()
+	local alignright = PRFrame_AlignRight:GetChecked()
+	local alignbottom = PRFrame_AlignBottom:GetChecked()
+
+	if title == "" then title = nil end
+	if filter == "" then filter = nil end
+	if groupBy == -1 then groupBy = nil end
+	if sortType == -1 then sortType = nil end
+	if colAnchor == -1 then colAnchor = nil end
+
+	scale = tonumber(string.format("%0.2f", scale))
+
+	-- Validation code here
 	local err
-	if buffname == "" then
-		err = "You must supply a buff name"
-	elseif disptext == "" then
-		err = "You must supply a display text"
+	if not (numColumns and maxUnits and colSpacing and colAnchor) and
+		(numColumns or maxUnits or colSpacing or colAnchor) then
+		err = L["If you choose any of the column options, all of them become required fields.  Please choose the number of columns, max units, column spacing and column anchor."]
+	elseif not filter then
+		err = L["You must select at least one class or group to display."]
 	end

-	if err then
-		StaticPopupDialogs["PR_BUFF_SAVE_ERROR"].text = err
-		StaticPopup_Show("PR_BUFF_SAVE_ERROR")
-		return
+	if err then
+		StaticPopupDialogs["PR_FRAME_SAVE_ERROR"].text = err
+		StaticPopup_Show("PR_FRAME_SAVE_ERROR")
 	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
+	local entry = self.editEntry or {}
+	entry.title = title
+	entry.groupBy = groupBy
+	entry.sortType = sortType
+
+	entry.filter = filter
+	entry.numColumns = numColumns
+	entry.maxUnits = maxUnits
+	entry.colSpacing = colSpacing
+	entry.colAnchor = colAnchor
+	entry.hBackdrop = hBackdrop
+	entry.scale = scale
+	entry.strict = strict
+	entry.colorclass = colorclass
+	entry.reverse = reverse
+	entry.deficit = deficit
+	entry.alignright = alignright
+	entry.alignbottom = alignbottom
+
+	if not self.editEntry then
+		table.insert(PerfectRaid.db.profile.headers, entry)
 	end
-
-	local conds = strjoin(",", unpack(work))
-	entry.conds = conds
-	entry.missing = frame.missing:GetChecked()
-	entry.disabled = frame.disabled: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()
+	self.scrollframe.update()
+	PerfectRaid:UpdateRaidFrames()
+
+	-- Update button layouts
+	local idx = PROptions_Frames.selected
+	if idx then
+		local header = getglobal("PRHeader"..idx)
+		for i=1,header:GetNumChildren() do
+			local button = header:GetAttribute("child"..i)
+			PerfectRaid:UpdateButtonLayout(button)
+		end
+		for unit in pairs(frames) do
+			PerfectRaid:UNIT_HEALTH("UNIT_HEALTH", unit)
+		end
+	end
 end

 function Frames:CancelEntry()
-	local options = PROptions_Buffs_Edit
-	self.options:FadeOut(options)
-	options.editEntry = nil
-	self.options:FadeIn(PROptions_Buffs)
-end
-
-function Frames:EnableButtons()
-	local selected = PROptions_Frames.selected
-	if selected then
-		PRFrames_Edit:Enable()
-		PRFrames_Delete:Enable()
-	else
-		PRFrames_Edit:Disable()
-		PRFrames_Delete:Disable()
-	end
+	PROptions_Frames_EditTitle:SetText("")
+	UIDropDownMenu_ClearAll(PRFrames_GroupByDropDown)
+	UIDropDownMenu_ClearAll(PRFrames_SortDropDown)
+	UIDropDownMenu_ClearAll(PRFrames_ColAnchorDropDown)
+
+	for k,v in pairs(self.filters) do v:SetChecked(false) end
+
+	PROptions_Frames_EditNumCols:SetText("")
+	PROptions_Frames_EditMaxUnits:SetText("")
+	PROptions_Frames_EditColSpacing:SetText("")
+	PRFrame_HeaderBackdrop:SetChecked(false)
+	PRFrame_Scale:SetValue(1.0)
+	PRFrame_Strict:SetChecked(false)
+	PRFrame_ColorClass:SetChecked(false)
+	PRFrame_ReverseBar:SetChecked(false)
+	PRFrame_Deficit:SetChecked(false)
+	PRFrame_AlignRight:SetChecked(false)
+	PRFrame_AlignBottom:SetChecked(false)
+
+	self.options:FadeOut(PROptions_Frames_Edit)
+	self.options:FadeIn(PROptions_Frames)
 end

 StaticPopupDialogs["PR_FRAME_SAVE_ERROR"] = {
@@ -480,3 +549,77 @@ StaticPopupDialogs["PR_FRAME_SAVE_ERROR"] = {
 	timeout = 0,
 	hideOnEscape = 1
 }
+
+local function colorSwatchCancel()
+	local self = ColorPickerFrame.object
+	local r, g, b, a = self.r, self.g, self.b
+	self.normalTexture:SetVertexColor(r, g, b, a)
+end
+
+local function colorSwatchColor()
+	local self = ColorPickerFrame.object
+	local r, g, b = ColorPickerFrame:GetColorRGB()
+	local a = OpacitySliderFrame:GetValue()
+	self.normalTexture:SetVertexColor(r, g, b, a)
+end
+
+local function colorSwatchOpacity()
+	local self = ColorPickerFrame.object;
+	local a = OpacitySliderFrame:GetValue();
+	self.normalTexture:SetAlpha(a)
+end
+
+local function colorSwatchShow(self)
+	local r, g, b, a = 1, 1, 1, 1
+
+	self.r, self.g, self.b, self.a = r, g, b, a
+	self.opacityFunc = colorSwatchOpacity
+	self.swatchFunc = colorSwatchColor
+	self.cancelFunc = colorSwatchCancel
+
+	ColorPickerFrame.object = self
+	ColorPickerFrame.opacity = a
+	ColorPickerFrame.hasOpacity = self.hasAlpha
+	ColorPickerFrame.func = self.swatchFunc
+	ColorPickerFrame:Show()
+	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
+
+function Frames:CreateSwatch(name, parent)
+	local swatch = CreateFrame("Button", name, parent);
+	swatch:SetHeight(20) swatch:SetWidth(20)
+
+	local bg = swatch:CreateTexture(nil, "BACKGROUND");
+	local normalTexture = swatch:CreateTexture(nil, "ARTWORK");
+
+	normalTexture:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch");
+	normalTexture:SetAllPoints(swatch);
+	swatch:SetNormalTexture(normalTexture);
+	bg:SetTexture(1, 1, 1);
+	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, false
+
+	swatch:SetScript("OnEnter", colorSwatchOnEnter)
+	swatch:SetScript("OnLeave", colorSwatchOnLeave)
+	swatch:SetScript("OnClick", colorSwatchOnClick)
+	return swatch
+end
\ No newline at end of file
diff --git a/PerfectRaid_Hacks.lua b/PerfectRaid_Hacks.lua
deleted file mode 100644
index 3f70335..0000000
--- a/PerfectRaid_Hacks.lua
+++ /dev/null
@@ -1,159 +0,0 @@
---[[-------------------------------------------------------------------------
-  Copyright (c) 2006, Jim Whitehead II <cladhaire@gmail.com>
-  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions are
-  met:
-
-      * Redistributions of source code must retain the above copyright
-        notice, this list of conditions and the following disclaimer.
-      * Redistributions in binary form must reproduce the above
-        copyright notice, this list of conditions and the following
-        disclaimer in the documentation and/or other materials provided
-        with the distribution.
-      * Neither the name of PerfectRaid nor the names of its contributors
-        may be used to endorse or promote products derived from this software
-        without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------]]
-
-local Hacks = PerfectRaid:NewModule("PerfectRaid-Hacks")
-local L = PerfectRaidLocals
-local frames
-
-function Hacks:Initialize()
-	PerfectRaid.defaults.profile.HacksScale = 1.0
-	PerfectRaid.defaults.profile.HacksBackdrop = false
-
-	frames = PerfectRaid.frames
-end
-
-function Hacks:ShowButton(button)
-	self:UpdateScale()
-	self:UpdateBackdrop()
-end
-
-local options
-function Hacks:CreateOptions(opt)
-	options = CreateFrame("Frame", "PROptions_Hacks", PROptions)
-	options:SetScript("OnShow", function() self:OnShow() end)
-
-	opt:AddOptionsTab("Hacks", options)
-
-	options.widgets = {}
-
-	local slider = CreateFrame("Slider", "PRHacks_Scale", options, "PRSliderTemplate")
-	slider.Text:SetText(L["Frame Scale"])
-	slider.High:SetText("2.0")
-	slider.Low:SetText("0.1")
-	slider:SetMinMaxValues(0.1,2.0)
-	slider:SetValueStep(0.05)
-	table.insert(options.widgets, slider)
-
-	local check = CreateFrame("CheckButton", "PRHacks_Backdrop", options, "PRCheckTemplate")
-	check.Label:SetText(L["Put a backdrop behind PerfectRaid"])
-	table.insert(options.widgets, check)
-
-	slider:SetScript("OnValueChanged", function(frame)
-		frame.Current:SetText(string.format("%0.2f", frame:GetValue()))
-		self:UpdateScale(frame:GetValue())
-	end)
-
-	local cancel = CreateFrame("Button", "PRHacks_Cancel", options, "PRButtonTemplate")
-	cancel:SetText(L["Cancel"])
-	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
-	cancel:SetScript("OnClick", function() self:OnShow() end)
-	cancel:Show()
-
-	local save = CreateFrame("Button", "PRHacks_Save", options, "PRButtonTemplate")
-	save:SetText(L["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, -5)
-		else
-			widget:SetPoint("TOPLEFT", options.widgets[idx - 1], "BOTTOMLEFT", 0, -15)
-		end
-	end
-end
-
-function Hacks:OnShow()
-	local profile = PerfectRaid.db.profile
-
-	PRHacks_Scale:SetValue(profile.HacksScale)
-	PRHacks_Backdrop:SetChecked(profile.HacksBackdrop)
-	self:UpdateScale()
-	self:UpdateBackdrop()
-end
-
-function Hacks:SaveOptions()
-	local profile = PerfectRaid.db.profile
-
-	profile.HacksScale = PRHacks_Scale:GetValue()
-	profile.HacksBackdrop = PRHacks_Backdrop:GetChecked()
-	self:UpdateScale()
-	self:SavePositions()
-	self:UpdateBackdrop()
-end
-
-function Hacks:UpdateBackdrop()
-	if not PRHeader1 or not PRHeader8 or not PRHeader4 or not PRHeader8:GetRect() then return end
-	if not self.backdrop then
-		self.backdrop = PRHeader1:CreateTexture(nil, "BACKGROUND")
-		self.backdrop:SetPoint("TOPLEFT", PRHeader1, "TOPLEFT", -5 , 15)
-		self.backdrop:SetPoint("RIGHT", PRHeader8, "RIGHT", 3, 0)
-		if PRHeader4:GetBottom() < PRHeader8:GetBottom() then
-			self.backdrop:SetPoint("BOTTOM", PRHeader4, "BOTTOM", 0, -10)
-		else
-			self.backdrop:SetPoint("BOTTOM", PRHeader8, "BOTTOM", 0, -10)
-		end
-		self.backdrop:SetTexture(0,0,0,1)
-	end
-	if PerfectRaid.db.profile.HacksBackdrop then
-		self.backdrop:Show()
-	else
-		self.backdrop:Hide()
-	end
-end
-
-function Hacks:UpdateScale(val)
-	if InCombatLockdown() then return end
-	local scale = val or PerfectRaid.db.profile.HacksScale
-	if PRHeader1:GetScale() == scale then return end
-
-	for i=1,8 do
-		local name = "PRHeader"..i
-		local button = getglobal(name)
-		if button then
-			button:SetScale(scale)
-		end
-	end
-	PerfectRaid:RestorePosition("PRHeader1")
-end
-
-function Hacks:SavePositions()
-	for i=1,8 do
-		local name = "PRHeader"..i
-		local button = getglobal(name)
-		if button then
-			PerfectRaid:SavePosition(name)
-		end
-	end
-end
-
diff --git a/PerfectRaid_Range.lua b/PerfectRaid_Range.lua
index 817f054..8b083f8 100644
--- a/PerfectRaid_Range.lua
+++ b/PerfectRaid_Range.lua
@@ -30,38 +30,49 @@
 ---------------------------------------------------------------------------]]

 local Range = PerfectRaid:NewModule("PerfectRaid-Range")
-local frames, rangespell, rate, alpha
+local frames, rangespell, rate
+local prialpha, secalpha, outalpha
+local prispell, secspell
 local L = PerfectRaidLocals

 function Range:Initialize()
 	PerfectRaid.defaults.profile.RangeCheck = true
 	PerfectRaid.defaults.profile.RangeRate = 0.2
-	PerfectRaid.defaults.profile.RangeAlpha = 0.3
+	PerfectRaid.defaults.profile.RangePriAlpha = 1.0
+	PerfectRaid.defaults.profile.RangeSecAlpha = 0.6
+	PerfectRaid.defaults.profile.RangeOutAlpha = 0.3

-	frames = PerfectRaid.frames
-end
-
--- TODO: Only works for healers, make it an option
-
-function Range:Enable()
-	if not PerfectRaid.defaults.profile.RangeCheck then return end
 	local class = select(2, UnitClass("player"))
-	local spells = {
+	local pri = {
 		DRUID = L["Healing Touch"],
 		SHAMAN = L["Healing Wave"],
 		PRIEST = L["Lesser Heal"],
 		PALADIN = L["Holy Light"],
 	}
-
-	rangespell = spells[class]

-	if rangespell then
+	PerfectRaid.defaults.profile.RangePriSpell = pri[class]
+	frames = PerfectRaid.frames
+end
+
+function Range:Enable()
+	if not PerfectRaid.defaults.profile.RangeCheck then return end
+	local class = select(2, UnitClass("player"))
+
+	prispell = PerfectRaid.db.profile.RangePriSpell
+	secspell = PerfectRaid.db.profile.RangeSecSpell
+
+	if (prispell or secspell) and PerfectRaid.db.profile.RangeCheck then
 		if not self.frame then
 			self.frame = CreateFrame("Frame")
 		end
 		self.frame:SetScript("OnUpdate", self.OnUpdate)
 		rate = PerfectRaid.db.profile.RangeRate
-		alpha = PerfectRaid.db.profile.RangeAlpha
+		prialpha = PerfectRaid.db.profile.RangePriAlpha
+		secalpha = PerfectRaid.db.profile.RangeSecAlpha
+		outalpha = PerfectRaid.db.profile.RangeOutAlpha
+		prispell = PerfectRaid.db.profile.RangePriSpell
+		secspell = PerfectRaid.db.profile.RangeSecSpell
+
 		self:RegisterMessage("DONGLE_PROFILE_CHANGED")
 	end
 end
@@ -80,7 +91,11 @@ end
 function Range:DONGLE_PROFILE_CHANGED(event, addon, svname, name)
 	if svname == "PerfectRaidDB" then
 		rate = PerfectRaid.db.profile.RangeRate
-		alpha = PerfectRaid.db.profile.RangeAlphaa
+		prialpha = PerfectRaid.db.profile.RangePriAlpha
+		secalpha = PerfectRaid.db.profile.RangeSecAlpha
+		outalpha = PerfectRaid.db.profile.RangeOutAlpha
+		prispell = PerfectRaid.db.profile.RangePriSpell
+		secspell = PerfectRaid.db.profile.RangeSecSpell
 		if PerfectRaid.db.profile.RangeCheck then
 			self:Enable()
 		else
@@ -92,12 +107,22 @@ end
 local elapsed = 0
 function Range.OnUpdate()
 	elapsed = elapsed + arg1
+	local alpha = 1
+
 	if elapsed >= rate then
 		for unit,tbl in pairs(frames) do
-			local range = IsSpellInRange(rangespell, unit) == 1
-			local alpha = range and 1.0 or alpha
+			local prirange = IsSpellInRange(prispell, unit) == 1
+
+			if prirange then
+				alpha = prialpha
+			elseif secspell and IsSpellInRange(secspell, unit) == 1 then
+				alpha = secalpha
+			else
+				alpha = outalpha
+			end
+
 			for frame in pairs(tbl) do
-					frame:SetAlpha(alpha)
+				frame:SetAlpha(alpha)
 			end
 		end
 		elapsed = 0
@@ -125,7 +150,23 @@ function Range:CreateOptions(opt)
 	slider:SetValueStep(0.1)
 	table.insert(options.widgets, slider)

-	local slider = CreateFrame("Slider", "PRRange_Alpha", options, "PRSliderTemplate")
+	local slider = CreateFrame("Slider", "PRRange_PriAlpha", options, "PRSliderTemplate")
+	slider.Text:SetText(L["Primary In-Range Alpha"])
+	slider.High:SetText("1")
+	slider.Low:SetText("0")
+	slider:SetMinMaxValues(0,1)
+	slider:SetValueStep(0.05)
+	table.insert(options.widgets, slider)
+
+	local slider = CreateFrame("Slider", "PRRange_SecAlpha", options, "PRSliderTemplate")
+	slider.Text:SetText(L["Secondary In-Range Alpha"])
+	slider.High:SetText("1")
+	slider.Low:SetText("0")
+	slider:SetMinMaxValues(0,1)
+	slider:SetValueStep(0.05)
+	table.insert(options.widgets, slider)
+
+	local slider = CreateFrame("Slider", "PRRange_OutAlpha", options, "PRSliderTemplate")
 	slider.Text:SetText(L["Out-of-Range Alpha"])
 	slider.High:SetText("1")
 	slider.Low:SetText("0")
@@ -133,6 +174,32 @@ function Range:CreateOptions(opt)
 	slider:SetValueStep(0.05)
 	table.insert(options.widgets, slider)

+	local widget = CreateFrame("Frame", nil, options)
+	widget:SetHeight(35)
+	widget:SetWidth(160)
+	local editbox = CreateFrame("EditBox", "PRRange_PriSpell", widget, "InputBoxTemplate")
+	local font = editbox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Primary Spell:"])
+	font:SetPoint("TOPLEFT", widget, "TOPLEFT", 0, 0)
+	editbox:SetAutoFocus(nil)
+	editbox:SetWidth(160)
+	editbox:SetHeight(20)
+	editbox:SetPoint("BOTTOMLEFT", 0, 0)
+	table.insert(options.widgets, widget)
+
+	local widget = CreateFrame("Frame", nil, options)
+	widget:SetHeight(35)
+	widget:SetWidth(160)
+	local editbox = CreateFrame("EditBox", "PRRange_SecSpell", widget, "InputBoxTemplate")
+	local font = editbox:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
+	font:SetText(L["Secondary Spell:"])
+	font:SetPoint("TOPLEFT", widget, "TOPLEFT", 0, 0)
+	editbox:SetAutoFocus(nil)
+	editbox:SetWidth(160)
+	editbox:SetHeight(20)
+	editbox:SetPoint("BOTTOMLEFT", 0, 0)
+	table.insert(options.widgets, widget)
+
 	local cancel = CreateFrame("Button", "PRRange_Cancel", options, "PRButtonTemplate")
 	cancel:SetText(L["Cancel"])
 	cancel:SetPoint("BOTTOMRIGHT", 0, 5)
@@ -160,7 +227,11 @@ function Range:OnShow()

 	PRRange_Enable:SetChecked(profile.RangeCheck)
 	PRRange_Rate:SetValue(profile.RangeRate)
-	PRRange_Alpha:SetValue(profile.RangeAlpha)
+	PRRange_PriAlpha:SetValue(profile.RangePriAlpha)
+	PRRange_SecAlpha:SetValue(profile.RangeSecAlpha)
+	PRRange_OutAlpha:SetValue(profile.RangeOutAlpha)
+	PRRange_PriSpell:SetText(profile.RangePriSpell or "")
+	PRRange_SecSpell:SetText(profile.RangeSecSpell or "")
 end

 function Range:SaveOptions()
@@ -168,7 +239,17 @@ function Range:SaveOptions()

 	profile.RangeCheck = PRRange_Enable:GetChecked() or false
 	profile.RangeRate = PRRange_Rate:GetValue()
-	profile.RangeAlpha = PRRange_Alpha:GetValue()
+	profile.RangePriAlpha = PRRange_PriAlpha:GetValue()
+	profile.RangeSecAlpha = PRRange_SecAlpha:GetValue()
+	profile.RangeOutAlpha = PRRange_OutAlpha:GetValue()
+
+	local prispell = PRRange_PriSpell:GetText()
+	local secspell = PRRange_SecSpell:GetText()
+	if prispell == "" then prispell = nil end
+	if secspell == "" then secspell = nil end
+
+	profile.RangePriSpell = prispell
+	profile.RangeSecSpell = secspell

 	if profile.RangeCheck then
 		self:Enable()