--[[
BagnonFrame
Functionality for Bagnon Inventory\Bank frames
BagSlots:
-2: Key (1.11)
-1: Bank (24 slots)
0: Main Inventory (16 slots)
1, 2, 3, 4: Inventory Bags (16 - 32?)
5, 6, 7, 8, 9, 10: Bank Bags (16 - 32?)
TODO:
Potentially, I can make the frame completely dynamically generated and merge Bagnon, Bagnon_Core, and Banknon
--]]
--Local constants
local FRAMESTRATA = {"LOW", "MEDIUM", "HIGH"};
local DEFAULT_COLS = 8;
local DEFAULT_SPACING = 2;
--[[
Load settings for the frame
--]]
function BagnonFrame_Load(frame, bags, title)
local frameName = frame:GetName()
--make frame close on escape
tinsert(UISpecialFrames, frameName);
--initialize variables for the frame if there are none
if(not BagnonSets[frameName] ) then
BagnonSets[frameName] = {
stayOnScreen = 1,
};
end
--add what bags are controlled by the frame
if(not BagnonSets[frameName].bags) then
BagnonSets[frameName].bags = bags;
end
--set the frame's transparency
if(BagnonSets[frameName].alpha) then
frame:SetAlpha(BagnonSets[frameName].alpha);
end
--set the frame's background
if(not BagnonSets[frameName].bg or tonumber(BagnonSets[frameName].bg) ) then
BagnonSets[frameName].bg = {r = 0, g = 0, b = 0, a = 1};
end
local bgSets= BagnonSets[frameName].bg;
frame:SetBackdropColor(bgSets.r, bgSets.g, bgSets.b, bgSets.a);
frame:SetBackdropBorderColor(1, 1, 1, bgSets.a);
--set the frame's layer (low, medium, or high)
if(BagnonSets[frameName].strata) then
BagnonFrame_SetStrata(frame, BagnonSets[frameName].strata);
end
local bagFrame = getglobal(frameName .. "Bags");
if(bagFrame and BagnonSets[frameName].bagsShown) then
bagFrame:Show();
getglobal(frameName .. "ShowBags"):SetText(BAGNON_HIDEBAGS);
end
frame:SetClampedToScreen(BagnonSets[frameName].stayOnScreen);
--load any settings needed if we have cached data
if(BagnonDB) then
frame.player = UnitName("player");
frame.defaultBags = bags;
local dropdownButton = CreateFrame("Button", frameName, frame, "BagnonDBUIDropDownButton");
dropdownButton:SetAlpha(frame:GetAlpha());
getglobal(frameName .. "Title"):SetPoint("TOPLEFT", dropdownButton, "TOPRIGHT", 2, 2);
end
BagnonFrame_OrderBags(frame, BagnonSets[frameName].reverse);
--set the frame's title
frame.title = title;
getglobal(frameName .. "Title"):SetText( format(frame.title, UnitName("player") ) );
frame:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp");
--create the frame
--reposition actually handles rescaling
BagnonFrame_Reposition(frame);
BagnonFrame_Generate(frame);
end
--[[
Generate the frame (add all items, resize)
--]]
function BagnonFrame_Generate(frame)
frame.size = 0;
local frameName = frame:GetName();
--generate a cached frame
if( Bagnon_IsCachedFrame(frame) ) then
MoneyFrame_Update(frameName .. "MoneyFrame", BagnonDB.GetMoney(frame.player));
for _, bagID in pairs(frame.defaultBags) do
BagnonFrame_AddBag(frame, bagID);
end
--generate a normal frame
else
MoneyFrame_Update(frameName .. "MoneyFrame", GetMoney());
for _, bagID in pairs(BagnonSets[frameName].bags) do
BagnonFrame_AddBag(frame, bagID);
end
end
BagnonFrame_Layout(frame, BagnonSets[frame:GetName()].cols, BagnonSets[frame:GetName()].space);
frame:Show();
end
--[[
Add all the slots of the given bag to the given frame.
Increase the total size of frame to include the size of the bag
--]]
local function CreateDummyBag(parent, bagID)
local dummyBag = CreateFrame("Frame", parent:GetName() .. "DummyBag" .. bagID, parent);
dummyBag:SetID(bagID);
return dummyBag;
end
function BagnonFrame_AddBag(frame, bagID)
local frameName = frame:GetName();
local slot = frame.size;
local bagSize;
if( Bagnon_IsCachedBag(frame.player, bagID) ) then
bagSize = (BagnonDB.GetBagData(frame.player, bagID)) or 0;
else
if(bagID == KEYRING_CONTAINER) then
bagSize = GetKeyRingSize();
else
bagSize = GetContainerNumSlots(bagID);
end
end
--update used slots
local dummyBag = getglobal(frameName .. "DummyBag" .. bagID) or CreateDummyBag(frame, bagID);
for index = 1, bagSize, 1 do
slot = slot + 1;
local item = getglobal( frameName .. "Item".. slot) or BagnonItem_Create(frameName .. "Item".. slot, dummyBag);
item:SetID(index);
item:SetParent(dummyBag);
item:Show();
BagnonItem_Update(item);
end
frame.size = frame.size + bagSize;
end
--[[
Resize and hide any unusable bag slots in the frame.
This function needs to know about the layout of the frame
--]]
function BagnonFrame_TrimToSize(frame)
if( not frame.space ) then
return;
end
local frameName = frame:GetName();
local slot, height;
--hide unused slots
if(frame.size) then
local slot = frame.size + 1;
local button = getglobal( frameName .. "Item".. slot );
while button do
button:Hide();
slot = slot + 1;
button = getglobal( frameName .. "Item".. slot );
end
end
--set the frame's width
--correction for any frame that's completely empty
if(not frame.size or frame.size == 0 ) then
height = 64;
frame:SetWidth(256);
else
--15 is the estimated border width of the frame, 37 is the width of an item button
if(frame.size < frame.cols) then
frame:SetWidth( ( 37 + frame.space ) * frame.size + 16 - frame.space );
else
frame:SetWidth( ( 37 + frame.space ) * frame.cols + 16 - frame.space );
end
--set the frame's height, adjusted to fit a bag frame if its shown/there is one
height = ( 37 + frame.space ) * math.ceil( frame.size / frame.cols ) + 64 - frame.space;
end
--adjust for the bag frame, if present
local bagFrame = getglobal(frame:GetName() .. "Bags");
if( bagFrame and bagFrame:IsShown() ) then
frame:SetHeight(height + bagFrame:GetHeight());
if(frame:GetWidth() < bagFrame:GetWidth()) then
--the +8 is for correcting for the border width of the frame
frame:SetWidth(bagFrame:GetWidth() + 8);
end
else
frame:SetHeight(height);
end
--this might no longer be necessary
--save the frame's position, if it moved
BagnonFrame_SavePosition(frame);
end
-- Update all item information for usable slots
function BagnonFrame_Update(frame, bagID)
if( not frame.size or Bagnon_IsCachedFrame(frame) ) then
return;
end
local frameName = frame:GetName();
local startSlot = 1;
local endSlot;
--if we don't know the ID of the bag that updated, then update all slots, else only update the necessary slots.
if(not bagID) then
endSlot = frame.size;
else
for _, bag in ipairs(BagnonSets[frameName].bags) do
if(bag == bagID) then
if(bag == KEYRING_CONTAINER) then
endSlot = startSlot + GetKeyRingSize() - 1;
elseif(bag == -1) then
endSlot = startSlot + 23;
else
endSlot = startSlot + GetContainerNumSlots(bag) - 1;
end
break;
else
if(bag == KEYRING_CONTAINER) then
startSlot = startSlot + GetKeyRingSize();
elseif(bag == -1) then
startSlot = startSlot + 24;
else
startSlot = startSlot + GetContainerNumSlots(bag);
end
end
end
end
--update the necessary slots
for slot = startSlot, endSlot do
local item = getglobal( frameName .. "Item" .. slot );
if( item ) then
BagnonItem_Update( item );
end
end
end
function BagnonFrame_UpdateLock(frame)
if( not frame.size or Bagnon_IsCachedFrame(frame) ) then
return;
end
local frameName = frame:GetName();
for slot = 1, frame.size do
local item = getglobal( frameName .. "Item" .. slot );
local _, _, locked = GetContainerItemInfo(item:GetParent():GetID(), item:GetID());
SetItemButtonDesaturated(item, locked, 0.5, 0.5, 0.5);
end
end
-- Layout the frame with given number of columns and button spacing
function BagnonFrame_Layout(frame, cols, space)
if(not frame.size) then
return;
end
local frameName = frame:GetName();
if(not cols) then
cols = DEFAULT_COLS;
end
if(not space) then
space = DEFAULT_SPACING;
end
if(cols == DEFAULT_COLS) then
BagnonSets[frameName].cols = nil
else
BagnonSets[frameName].cols = cols
end
if(space == DEFAULT_SPACING) then
BagnonSets[frameName].space = nil;
else
BagnonSets[frameName].space = space;
end
local rows = math.ceil( frame.size / cols );
local index = 1;
local button;
--resize the frame
frame.cols = cols;
frame.space = space;
button = getglobal(frameName .. "Item1");
if(button) then
button:ClearAllPoints();
button:SetPoint("TOPLEFT", frame, "TOPLEFT", 8, -31);
for i = 1, rows, 1 do
for j = 1, cols, 1 do
index = index + 1;
button = getglobal(frameName .. "Item" .. index);
if(not button) then
break;
end
button:ClearAllPoints();
button:SetPoint("LEFT", frameName .. "Item" .. index - 1, "RIGHT", space, 0);
end
button = getglobal(frameName .. "Item" .. index);
if(not button) then
break;
end
button:ClearAllPoints();
button:SetPoint("TOP", frameName .. "Item" .. index - cols, "BOTTOM", 0, -space);
end
end
BagnonFrame_TrimToSize(frame);
end
--[[
Safe Open/Close/Toggle
--]]
function BagnonFrame_Open(frameName, automatic)
local frame = getglobal(frameName);
if( frame ) then
BagnonFrame_Generate(frame);
else
LoadAddOn(frameName);
end
if(frame and not automatic) then
frame.manOpened = 1;
end
end
function BagnonFrame_Close(frameName, automatic)
local frame = getglobal(frameName);
if ( frame ) then
if(not (automatic and frame.manOpened) ) then
frame:Hide();
frame.manOpened = nil;
end
end
end
function BagnonFrame_Toggle(frameName)
local frame = getglobal(frameName);
if( frame ) then
if ( frame:IsVisible() ) then
BagnonFrame_Close(frameName);
else
BagnonFrame_Open(frameName);
end
else
LoadAddOn(frameName);
end
end
--[[
Highlight all the slots of
--]]
function BagnonFrame_HighlightSlots(frame, bagID)
if(not frame.size) then return; end
local frameName = frame:GetName();
local slot;
--update only the slots the player can use
for slot = 1, frame.size, 1 do
local item = getglobal( frameName .. "Item" .. slot );
if( item:GetParent():GetID() == bagID ) then
item:LockHighlight();
end
end
end
function BagnonFrame_UnhighlightAll(frame)
if(not frame.size) then return; end
local frameName = frame:GetName();
local slot;
--update only the slots the player can use
for slot = 1, frame.size, 1 do
getglobal( frameName .. "Item" .. slot ):UnlockHighlight();
end
end
--[[
Add/Remove from the frame
--]]
function BagnonFrame_ToggleBag(frame, bagID)
if(not frame) then return; end
local frameName = frame:GetName();
--add bag
if(not Bagnon_FrameHasBag(frameName, bagID) ) then
table.insert(BagnonSets[frameName].bags, bagID);
--remove bag
else
local index;
for index in BagnonSets[frameName].bags do
if( BagnonSets[frameName].bags[index] and BagnonSets[frameName].bags[index] == bagID) then
table.remove(BagnonSets[frame:GetName()].bags, index);
end
end
end
BagnonFrame_OrderBags(frame, BagnonSets.reverseOrder);
--update frame
if( frame:IsShown() ) then
BagnonFrame_Generate(frame);
end
end
--[[
Frame Positioning Functions
--]]
function BagnonFrame_StartMoving(frame)
if(not BagnonSets[frame:GetName()].locked) then
frame.isMoving = 1;
frame:StartMoving();
end
end
function BagnonFrame_StopMoving(frame)
frame.isMoving = nil;
frame:StopMovingOrSizing();
BagnonFrame_SavePosition(frame);
end
--Place the frame at the last place it was at.
--This is used when the frame first loads because the game currently does not remember the last position of a frame that's dynamically loaded
function BagnonFrame_Reposition(frame)
local frameName = frame:GetName();
if(not (BagnonSets[frameName] and BagnonSets[frameName].top) ) then
return;
end
local ratio;
--this step is to take care of a potential flaw if the UI scale changes between loadings of the frame
if(BagnonSets[frameName].parentScale ) then
ratio = BagnonSets[frameName].parentScale / frame:GetParent():GetScale();
else
ratio = 1;
end
frame:ClearAllPoints();
frame:SetScale(BagnonSets[frameName].scale);
frame:SetPoint("TOPLEFT", frame:GetParent(), "BOTTOMLEFT", BagnonSets[frameName].left * ratio, BagnonSets[frameName].top * ratio);
end
--Save the frame's current position. Needed because frames don't remember their positions if dynamically loaded.
function BagnonFrame_SavePosition(frame)
local frameName = frame:GetName();
if(not BagnonSets[frameName] ) then
BagnonSets[frameName] = {};
end
BagnonSets[frameName].top = frame:GetTop();
BagnonSets[frameName].left = frame:GetLeft();
BagnonSets[frameName].scale = frame:GetScale();
BagnonSets[frameName].parentScale = frame:GetParent():GetScale();
end
--set the layer of the frame
function BagnonFrame_SetStrata(frame, strata)
BagnonSets[frame:GetName()].strata = strata;
frame:SetFrameStrata(FRAMESTRATA[strata]);
end
--[[
Tooltip Functions
--]]
--tooltips for the title
function BagnonFrame_OnEnter()
if(BagnonSets.showTooltips) then
GameTooltip_SetDefaultAnchor(GameTooltip,this);
GameTooltip:SetText(this:GetText(), 1, 1, 1);
GameTooltip:AddLine(BAGNON_TITLE_TOOLTIP);
GameTooltip:Show();
end
end
function BagnonFrame_OnLeave()
GameTooltip:Hide();
end
--[[
Money Frame
--]]
--for money frame tooltips, ment to be overriden by forever/kc
function BagnonFrameMoney_OnEnter()
return;
end
function BagnonFrameMoney_OnLeave()
GameTooltip:Hide();
end
-- This is a hack that enables tooltips but still allows clicking on the money frame
function BagnonFrameMoney_OnClick()
local parentName = this:GetParent():GetName();
if( MouseIsOver(getglobal(parentName .. "GoldButton")) ) then
local parent = this:GetParent();
OpenCoinPickupFrame(COPPER_PER_GOLD, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent);
parent.hasPickup = 1;
elseif( MouseIsOver(getglobal(parentName .. "SilverButton")) ) then
local parent = this:GetParent();
OpenCoinPickupFrame(COPPER_PER_SILVER, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent);
parent.hasPickup = 1;
elseif( MouseIsOver(getglobal(parentName .. "CopperButton")) ) then
local parent = this:GetParent();
OpenCoinPickupFrame(1, MoneyTypeInfo[parent.moneyType].UpdateFunc(), parent);
parent.hasPickup = 1;
end
end
--[[
Rightclick Menu Stuff
--]]
--hide any menus attached to the frame, if they're visible and we're hiding the frame
function BagnonFrame_OnHide()
if(BagnonMenu:IsVisible() and BagnonMenu.frame == this) then
BagnonMenu:Hide();
end
end
--this function is here so that it can be overriden
function BagnonFrame_OnDoubleClick(frame)
return;
end
function BagnonFrame_OnClick(frame, mouseButton)
if(mouseButton == "RightButton") then
BagnonMenu_Show(frame);
end
end
--[[
Bag Sorting
--]]
--when sorting in reverse, the keyring is always at the top of the frame
local function ReverseSort(a, b)
if(a == KEYRING_CONTAINER) then
return true;
elseif(b == KEYRING_CONTAINER) then
return false;
elseif(a and b) then
return a > b;
end;
end
--when sorting in normal order, the keyring is always at the bottom of the frame
local function NormalSort(a, b)
if(a == KEYRING_CONTAINER) then
return false;
elseif(b == KEYRING_CONTAINER) then
return true;
elseif(a and b) then
return a < b;
end
end
function BagnonFrame_OrderBags(frame, reverse)
if(reverse) then
if(frame) then
table.sort(BagnonSets[frame:GetName()].bags, ReverseSort);
if(frame.defaultBags) then
table.sort(frame.defaultBags, ReverseSort);
end
end
else
if(frame) then
table.sort(BagnonSets[frame:GetName()].bags, NormalSort);
if(frame.defaultBags) then
table.sort(frame.defaultBags, NormalSort);
end
end
end
end