--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	GypsyMod Library - Gypsy_Capsule.lua
--|	Frame movement system
--|	Respect to Dhargo, creator of Flexbar, for the meta table usage
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	Summary & Explanation
--|	The Capsule library automates a number of basic operations involved in properly
--|	operating a capsule, and provides information which add-ons can use to likewise
--|	manage the contents of each capsule. Capsules operate around a few basic concepts
--|	in the way of display.
--|
--|	Screen Quadrant ["TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT"] - This is 
--|		a figure which is automatically calculated by the library, which is to
--|		basically say what "quarter" of the screen the capsule is mostly in. This
--|		information is central to everything the library is doing, and determines
--|		how most of the other properties are used. One of the main aims of the
--|		library is to keep a capsule's tab always on the "inward" portion of the
--|		screen from the rest of the capsule, to assure draggability. The same can be
--|		done with drop down menus and tooltips, based on quadrant, so they never run
--|		off-screen.
--|	Orientation ["HORI", "VERT"] - The way in which the capsule runs across the page,
--|		this is always supplied externally, and is only a simple switch for capsules
--|		to determine which tab to show - the horizontal tab, running horizontally
--|		either above or below the capsule, or the vertical tab, running vertically
--|		either to the left or right of the capsule.
--|	Order - ["ASC", "DESC"] - Ascending or descending, the order property expresses
--|		in which way the capsule is "running" from the edge of the screen. A capsule
--|		that is horizontally aligned, and dragged to the top half of the screen will
--|		be descending (it's tab will be below the capsule). This information can be
--|		applied to ordered items within a capsule as well.
--|	Direction - ["INC", "DEC"] - Increment or decrement, determines which way the
--|		capsule is "facing". A horizontally aligned capsule that has been dragged to
--|		the bottom left quadrant will be ascending (tab on top), and incremented
--|		(tab at right corner). If it were dragged then to the top right, it would
--|		be descending (tab on bottom), and decremented (tab on left corner). Like
--|		order this can be observed by add-ons utilizing the library to adjust their
--|		own objects being placed in the capsule.
--|
--|	Note that order and direction are not switched when orientation is switched,
--|	as might be expected. Rather they need to be interpreted with orientation
--|	in mind. That is, order should be used where direction would have, and direction
--|	used where order was.
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

-- Isolated file version
local version = 1.13;

--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
--|	Version History
--|	1.00 - Original library with full complement of functions ported from add-on version
--|	1.05 - Added OnUpdate functionality due to unreliable OnEnter and OnLeave catches
--|	1.06 - Added Size method and ratio, width, & height registrations.
--| 1.07 - Added GetOrientation, auto-sizing for tabs larger than their capsule
--| 1.10 - Number of fundamental structuring changes and optimizations
--| 1.11 - Added individual capsule locking
--|	1.12 - Removed Shell lock, added universal locking functionality
--|	1.13 - Added lock menu initialization
--|	1.14 - Adjusted padding temporarily.
--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

-- Load only if the library has not been loaded previously, or if previously loaded versions are older
if (not Gypsy_Capsule or not Gypsy_Capsule.version or Gypsy_Capsule.version < version) then

	-- Depends on other libraries being loaded first, exit if they have not been
	if (not Gypsy_Debug or not Gypsy_Color or not Gypsy_Sound or not Gypsy_Utility) then
		return;
	end
	
	-- Libraries	
	local sound = Gypsy_Sound;
	local color = Gypsy_Color:New();
	local utility = Gypsy_Utility:New();
	local debug = Gypsy_Debug:New();
	
	-- Capsule object
	Gypsy_Capsule = {};
	Gypsy_Capsule.version = version;
	-- Global lock setting
	Gypsy_Capsule.locked = 0;
	-- Toggle to show all capsules any time (See Gypsy_Capsule_OnUpdate)
	Gypsy_Capsule.showAll = 0;
	-- Check variable so that multiple lock buttons aren't shown
	Gypsy_Capsule.buttonShown = 0;
	
	Gypsy_Capsule.capsules = {};
	
	-- Constants
	Gypsy_Capsule.padding = 6;
	Gypsy_Capsule.tabXOffset = 7;
	Gypsy_Capsule.tabYOffset = 2;
	Gypsy_Capsule.tabSizeOffset = 24;
	Gypsy_Capsule.tabEndsSize = 32;
	Gypsy_Capsule.updateInterval = .1;
	Gypsy_Capsule.fadeTime = 0.5;

	-- Tab display map
	Gypsy_Capsule.quadrantSettings = {
		["BOTTOMLEFT"] = {
			["HORI"] = {
				["order"] = "ASC";
				["direct"] = "INC";
				["corner"] = "RIGHT";
				["tabPoint"] = "BOTTOM";
				["framePoint"] = "TOP";
				["offsetX"] = -Gypsy_Capsule.tabXOffset;
				["offsetY"] = -Gypsy_Capsule.tabYOffset;
			};
			["VERT"] = {
				["order"] = "ASC";
				["direct"] = "INC";
				["point"] = "TOP";
				["tabCorner"] = "LEFT";
				["frameCorner"] = "RIGHT";
				["offsetX"] = -Gypsy_Capsule.tabYOffset;
				["offsetY"] = -Gypsy_Capsule.tabXOffset;
			};
		};
		["BOTTOMRIGHT"] = {
			["HORI"] = {
				["order"] = "ASC";
				["direct"] = "DEC";
				["corner"] = "LEFT";
				["tabPoint"] = "BOTTOM";
				["framePoint"] = "TOP";
				["offsetX"] = Gypsy_Capsule.tabXOffset;
				["offsetY"] = -Gypsy_Capsule.tabYOffset;
			};
			["VERT"] = {
				["order"] = "ASC";
				["direct"] = "DEC";
				["point"] = "TOP";
				["tabCorner"] = "RIGHT";
				["frameCorner"] = "LEFT";
				["offsetX"] = Gypsy_Capsule.tabYOffset;
				["offsetY"] = -Gypsy_Capsule.tabXOffset;
			};
		};
		["TOPLEFT"] = {
			["HORI"] = {
				["order"] = "DESC";
				["direct"] = "INC";
				["corner"] = "RIGHT";
				["tabPoint"] = "TOP";
				["framePoint"] = "BOTTOM";
				["offsetX"] = -Gypsy_Capsule.tabXOffset;
				["offsetY"] = Gypsy_Capsule.tabYOffset;
			};
			["VERT"] = {
				["order"] = "DESC";
				["direct"] = "INC";
				["point"] = "BOTTOM";
				["tabCorner"] = "LEFT";
				["frameCorner"] = "RIGHT";
				["offsetX"] = -Gypsy_Capsule.tabYOffset;
				["offsetY"] = Gypsy_Capsule.tabXOffset;
			};
		};
		["TOPRIGHT"] = {
			["HORI"] = {
				["order"] = "DESC";
				["direct"] = "DEC";
				["corner"] = "LEFT";
				["tabPoint"] = "TOP";
				["framePoint"] = "BOTTOM";
				["offsetX"] = Gypsy_Capsule.tabXOffset;
				["offsetY"] = Gypsy_Capsule.tabYOffset;
			};
			["VERT"] = {
				["order"] = "DESC";
				["direct"] = "DEC";
				["point"] = "BOTTOM";
				["tabCorner"] = "RIGHT";
				["frameCorner"] = "LEFT";
				["offsetX"] = Gypsy_Capsule.tabYOffset;
				["offsetY"] = Gypsy_Capsule.tabXOffset;
			};
		};
		["NODIRECT"] = {
			["HORI"] = {
				["direct"] = "INC";
				["corner"] = "RIGHT";
				["offsetX"] = -Gypsy_Capsule.tabXOffset;
			};
			["VERT"] = {
				["order"] = "DESC";
				["point"] = "BOTTOM";
				["offsetY"] = Gypsy_Capsule.tabXOffset;
			};
		};
	};
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:New
	--|	Used to make this library available for use locally from script files. 
	--|		Usage: local capsule = Gypsy_Capsule:New();
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:New ()			
		local object = {};
		setmetatable(object, self);
		self.__index = self;
		return object;
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Register
	--|	The capsule registration function is responsible for initialization of a properly
	--|	setup capsule object, and can be called at any time. The function accepts multiple
	--|	arguments in the form of a table, and stores the information for access by other
	--|	library functions. The function is also responsible for the initial enabling of
	--|	all capsules which are not disabled explicitly, which facilitates the initalization
	--|	and display of said capsules.
	--|	Parameters:
	--|
	--|	* - Required
	--|
	--|		capsule* - Name of the capsule object being registered, must be a string, and
	--|			of course must be unique, as XML object names must be unique.
	--|		locked - Individual capsule lock setting, 1 or 0. Set to 0 by default.
	--|		enabled - Set to 1 by default, a value of 0 will disable, or hide, the capsule.
	--|		orient - The capsule's orientation, defaults to horizontal. The orientation of
	--|			a capsule refers to whether it is running horizontally or vertically, and
	--|			will show a properly display tab for the capsule depending on which. This
	--|			can be changed by using Gypsy_Capsule:UpdateOrientation.
	--|			Valid string values: "HORI" and "VERT"
	--|		doDir - Defaults to 1, and determines whether the capsule will automatically
	--|			adjust to a horizontal region change; that is, when the capsule is moved
	--|			from the left half of the screen to the right, or vice versa. 
	--|		orientFunc - A function to call when the capsule's orientation is changed, 
	--|			intended to take care of capsule-specific content changes based on orient-
	--|			ation.
	--|		ordFunc - A function to call when the capsule's order is changed, which occurs
	--|			when the capsule changes vertical screen regions - goes from top to bottom, 
	--|			or bottom to top. The capsule's tab also adjusts it's position when this
	--|			occurs.
	--|		menuFunc - A menu initialization function to call when the caspule's tab is
	--|			right-clicked.
	--|		title - The title text to display on the tab of a capsule, limited to 20
	--|			characters.
	--|		tooltipTitle - Title of a tooltip to display when the tab is moused over, 
	--|			limited to 100 characters.
	--|		tooltipText - Text of a tooltip to display when the tab is moused over,
	--|			limited to 100 characters.
	--|		width - Width in pixels to set the capsule to, defaults to 1.
	--|		height - Height in pixels to set the capsule to, defaults to 1.
	--|		ratio - Scaling ratio to apply to capsule padding, defaults to 1. This should
	--|			be used when an encapsulated frame is scaled, and the padding between the
	--|			capsule's border and the encapsulated frame is desired to adjust as well.
	--|			The ratio should represent the difference in size due to scaling changes
	--|			enacted upon the encapsulated frame. A ratio of 0.5 will cut the padding
	--|			in half, while a ratio of 1.25 would increase a padding of 8 to 10.
	--|			The default ratio is set to 1, or no change.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Register (args)
		if (not args) then
			debug:EPrint("No arguments passed to Gypsy_Capsule:Register().");
			return;
		end
		
		--= Data preperation =--

		local frame, locked, enabled, orient, doDir;
		local initFunc, orientFunc, ordFunc, menuFunc, saveFunc;
		local title, tooltipTitle, tooltipText;
		local width, height, ratio;
		local lockButton;

		-- Argument parsing		
		if (args.frame) then			frame = args.frame;					else	frame = nil;		end
		if (args.locked) then			locked = args.locked;				else	locked = nil;		end
		if (args.enabled) then			enabled = args.enabled;				else	enabled = nil;		end
		if (args.orient) then			orient = args.orient;				else	orient = nil;		end
		if (args.doDir) then			doDir = args.doDir;					else	doDir = nil;		end
		if (args.initFunc) then			initFunc = args.initFunc;			else	initFunc = nil;		end
		if (args.orientFunc) then		orientFunc = args.orientFunc;		else	orientFunc = nil;	end
		if (args.ordFunc) then			ordFunc = args.ordFunc;				else	ordFunc = nil;		end
		if (args.menuFunc) then			menuFunc = args.menuFunc;			else	menuFunc = nil;		end
		if (args.saveFunc) then			saveFunc = args.saveFunc;			else	saveFunc = nil;	end
		if (args.title) then			title = args.title;					else	title = nil;		end
		if (args.tooltipTitle) then		tooltipTitle = args.tooltipTitle;	else	tooltipTitle = nil;	end
		if (args.tooltipText) then		tooltipText = args.tooltipText;		else	tooltipText = nil;	end
		if (args.width) then			width = args.width;					else	width = nil;		end
		if (args.height) then			height = args.height;				else	height = nil;		end
		if (args.ratio) then			ratio = args.ratio;					else	ratio = nil;		end
		if (args.lockButton) then		lockButton = args.lockButton;		else	lockButton = nil;	end

		-- Data checks		
		if (not frame) then
			debug:EPrint("Nil capsule parameter passed to Gypsy_Capsule:Register().", "Capsule");
			return;
		else
			if (type(frame) ~= "string") then
				debug:EPrint("Non-string capsule parameter passed to Gypsy_Capsule:Register(), type: "..type(capsule), "Capsule");
				return;
			else
				if (not getglobal(frame)) then
					debug:EPrint("Invalid capsule parameter passed to Gypsy_Capsule:Register(), global reference not found. Capsule: "..capsule, "Capsule");
					return;
				end
			end
		end
		
		if (not enabled) then
			enabled = 1;
		else
			if (type(enabled) ~= "number") then
				debug:EPrint("Non-number enabled parameter passed to Gypsy_Capsule:Register(), type: "..type(enabled), "Capsule");
				return;
			else
				if (enabled > 1 or enabled < 0) then
					debug:Eprint("Invalid enabled parameter passed to Gypsy_Capsule:Register(), enabled: "..enabled, "Capsule");
					return;
				end
			end
		end
		
		if (not locked) then
			locked = 0;
		else
			if (type(locked) ~= "number") then
				debug:EPrint("Non-number locked parameter passed to Gypsy_Capsule:Register(), type: "..type(locked), "Capsule");
				return;
			else
				if (enabled > 1 or enabled < 0) then
					debug:EPrint("Invalid enabled parameter passed to Gypsy_Capsule:Register(), locked: "..locked, "Capsule");
					return;
				end
			end
		end
		
		if (not orient) then
			orient = "HORI";
		else
			if (type(orient) ~= "string") then
				debug:EPrint("Non-string orient parameter passed to Gypsy_Capsule:Register(), type: "..type(orient), "Capsule");
				return;
			else
				if (orient ~= "HORI" and orient ~= "VERT") then
					debug:EPrint("Invalid enabled parameter passed to Gypsy_Capsule:Register(), enabled: "..enabled, "Capsule");
					return;
				end
			end
		end
		
		if (not doDir) then
			doDir = 1;
		else
			if (type(doDir) ~= "number") then
				debug:EPrint("Non-number doDir parameter passed to Gypsy_Capsule:Register(), type: "..type(doDir), "Capsule");
				return;
			else
				if (doDir > 1 or doDir < 0) then
					debug:EPrint("Invalid enabled parameter passed to Gypsy_Capsule:Register(), enabled: "..enabled, "Capsule");
					return;
				end
			end
		end
		
		if (initFunc) then
			if (type(initFunc) == "string") then
				if (not getglobal(initFunc)) then
					debug:EPrint("Invalid initFunc parameter passed to Gypsy_Capsule:Register() as type string, initFunc: "..initFunc, "Capsule");
					return;
				else
					initFunc = getglobal(initFunc);
				end
			elseif (type(orientFunc) ~= "function") then
				debug:EPrint("Non-function initFunc parameter passed to Gypsy_Capsule:Register(), type: "..type(initFunc), "Capsule");
				return;
			end
		end
		
		if (orientFunc) then
			if (type(orientFunc) == "string") then
				if (not getglobal(orientFunc)) then
					debug:EPrint("Invalid orientFunc parameter passed to Gypsy_Capsule:Register() as type string, orientFunc: "..orientFunc, "Capsule");
					return;
				else
					orientFunc = getglobal(orientFunc);
				end
			elseif (type(orientFunc) ~= "function") then
				debug:EPrint("Non-function orientFunc parameter passed to Gypsy_Capsule:Register(), type: "..type(orientFunc), "Capsule");
				return;
			end
		end
		
		if (ordFunc) then
			if (type(ordFunc) == "string") then
				if (not getglobal(ordFunc)) then
					debug:EPrint("Invalid ordFunc parameter passed to Gypsy_Capsule:Register() as type string, ordFunc: "..ordFunc, "Capsule");
					return;
				else
					ordFunc = getglobal(ordFunc);
				end
			elseif (type(ordFunc) ~= "function") then
				debug:EPrint("Non-function ordFunc parameter passed to Gypsy_Capsule:Register(), type: "..type(ordFunc), "Capsule");
				return;
			end
		end
		
		if (menuFunc) then
			if (type(menuFunc) == "string") then
				if (not getglobal(menuFunc)) then
					debug:EPrint("Invalid menuFunc parameter passed to Gypsy_Capsule:Register() as type string, menuFunc: "..menuFunc, "Capsule");
					return;
				else
					menuFunc = getglobal(menuFunc);
				end
			elseif (type(menuFunc) ~= "function") then
				debug:EPrint("Non-function menuFunc parameter passed to Gypsy_Capsule:Register(), type: "..type(menuFunc), "Capsule");
				return;
			end
		end
		
		if (saveFunc) then
			if (type(saveFunc) == "string") then
				if (not getglobal(saveFunc)) then
					debug:EPrint("Invalid saveFunc parameter passed to Gypsy_Capsule:Register() as type string, saveFunc: "..saveFunc, "Capsule");
					return;
				else
					saveFunc = getglobal(saveFunc);
				end
			elseif (type(saveFunc) ~= "function") then
				debug:EPrint("Non-function menuFunc parameter passed to Gypsy_Capsule:Register(), type: "..type(saveFunc), "Capsule");
				return;
			end
		end
		
		if (not title) then
			title = getglobal(frame):GetName();
		else
			if (type(title) ~= "string") then
				debug:EPrint("Non-string title parameter passed to Gypsy_Capsule:Register(), type: "..type(title), "Capsule");
				return;
			else
				if (strlen(title) > 20) then
					title = strsub(title, 1, 20);
				end
			end
		end
		
		if (tooltipTitle) then
			if (type(tooltipTitle) ~= "string") then
				debug:EPrint("Non-string tooltipTitle parameter passed to Gypsy_Capsule:Register(), type: "..type(tooltipTitle), "Capsule");
				return;
			else
				if (strlen(tooltipTitle) > 100) then
					tooltipTitle = strsub(tooltipTitle, 1, 20);
				end
			end
		end
		
		if (tooltipText) then
			if (type(tooltipText) ~= "string") then
				debug:EPrint("Non-string tooltipText paramter passed to Gypsy_Capsule:Register(), type: "..type(tooltipText), "Capsule");
				return;
			else
				if (strlen(tooltipText) > 100) then
					tooltipText = strsub(tooltipText, 1, 100);
				end
			end
		end
		
		if (not width) then
			width = 1;
		else
			if (type(width) ~= "number") then
				debug:EPrint("Non-number width parameter passed to Gypsy_Capsule:Register(), type: "..type(width), "Capsule");
				return;
			end
		end
		
		if (not height) then
			height = 1;
		else
			if (type(height) ~= "number") then
				debug:EPrint("Non-number height parameter passed to Gypsy_Capsule:Register(), type: "..type(height), "Capsule");
				return;
			end
		end
		
		if (not ratio) then
			ratio = 1;
		else
			if (type(ratio) ~= "number") then
				debug:EPrint("Non-number ratio parameter passed to Gypsy_Capsule:Register(), type: "..type(ratio), "Capsule");
				return;
			end
		end
		
		--= Save information =--
		
		frame = getglobal(frame);
		frame.locked = locked;
		frame.enabled = enabled;
		frame.orient = orient;
		frame.doDir = doDir;
		frame.initFunc = initFunc;
		frame.orientFunc = orientFunc;
		frame.ordFunc = ordFunc;
		frame.menuFunc = menuFunc;
		frame.saveFunc = saveFunc;
		frame.title = title;
		frame.tooltipTitle = tooltipTitle;
		frame.tooltipText = tooltipText;
		frame.width = width;
		frame.height = height;
		frame.ratio = ratio;
		
		tinsert(self.capsules, frame);
		
		debug:TPrint(frame:GetName(), frame);
		
		-- Initialize lock button
		if (lockButton and Gypsy_Capsule.buttonShown == 0) then
			lockButton:Show();
			lockButton:SetChecked(lockButton.locked);
			
			local dropDown = getglobal(lockButton:GetName().."DropDown");
			self:GenerateLockMenu(dropDown);
			UIDropDownMenu_Initialize(dropDown, dropDown.initialize, "MENU");

			Gypsy_Capsule.buttonShown = 1;
			Gypsy_Capsule.locked = lockButton.locked;
			self:UpdateLocking();
		end
	end

	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Init
	--|	This function is responsible for setting up all capsule properties, and with
	--|	the initialization of their display. This is run the first time the OnUpdate
	--|	function becomes available for each capsule. If the registration is made during
	--|	events such as VARIABLES_LOADED or UNIT_NAME_UPDATE, or OnLoad, this will mean
	--|	the initalization occurs as soon as OnUpdate begins firing after the game loads.
	--|	If the registration is made after the initial UI loading period, then this will
	--|	run once, after the first time the capsule is shown (enabled).
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Init (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:Init().", "Capsule");
			return;
		end
		
		self:Size(frame);
		self:ApplyOrientation(frame);
		self:ApplyOrderAndDirection(frame);
		
		frame.updateTime = 0;
		frame.initialized = 1;
		
		if (frame.initFunc) then
			frame.initFunc();
		end
		
		if (frame.enabled == 0 or frame.locked == 1 or Gypsy_Capsule.locked == 1) then
			frame:Hide();
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Size
	--|	Sizes the capsule with information provided through registration, or updated
	--|	externally. The ratio property can be specified to enact a scaling effect on the
	--|	padding, useful when child objects of the capsule are scaled.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Size (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:Size().", "Capsule");
			return;
		end

		frame:SetWidth(frame.width + ((self.padding * frame.ratio) * 2));
		frame:SetHeight(frame.height + ((self.padding * frame.ratio) * 2));
		
		self:SizeTabs(frame);
		self:ValidatePosition(frame);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:SizeTabs
	--|	Applies labels to and automatically sizes both the horizontal and vertical tab
	--|	for a capsule. If text is wider or taller than the tab's parent frame, it will
	--|	be adjusted.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:SizeTabs (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:SizeTabs().", "Capsule");
			return;
		end
		
		local horiTab = getglobal(frame:GetName().."HoriTab");
		
		local horiMid = getglobal(horiTab:GetName().."Middle");
		horiTab:SetText(frame.title);
		
		local width = horiTab:GetTextWidth() + self.tabSizeOffset;
		horiTab:SetWidth(width);
		horiMid:SetWidth(width - self.tabEndsSize);
		
		if (width > frame:GetWidth() - self.tabXOffset) then
			horiTab:SetWidth(frame:GetWidth() - self.tabXOffset);
			horiMid:SetWidth(frame:GetWidth() - (self.tabEndsSize + self.tabXOffset));
			
			local ratio = (width - (frame:GetWidth() - self.tabXOffset)) / width;
			local newLength = floor(strlen(frame.title) - (strlen(frame.title) * ratio));
			
			horiTab:SetText(strsub(frame.title, 1, newLength));
		end
		
		local vertTab = getglobal(frame:GetName().."VertTab");
		
		local vertMid = getglobal(vertTab:GetName().."Middle");
		local text = "";
		
		for i=1, strlen(frame.title) do
			text = text..strsub(frame.title, i, i).."\n";
		end
		vertTab:SetText(text);
		
		local height = vertTab:GetTextHeight() + self.tabSizeOffset;
		vertTab:SetHeight(height);
		vertMid:SetHeight(height - self.tabEndsSize);
		
		if (height > frame:GetHeight() - self.tabXOffset) then
			vertTab:SetHeight(frame:GetHeight() - self.tabXOffset);
			vertMid:SetHeight(frame:GetHeight() - (self.tabEndsSize + self.tabXOffset));
			
			local ratio = (height - (frame:GetHeight() - self.tabXOffset)) / height;
			local newLength = floor(strlen(frame.title) - (strlen(frame.title) * ratio));
			text = "";
			
			for i=1, newLength do
				text = text..strsub(frame.title, i, i).."\n";
			end
			
			vertTab:SetText(text);
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:ValidatePosition
	--|	This function is run in all of the same cases as ApplyOrderAndDirection, and is
	--|	run directly after. For this reason, all capsule size changes should be taken care
	--|	of when the ordfunc is called, as this function will look at the outer bounds of
	--|	the capsule, and make sure it is safely inside the screen borders - correcting
	--|	it's positioning if not.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:ValidatePosition (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:ValidatePosition().", "Capsule");
			return;
		end
		
		local frameLeft = frame:GetLeft();
		local frameRight = frame:GetRight();
		local frameTop = frame:GetTop();
		local frameBottom = frame:GetBottom();
		local screenLeft = UIParent:GetLeft();
		local screenRight = UIParent:GetRight();
		local screenTop = UIParent:GetTop();
		local screenBottom = UIParent:GetBottom();
		
		local adjustment = 3;
		
		local x = frameLeft;
		local y = frameBottom;

		if (screenTop < frameTop) then
			y = screenTop - frame:GetHeight() + adjustment;
		elseif (screenBottom > frameBottom) then
			y = screenBottom - adjustment;
		end
		
		if (screenLeft > frameLeft) then
			x = screenLeft - adjustment;
		elseif (screenRight < frameRight) then
			x = screenRight - frame:GetWidth() + adjustment;
		end
		
		if (x == 0) then
			x = 1 - adjustment;
		end
		
		if (y == screenTop - frame:GetHeight()) then
			y = y - 1 + adjustment;
		end
		
		if (x == frameLeft and y == frameBottom) then
			return;
		end
		
		frame:ClearAllPoints();
		frame:SetPoint("BOTTOMLEFT", "UIParent", "BOTTOMLEFT", x, y);
		frame:SetUserPlaced(1);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:ApplyOrientation
	--|	This function simply shows the appropriate tab, depending on orientation, and then
	--|	calls the capsule's orientfunc registration if there is one.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:ApplyOrientation (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:ApplyOrientation()", "Capsule");
			return;
		end
		
		local horiTab = getglobal(frame:GetName().."HoriTab");
		local vertTab = getglobal(frame:GetName().."VertTab");
		
		if (frame.orient == "HORI") then
			horiTab:Show();
			vertTab:Hide();
		elseif (frame.orient == "VERT") then
			horiTab:Hide();
			vertTab:Show();
		end
		
		if (frame.orientFunc and frame.initialized) then
			frame.orientFunc();
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:ApplyOrderAndDirection
	--|	This function is responsible for enacting the majority of position-specific
	--|	display properties. The function is called when a capsule is initalized, when it's
	--|	orientation is changed, and when the user releases the mouse after moving the
	--|	capsule. Changes are only enacted if the capsule is moved to another quadrant, or
	--|	if a quadrant has not yet been calculated for the capsule (on init). Settings are
	--|	applied for both vertical and horizontal orientations, no matter what the capsule's
	--|	current orientation is. This function is also responsible for calling the ordfunc
	--|	if it is registered. Note that all display changes only actually effect the tabs
	--|	of the capsule. Capsule size and the display of any child frames are left up to
	--|	each add-on to determine and manage, with the help of the orientfunc and ordfunc
	--|	registrations. Also note that for the purposes of the capsule's tabs, a vertical
	--|	orientation will effectively switch the role of order and direction.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:ApplyOrderAndDirection (frame, force)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:ApplyOrderAndDirection().", "Capsule");
			return;
		end
		
		local quadrant = utility:GetQuadrant(frame);
		
		if (frame.quadrant and frame.quadrant == quadrant and not force) then
			return;
		end
		
		frame.quadrant = quadrant;
		
		local order = nil;
		local direct = nil;
		local tabPoint = nil;
		local framePoint = nil;
		local offsetX = nil;
		local offsetY = nil;		
		
		local settings = self.quadrantSettings[quadrant].HORI;
		
		order = settings.order;
		direct = settings.direct;
		corner = settings.corner;
		tabPoint = settings.tabPoint;
		framePoint = settings.framePoint;
		offsetX = settings.offsetX;
		offsetY = settings.offsetY;
		
		if (frame.doDir == 0) then
			settings = self.quadrantSettings["NODIRECT"].HORI;
			direct = settings.direct;
			corner = settings.corner;
			offsetX = settings.offsetX;
		end
		
		local horiTab = getglobal(frame:GetName().."HoriTab");
		local horiTabLeft = getglobal(horiTab:GetName().."Left");
		local horiTabMiddle = getglobal(horiTab:GetName().."Middle");
		local horiTabRight = getglobal(horiTab:GetName().."Right");
	
		horiTab:ClearAllPoints();
		horiTab:SetPoint(tabPoint..corner, frame:GetName(), framePoint..corner, offsetX, offsetY);

		if (order == "ASC") then
			horiTabLeft:SetTexCoord(0, 0.25, 0.35, 1);
			horiTabMiddle:SetTexCoord(0.25, 0.75, 0.35, 1);
			horiTabRight:SetTexCoord(0.75, 1, 0.35, 1);
		else
			horiTabLeft:SetTexCoord(0, 0.25, 1, 0.35);
			horiTabMiddle:SetTexCoord(0.25, 0.75, 1, 0.35);
			horiTabRight:SetTexCoord(0.75, 1, 1, 0.35);
		end
	
		settings = self.quadrantSettings[quadrant].VERT;
		order = settings.order;
		direct = settings.direct;
		point = settings.point;
		tabCorner = settings.tabCorner;
		frameCorner = settings.frameCorner;
		offsetX = settings.offsetX;
		offsetY = settings.offsetY;
		
		if (frame.doDir == 0) then
			settings = self.quadrantSettings["NODIRECT"].VERT;
			order = settings.order;
			point = settings.point;
			offsetY = settings.offsetY;
		end
		
		local vertTab = getglobal(frame:GetName().."VertTab");
		local vertTabTop = getglobal(vertTab:GetName().."Top");
		local vertTabMiddle = getglobal(vertTab:GetName().."Middle");
		local vertTabBottom = getglobal(vertTab:GetName().."Bottom");
		
		vertTab:ClearAllPoints();
		vertTab:SetPoint(point..tabCorner, frame:GetName(), point..frameCorner, offsetX, offsetY);
		
		if (direct == "INC") then
			vertTabTop:SetTexCoord(0, 0.65, 0, 0.25);
			vertTabMiddle:SetTexCoord(0, 0.65, 0.25, 0.75);
			vertTabBottom:SetTexCoord(0, 0.65, 0.75, 1.0);
		else
			vertTabTop:SetTexCoord(0.65, 0, 0, 0.25);
			vertTabMiddle:SetTexCoord(0.65, 0, 0.25, 0.75);
			vertTabBottom:SetTexCoord(0.65, 0, 0.75, 1);
		end
		
		if (frame.ordFunc and frame.initialized) then
			frame.ordFunc();
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:StartMoving
	--|	A small function to begin the movement of a capsule when it's tab is left-clicked.
	--|	This function also sets the showAll setting to 1, effecting the display of all
	--|	other enabled capsules while the current capsule is being dragged.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:StartMoving (frame)
		if (this:GetParent() and not frame) then
			frame = this:GetParent();
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:StartMoving().", "Capsule");
			return;
		end
		
		frame:StartMoving();
		
		self.showAll = 1;
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:StopMoving
	--|	This function stops the moving begun in Gypsy_Capsule:StartMoving, when the mouse
	--|	button is let up. It also resets the showAll setting, and runs the ApplyOrderAnd-
	--|	Direction and ValidatePosition functions on the subject capsule.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:StopMoving (frame)
		if (this:GetParent() and not frame) then
			frame = this:GetParent();
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:StopMoving().", "Capsule");
			return;
		end
		
		frame:StopMovingOrSizing();
		
		self.showAll = 0;
		
		self:ApplyOrderAndDirection(frame);
		self:ValidatePosition(frame);		
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:MouseIsOver
	--|	This function is run at every interval to check whether the mouse is over each
	--|	capsule, and determine whether it needs to be shown or not. Will also check the
	--|	showAll setting, and immediately show the capsule.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

	function Gypsy_Capsule:MouseIsOver (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:MouseIsOver().", "Capsule");
			return;
		end
		
		local tab = nil;
		
		if (frame.orient == "HORI") then
			tab = getglobal(frame:GetName().."HoriTab");
		else
			tab = getglobal(frame:GetName().."VertTab");
		end
		
		if (self.showAll == 1) then
			frame:SetAlpha(1);
		elseif (MouseIsOver(frame) or MouseIsOver(tab)) then
			if (frame:GetAlpha() < 1) then
				self:Fade(frame, "IN", frame:GetAlpha());
			end
		else
			if (frame:GetAlpha() > 0) then
				self:Fade(frame, "OUT", frame:GetAlpha());
			end
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Fade
	--|	This is a specific-use function to interface with the default UIFrameFade function.
	--|	It accepts various fade-related parameters, and sets appropriate defaults for
	--|	those not used.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Fade (frame, mode, startAlpha, endAlpha, timeToFade)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:Fade().", "Capsule");
			return;
		end
		
		local fadeInfo = {};
		
		if (not mode or (mode ~= "IN" and mode ~= "OUT")) then
			mode = "IN";
		end
		
		if (not startAlpha) then
			if (mode == "IN") then
				startAlpha = 0;
			else
				startAlpha = 1;
			end
		end
		
		if (not endAlpha) then
			if (mode == "IN") then
				endAlpha = 1;
			else
				endAlpha = 0;
			end
		end
		
		if (not timeToFade) then
			timeToFade = self.fadeTime;
		end
		
		fadeInfo.mode = mode;
		fadeInfo.timeToFade = timeToFade;
		fadeInfo.startAlpha = startAlpha;
		fadeInfo.endAlpha = endAlpha;
		UIFrameFade(frame, fadeInfo);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:UpdateOrientation
	--|	This is a utility function which should be used to alter a capsule's orientation
	--|	after load. Note that this runs ApplyOrientation, which will run any registered
	--|	orientfunc. Code must be arranged appropriately.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:UpdateOrientation (frame, orient)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:UpdateOrientation().", "Capsule");
			return;
		end
		
		if (frame.orient == orient) then
			return;
		end
		
		frame.orient = orient;
		
		self:ApplyOrientation(frame);
		self:ApplyOrderAndDirection(frame, 1);
		self:ValidatePosition(frame);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:UpdateLocking
	--|	This is the master lock handling function. It will act upon the value of
	--|	Gypsy_Capsule.locked. If set to 1, capsules are hidden, else they are shown if
	--|	enabled. The lock setting will prevent the Enable command from showing a capsule, 
	--|	though not from changing it's enabled setting. Hiding and showing is used for
	--|	best performance, as OnUpdate will not be firing when capsules are locked.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:UpdateLocking ()
		for i,v in self.capsules do
			if (v.initialized) then
				if (Gypsy_Capsule.locked == 1) then
					v:Hide();
				elseif (v.locked == 1) then
					v:Hide();
				elseif (v.enabled == 0) then
					v:Hide();
				else
					v:Show();
				end

				if (v.saveFunc) then
					v.saveFunc(v);
				end
			end
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Enable
	--|	Another utility function which will enable a capsule. The function simply updates
	--|	stored data, and shows the capsule frame which resumes OnUpdate firing.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Enable (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:Enable().", "Capsule");
			return;
		end
		
		frame.enabled = 1;
		
		if (Gypsy_Capsule.locked == 0 or not frame.initialized) then
			frame:Show();
		end
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:Disable
	--|	Does the opposite of Enable. Note this is useful for optimizing performance, as
	--|	disabled capsules are hidden, and will stop using OnUpdate.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:Disable (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:Disable().", "Capsule");
			return;
		end
		
		frame.enabled = 0;
		frame:Hide();
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:GenerateLockMenu
	--|	Generates contents for a drop-down menu. Lists each registered capsule and gives
	--|	the option to individually lock and unlock them. Main capsule lock setting will
	--|	override this.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:GenerateLockMenu (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:GenerateLockMenu().", "Capsule");
			return;
		end
		
		frame.initialize = function ()
			local info = {};
			info.text = frame:GetParent().menuHead;
			info.isTitle = 1;
			info.notCheckable = 1;
			UIDropDownMenu_AddButton(info);
			
			for i,v in Gypsy_Capsule.capsules do
				info = {};
				info.text = v.title;
				if (v.locked == 1) then
					info.checked = 1;
				end
				info.value = v:GetName();
				info.func = function ()
					if (not this.checked) then
						getglobal(this.value).locked = 1;
					else
						getglobal(this.value).locked = 0;
					end
					Gypsy_Capsule:UpdateLocking();
				end
				if (v.enabled == 0 or Gypsy_Capsule.locked == 1) then
					info.disabled = 1;
				end
				UIDropDownMenu_AddButton(info);
			end
		end
	end

	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:PlaceLockMenu
	--|	Shows and dynamically places the game drop down menu based on the positioning of
	--|	a passed frame.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:ShowLockMenu (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:PlaceLockMenu().", "Capsule");
			return;
		end
		
		ToggleDropDownMenu(1, nil, getglobal(frame:GetName().."DropDown"), frame:GetName(), 0, 0);
		
		if (GameTooltip:IsOwned(frame)) then
			GameTooltip:Hide();
		end

		utility:PositionDropDown(frame);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule:SetLockTooltip
	--|	Routine to set the tooltip for lock buttons.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule:SetLockTooltip (frame)
		if (this and not frame) then
			frame = this;
		elseif (not frame) then
			debug:EPrint("No frame supplied to Gypsy_Capsule:SetLockTooltip().", "Capsule");
			return;
		end
		
		if (frame.tooltip) then
			if (GameTooltip:IsOwned(frame)) then
				GameTooltip:Hide();
			end
			
			utility:PositionTooltip(frame);
		
			local title = "";
			local body = "";

			if (frame.tooltip.func) then
				title, body = frame.tooltip.func();
			else
				title = frame.tooltip.title;
				body = frame.tooltip.body;
			end

			GameTooltip:AddLine(title, color:Parse("normal"));

			if (body) then
				GameTooltip:AddLine(body, color:Parse("white"));
			end
			
			GameTooltip:Show();
		end
	end
	
	-- Invoke self to use in frame functions
	local capsule = Gypsy_Capsule:New();
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	The remaining functions are OnFoo functions, which must be properly setup within
	--|	a capsule's XML framework.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	
	function Gypsy_Capsule_OnLoad ()
		this:SetBackdropColor(color:Parse("black"));
		this:SetBackdropBorderColor(color:Parse("black"));
		this:SetAlpha(0);
	end
	
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--
	--|	Gypsy_Capsule_OnUpdate
	--|	This function is responsible for detecting whether a capsule should be shown or
	--|	not based on mouse position, or the showAll setting. Also runs initalization
	--|	the first time it is called. The mouse position check runs once per interval,
	--|	which is set at the top of this file.
	--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--

	function Gypsy_Capsule_OnUpdate (elapsed)
		if (not this.initialized) then
			capsule:Init(this);
			return;
		end
		
		this.updateTime = this.updateTime + elapsed;
		
		if (this.updateTime >= capsule.updateInterval) then
			this.updateTime = 0;
			
			-- Need to re-validate position if the scale changes
			--if (this.uiScale ~= UIParent:GetScale()) then
			--	this.uiScale = UIParent:GetScale();
			--	
			--	capsule:ApplyOrderAndDirection(this, 1);
			--	capsule:ValidatePosition(this);
			--end
			
			capsule:MouseIsOver(this);
		end
	end

	function Gypsy_CapsuleHoriTab_OnLoad ()
		this:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp");
	end

	function Gypsy_CapsuleHoriTab_OnMouseDown (button)
		if (button == "LeftButton") then
			PlaySound(sound.tabsmall);
			capsule:StartMoving(this:GetParent());
			
			if (GameTooltip:IsOwned(this)) then
				GameTooltip:Hide();
			end
			
			if (DropDownList1:IsVisible()) then
				CloseDropDownMenus();
			end
		end
	end
	
	function Gypsy_CapsuleHoriTab_OnMouseUp (button)
		if (button == "RightButton") then
			if (this:GetParent().menuFunc) then
				this:GetParent().menuFunc();

				if (GameTooltip:IsOwned(this)) then
					GameTooltip:Hide();
				end
			end
			PlaySound(sound.tabsmall);
		else
			PlaySound(sound.tabsmall);
			capsule:StopMoving(this:GetParent());
		end
	end

	function Gypsy_CapsuleHoriTab_OnEnter ()
		local frame = this:GetParent();
		
		if (frame.tooltipTitle) then			
			utility:PositionTooltip(this);
			
			GameTooltip:AddLine(frame.tooltipTitle, color:Parse("normal"));
			
			if (frame.tooltipText) then
				GameTooltip:AddLine(frame.tooltipText, color:Parse("white"));
			end
			
			GameTooltip:Show();
		end
	end

	function Gypsy_CapsuleHoriTab_OnLeave ()
		if (GameTooltip:IsOwned(this)) then
			GameTooltip:Hide();
		end
	end

	function Gypsy_CapsuleVertTab_OnLoad ()
		this:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp");
	end

	function Gypsy_CapsuleVertTab_OnMouseDown (button)
		if (button == "LeftButton") then
			PlaySound(sound.tabsmall);
			capsule:StartMoving(this:GetParent());
			
			if (GameTooltip:IsOwned(this)) then
				GameTooltip:Hide();
			end
		end
	end

	function Gypsy_CapsuleVertTab_OnMouseUp (button)
		if (button == "RightButton") then
			if (this:GetParent().menuFunc) then
				this:GetParent().menuFunc();
				
				if (GameTooltip:IsOwned(this)) then
					GameTooltip:Hide();
				end
			end
			PlaySound(sound.tabsmall);
		else
			PlaySound(sound.tabsmall);
			capsule:StopMoving(this:GetParent());
		end
	end

	function Gypsy_CapsuleVertTab_OnEnter ()
		local frame = this:GetParent();
		
		if (frame.tooltipTitle) then
			utility:PositionTooltip(this);
			
			GameTooltip:AddLine(frame.tooltipTitle, color:Parse("normal"));
			
			if (frame.tooltipText) then
				GameTooltip:AddLine(frame.tooltipText, color:Parse("white"));
			end
			
			GameTooltip:Show();
		end
	end

	function Gypsy_CapsuleVertTab_OnLeave ()
		if (GameTooltip:IsOwned(this)) then
			GameTooltip:Hide();
		end
	end
	
	function Gypsy_Capsule_LockButton_OnLoad ()
		this:RegisterForClicks("LeftButtonUp", "RightButtonUp");
		MiniMapBattlefieldFrame:ClearAllPoints();
		MiniMapBattlefieldFrame:SetPoint("BOTTOM", this:GetName(), "TOP", -32, 7);
	end
	
	function Gypsy_Capsule_LockButton_OnClick (button)
		if (button == "RightButton") then
			-- Reset lock appearance
			this:SetChecked(Gypsy_Capsule.locked);
			
			capsule:GenerateLockMenu(getglobal(this:GetName().."DropDown"));
			capsule:ShowLockMenu(this);
		else
			if (this:GetChecked()) then
				Gypsy_Capsule.locked = 1;
			else
				Gypsy_Capsule.locked = 0;
			end
			
			capsule:UpdateLocking();
			
			capsule:SetLockTooltip(this);
		end
	end
	
	function Gypsy_Capsule_LockButton_OnEnter ()
		capsule:SetLockTooltip(this);
	end
	
	function Gypsy_Capsule_LockButton_OnLeave ()
		if (GameTooltip:IsOwned(this)) then
			GameTooltip:Hide();
		end
	end
	
	function Gypsy_Capsule_LockButtonDropDown_OnShow ()
		UIDropDownMenu_Initialize(this, this.initialize, "MENU");		
	end
end