local AddOnName, nsVars = ...
nsVars[AddOnName].localize()

CUSTOMDROPDOWNMENU_NORMAL_FONT_COLOR = {
  r = 0,
  g = 0.5,
  b = 1,
}

CUSTOMDROPDOWNMENU_HIGHLIGHT_FONT_COLOR = {
  r = 0.7,
  g = 0.7,
  b = 0.7,
}

CUSTOMDROPDOWNMENU_MAXBUTTONS = 1;
CUSTOMDROPDOWNMENU_MAXLEVELS = 2;
CUSTOMDROPDOWNMENU_BUTTON_HEIGHT = 16;
CUSTOMDROPDOWNMENU_BORDER_HEIGHT = 15;
-- The current open menu
CUSTOMDROPDOWNMENU_OPEN_MENU = nil;
-- The current menu being initialized
CUSTOMDROPDOWNMENU_INIT_MENU = nil;
-- Current level shown of the open menu
CUSTOMDROPDOWNMENU_MENU_LEVEL = 1;
-- Current value of the open menu
CUSTOMDROPDOWNMENU_MENU_VALUE = nil;
-- Time to wait to hide the menu
CUSTOMDROPDOWNMENU_SHOW_TIME = 2;
-- Default customdropdown text height
CUSTOMDROPDOWNMENU_DEFAULT_TEXT_HEIGHT = nil;
-- List of open menus
OPEN_CUSTOMDROPDOWNMENUS = {};

local CustomDropDownMenuDelegate = CreateFrame("FRAME");

function CustomDropDownMenuDelegate_OnAttributeChanged(self, attribute, value)
  if (attribute == "createframes" and value == true) then
    CustomDropDownMenu_CreateFrames(self:GetAttribute("createframes-level"), self:GetAttribute("createframes-index"));
  elseif (attribute == "initmenu") then
    CUSTOMDROPDOWNMENU_INIT_MENU = value;
  elseif (attribute == "openmenu") then
    CUSTOMDROPDOWNMENU_OPEN_MENU = value;
  end
end

CustomDropDownMenuDelegate:SetScript("OnAttributeChanged", CustomDropDownMenuDelegate_OnAttributeChanged);

function CustomDropDownMenu_InitializeHelper(frame)
  -- This deals with the potentially tainted stuff!
  if (frame ~= CUSTOMDROPDOWNMENU_OPEN_MENU) then
    CUSTOMDROPDOWNMENU_MENU_LEVEL = 1;
  end

  -- Set the frame that's being intialized
  CustomDropDownMenuDelegate:SetAttribute("initmenu", frame);

  -- Hide all the buttons
  local button, customDropDownList;
  for i = 1, CUSTOMDROPDOWNMENU_MAXLEVELS, 1 do
    customDropDownList = _G["CustomDropDownList" .. i];
    if (i >= CUSTOMDROPDOWNMENU_MENU_LEVEL or frame ~= CUSTOMDROPDOWNMENU_OPEN_MENU) then
      customDropDownList.numButtons = 0;
      customDropDownList.maxWidth = 0;
      for j = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS, 1 do
        button = _G["CustomDropDownList" .. i .. "Button" .. j];
        button:Hide();
      end
      customDropDownList:Hide();
    end
  end
  frame:SetHeight(CUSTOMDROPDOWNMENU_BUTTON_HEIGHT * 2);
end

local function GetChild(frame, name, key)
  if (frame[key]) then
    return frame[key];
  else
    return _G[name .. key];
  end
end

function CustomDropDownMenu_Initialize(frame, initFunction, displayMode, level, menuList)
  frame.menuList = menuList;

  securecall("CustomDropDownMenu_InitializeHelper", frame);

  -- Set the initialize function and call it.  The initFunction populates the customdropdown list.
  if (initFunction) then
    CustomDropDownMenu_SetInitializeFunction(frame, initFunction);
    initFunction(frame, level, frame.menuList);
  end

  --master frame
  if (level == nil) then
    level = 1;
  end

  local customDropDownList = _G["CustomDropDownList" .. level];
  customDropDownList.customdropdown = frame;
  customDropDownList.shouldRefresh = true;

  CustomDropDownMenu_SetDisplayMode(frame, displayMode);
end

function CustomDropDownMenu_SetInitializeFunction(frame, initFunction)
  frame.initialize = initFunction;
end

function CustomDropDownMenu_SetDisplayMode(frame, displayMode)
  -- Change appearance based on the displayMode
  -- Note: this is a one time change based on previous behavior.
  if (displayMode == "MENU") then
    local name = frame:GetName();
    GetChild(frame, name, "Left"):Hide();
    GetChild(frame, name, "Middle"):Hide();
    GetChild(frame, name, "Right"):Hide();
    local button = GetChild(frame, name, "Button");
    local buttonName = button:GetName();
    GetChild(button, buttonName, "NormalTexture"):SetTexture(nil);
    GetChild(button, buttonName, "DisabledTexture"):SetTexture(nil);
    GetChild(button, buttonName, "PushedTexture"):SetTexture(nil);
    GetChild(button, buttonName, "HighlightTexture"):SetTexture(nil);
    local text = GetChild(frame, name, "Text");

    button:ClearAllPoints();
    button:SetPoint("LEFT", text, "LEFT", -9, 0);
    button:SetPoint("RIGHT", text, "RIGHT", 6, 0);
    frame.displayMode = "MENU";
  end
end

function CustomDropDownMenu_RefreshDropDownSize(self)
  self.maxWidth = CustomDropDownMenu_GetMaxButtonWidth(self);
  self:SetWidth(self.maxWidth + 25);

  for i = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS, 1 do
    local icon = _G[self:GetName() .. "Button" .. i .. "Icon"];

    if (icon.tFitCustomDropDownSizeX) then
      icon:SetWidth(self.maxWidth - 5);
    end
  end
end

-- If customdropdown is visible then see if its timer has expired, if so hide the frame
function CustomDropDownMenu_OnUpdate(self, elapsed)
  if (self.shouldRefresh) then
    CustomDropDownMenu_RefreshDropDownSize(self);
    self.shouldRefresh = false;
  end

  if (not self.showTimer or not self.isCounting) then
    return ;
  elseif (self.showTimer < 0) then
    self:Hide();
    self.showTimer = nil;
    self.isCounting = nil;
  else
    self.showTimer = self.showTimer - elapsed;
  end
end

-- Start the countdown on a frame
function CustomDropDownMenu_StartCounting(frame)
  if (frame.parent) then
    CustomDropDownMenu_StartCounting(frame.parent);
  else
    frame.showTimer = CUSTOMDROPDOWNMENU_SHOW_TIME;
    frame.isCounting = 1;
  end
end

-- Stop the countdown on a frame
function CustomDropDownMenu_StopCounting(frame)
  if (frame.parent) then
    CustomDropDownMenu_StopCounting(frame.parent);
  else
    frame.isCounting = nil;
  end
end

function CustomDropDownMenuButtonInvisibleButton_OnEnter(self)
  CustomDropDownMenu_StopCounting(self:GetParent():GetParent());
  CloseCustomDropDownMenus(self:GetParent():GetParent():GetID() + 1);
  local parent = self:GetParent();
  if (parent.tooltipTitle and parent.tooltipWhileDisabled) then
    if (parent.tooltipOnButton) then
      GameTooltip:SetOwner(parent, "ANCHOR_RIGHT");
      GameTooltip_SetTitle(GameTooltip, parent.tooltipTitle);
      if parent.tooltipInstruction then
        GameTooltip_AddInstructionLine(GameTooltip, parent.tooltipInstruction);
      end
      if parent.tooltipText then
        GameTooltip_AddNormalLine(GameTooltip, parent.tooltipText, true);
      end
      if parent.tooltipWarning then
        GameTooltip_AddColoredLine(GameTooltip, parent.tooltipWarning, RED_FONT_COLOR, true);
      end
      GameTooltip:Show();
    else
      GameTooltip_AddNewbieTip(parent, parent.tooltipTitle, 1.0, 1.0, 1.0, parent.tooltipText, 1);
    end
  end
end

function CustomDropDownMenuButtonInvisibleButton_OnLeave(self)
  CustomDropDownMenu_StartCounting(self:GetParent():GetParent());
  GameTooltip:Hide();
end

function CustomDropDownMenuButton_OnEnter(self)
  if (self.hasArrow) then
    local level = self:GetParent():GetID() + 1;
    local listFrame = _G["CustomDropDownList" .. level];
    if (not listFrame or not listFrame:IsShown() or select(2, listFrame:GetPoint()) ~= self) then
      ToggleCustomDropDownMenu(self:GetParent():GetID() + 1, self.value, nil, nil, nil, nil, self.menuList, self);
    end
  else
    CloseCustomDropDownMenus(self:GetParent():GetID() + 1);
  end
  self.Highlight:Show();
  CustomDropDownMenu_StopCounting(self:GetParent());
  if (self.tooltipTitle and not self.noTooltipWhileEnabled) then
    if (self.tooltipOnButton) then
      GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
      GameTooltip_SetTitle(GameTooltip, self.tooltipTitle);
      if self.tooltipText then
        GameTooltip_AddNormalLine(GameTooltip, self.tooltipText, true);
      end
      GameTooltip:Show();
    else
      GameTooltip_AddNewbieTip(self, self.tooltipTitle, 1.0, 1.0, 1.0, self.tooltipText, 1);
    end
  end

  if (self.mouseOverIcon ~= nil) then
    self.Icon:SetTexture(self.mouseOverIcon);
    self.Icon:Show();
  end
end

function CustomDropDownMenuButton_OnLeave(self)
  self.Highlight:Hide();
  CustomDropDownMenu_StartCounting(self:GetParent());
  GameTooltip:Hide();

  if (self.mouseOverIcon ~= nil) then
    if (self.icon ~= nil) then
      self.Icon:SetTexture(self.icon);
    else
      self.Icon:Hide();
    end
  end
end

--[[
List of button attributes
======================================================
info.text = [STRING]  --  The text of the button
info.value = [ANYTHING]  --  The value that CUSTOMDROPDOWNMENU_MENU_VALUE is set to when the button is clicked
info.func = [function()]  --  The function that is called when you click the button
info.checked = [nil, true, function]  --  Check the button if true or function returns true
info.isNotRadio = [nil, true]  --  Check the button uses radial image if false check box image if true
info.isTitle = [nil, true]  --  If it's a title the button is disabled and the font color is set to yellow
info.disabled = [nil, true]  --  Disable the button and show an invisible button that still traps the mouseover event so menu doesn't time out
info.tooltipWhileDisabled = [nil, 1] -- Show the tooltip, even when the button is disabled.
info.hasArrow = [nil, true]  --  Show the expand arrow for multilevel menus
info.hasColorSwatch = [nil, true]  --  Show color swatch or not, for color selection
info.r = [1 - 255]  --  Red color value of the color swatch
info.g = [1 - 255]  --  Green color value of the color swatch
info.b = [1 - 255]  --  Blue color value of the color swatch
info.colorCode = [STRING] -- "|cAARRGGBB" embedded hex value of the button text color. Only used when button is enabled
info.swatchFunc = [function()]  --  Function called by the color picker on color change
info.hasOpacity = [nil, 1]  --  Show the opacity slider on the colorpicker frame
info.opacity = [0.0 - 1.0]  --  Percentatge of the opacity, 1.0 is fully shown, 0 is transparent
info.opacityFunc = [function()]  --  Function called by the opacity slider when you change its value
info.cancelFunc = [function(previousValues)] -- Function called by the colorpicker when you click the cancel button (it takes the previous values as its argument)
info.notClickable = [nil, 1]  --  Disable the button and color the font white
info.notCheckable = [nil, 1]  --  Shrink the size of the buttons and don't display a check box
info.owner = [Frame]  --  CustomDropdown frame that "owns" the current customdropdownlist
info.keepShownOnClick = [nil, 1]  --  Don't hide the customdropdownlist after a button is clicked
info.tooltipTitle = [nil, STRING] -- Title of the tooltip shown on mouseover
info.tooltipText = [nil, STRING] -- Text of the tooltip shown on mouseover
info.tooltipOnButton = [nil, 1] -- Show the tooltip attached to the button instead of as a Newbie tooltip.
info.justifyH = [nil, "CENTER"] -- Justify button text
info.arg1 = [ANYTHING] -- This is the first argument used by info.func
info.arg2 = [ANYTHING] -- This is the second argument used by info.func
info.fontObject = [FONT] -- font object replacement for Normal and Highlight
info.menuTable = [TABLE] -- This contains an array of info tables to be displayed as a child menu
info.noClickSound = [nil, 1]  --  Set to 1 to suppress the sound when clicking the button. The sound only plays if .func is set.
info.padding = [nil, NUMBER] -- Number of pixels to pad the text on the right side
info.leftPadding = [nil, NUMBER] -- Number of pixels to pad the button on the left side
info.minWidth = [nil, NUMBER] -- Minimum width for this line
info.customFrame = frame -- Allows this button to be a completely custom frame, should inherit from CustomDropDownCustomMenuEntryTemplate and override appropriate methods.
info.icon = [TEXTURE] -- An icon for the button.
info.mouseOverIcon = [TEXTURE] -- An override icon when a button is moused over.
]]

function CustomDropDownMenu_CreateInfo()
  return {};
end

function CustomDropDownMenu_CreateFrames(level, index)
  while (level > CUSTOMDROPDOWNMENU_MAXLEVELS) do
    CUSTOMDROPDOWNMENU_MAXLEVELS = CUSTOMDROPDOWNMENU_MAXLEVELS + 1;
    local newList = CreateFrame("Button", "CustomDropDownList" .. CUSTOMDROPDOWNMENU_MAXLEVELS, nil, "CustomDropDownListTemplate");
    newList:SetFrameStrata("FULLSCREEN_DIALOG");
    newList:SetToplevel(true);
    newList:Hide();
    newList:SetID(CUSTOMDROPDOWNMENU_MAXLEVELS);
    newList:SetWidth(180)
    newList:SetHeight(10)
    for i = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS do
      local newButton = CreateFrame("Button", "CustomDropDownList" .. CUSTOMDROPDOWNMENU_MAXLEVELS .. "Button" .. i, newList, "CustomDropDownMenuButtonTemplate");
      newButton:SetID(i);
    end
  end

  while (index > CUSTOMDROPDOWNMENU_MAXBUTTONS) do
    CUSTOMDROPDOWNMENU_MAXBUTTONS = CUSTOMDROPDOWNMENU_MAXBUTTONS + 1;
    for i = 1, CUSTOMDROPDOWNMENU_MAXLEVELS do
      local newButton = CreateFrame("Button", "CustomDropDownList" .. i .. "Button" .. CUSTOMDROPDOWNMENU_MAXBUTTONS, _G["CustomDropDownList" .. i], "CustomDropDownMenuButtonTemplate");
      newButton:SetID(CUSTOMDROPDOWNMENU_MAXBUTTONS);
    end
  end
end

function CustomDropDownMenu_AddSeparator(level)
  local separatorInfo = {
    hasArrow = false;
    dist = 0;
    isTitle = true;
    isUninteractable = true;
    notCheckable = true;
    iconOnly = true;
    icon = "Interface\\Common\\UI-TooltipDivider-Transparent";
    tCoordLeft = 0;
    tCoordRight = 1;
    tCoordTop = 0;
    tCoordBottom = 1;
    tSizeX = 0;
    tSizeY = 8;
    tFitCustomDropDownSizeX = true;
    iconInfo = {
      tCoordLeft = 0,
      tCoordRight = 1,
      tCoordTop = 0,
      tCoordBottom = 1,
      tSizeX = 0,
      tSizeY = 8,
      tFitCustomDropDownSizeX = true
    },
  };

  CustomDropDownMenu_AddButton(separatorInfo, level);
end

function CustomDropDownMenu_AddButton(info, level)
  --[[
  Might to uncomment this if there are performance issues
  if ( not CUSTOMDROPDOWNMENU_OPEN_MENU ) then
    return;
  end
  ]]
  if (not level) then
    level = 1;
  end

  local listFrame = _G["CustomDropDownList" .. level];
  local index = listFrame and (listFrame.numButtons + 1) or 1;
  local width;

  CustomDropDownMenuDelegate:SetAttribute("createframes-level", level);
  CustomDropDownMenuDelegate:SetAttribute("createframes-index", index);
  CustomDropDownMenuDelegate:SetAttribute("createframes", true);

  listFrame = listFrame or _G["CustomDropDownList" .. level];
  local listFrameName = listFrame:GetName();

  -- Set the number of buttons in the listframe
  listFrame.numButtons = index;

  local button = _G[listFrameName .. "Button" .. index];
  local normalText = _G[button:GetName() .. "NormalText"];
  local icon = _G[button:GetName() .. "Icon"];
  -- This button is used to capture the mouse OnEnter/OnLeave events if the customdropdown button is disabled, since a disabled button doesn't receive any events
  -- This is used specifically for drop down menu time outs
  local invisibleButton = _G[button:GetName() .. "InvisibleButton"];

  -- Default settings
  button:SetDisabledFontObject(GameFontDisableSmallLeft);
  invisibleButton:Hide();
  button:Enable();

  -- If not clickable then disable the button and set it white
  if (info.notClickable) then
    info.disabled = true;
    button:SetDisabledFontObject(GameFontHighlightSmallLeft);
  end

  -- Set the text color and disable it if its a title
  if (info.isTitle) then
    info.disabled = true;
    button:SetDisabledFontObject(GameFontNormalSmallLeft);
  end

  -- Disable the button if disabled and turn off the color code
  if (info.disabled) then
    button:Disable();
    invisibleButton:Show();
    info.colorCode = nil;
  end

  -- If there is a color for a disabled line, set it
  if (info.disablecolor) then
    info.colorCode = info.disablecolor;
  end

  -- Configure button
  if (info.text) then
    -- look for inline color code this is only if the button is enabled
    if (info.colorCode) then
      button:SetText(info.colorCode .. info.text .. "|r");
    else
      button:SetText(info.text);
    end

    -- Set icon
    if (info.icon or info.mouseOverIcon) then
      icon:SetSize(16, 16);
      icon:SetTexture(info.icon);
      icon:ClearAllPoints();
      icon:SetPoint("RIGHT");

      if (info.tCoordLeft) then
        icon:SetTexCoord(info.tCoordLeft, info.tCoordRight, info.tCoordTop, info.tCoordBottom);
      else
        icon:SetTexCoord(0, 1, 0, 1);
      end
      icon:Show();
    else
      icon:Hide();
    end

    -- Check to see if there is a replacement font
    if (info.fontObject) then
      button:SetNormalFontObject(info.fontObject);
      button:SetHighlightFontObject(info.fontObject);
    else
      button:SetNormalFontObject(Exo2Medium12);
      button:SetHighlightFontObject(Exo2Medium12);
    end
  else
    button:SetText("");
    icon:Hide();
  end

  button.iconOnly = nil;
  button.icon = nil;
  button.iconInfo = nil;

  if (info.iconInfo) then
    icon.tFitCustomDropDownSizeX = info.iconInfo.tFitCustomDropDownSizeX;
  else
    icon.tFitCustomDropDownSizeX = nil;
  end
  if (info.iconOnly and info.icon) then
    button.iconOnly = true;
    button.icon = info.icon;
    button.iconInfo = info.iconInfo;

    CustomDropDownMenu_SetIconImage(icon, info.icon, info.iconInfo);
    icon:ClearAllPoints();
    icon:SetPoint("LEFT");
  end

  -- Pass through attributes
  button.func = info.func;
  button.owner = info.owner;
  button.hasOpacity = info.hasOpacity;
  button.opacity = info.opacity;
  button.opacityFunc = info.opacityFunc;
  button.cancelFunc = info.cancelFunc;
  button.swatchFunc = info.swatchFunc;
  button.keepShownOnClick = info.keepShownOnClick;
  button.tooltipTitle = info.tooltipTitle;
  button.tooltipText = info.tooltipText;
  button.tooltipInstruction = info.tooltipInstruction;
  button.tooltipWarning = info.tooltipWarning;
  button.arg1 = info.arg1;
  button.arg2 = info.arg2;
  button.hasArrow = info.hasArrow;
  button.hasColorSwatch = info.hasColorSwatch;
  button.notCheckable = info.notCheckable;
  button.menuList = info.menuList;
  button.tooltipWhileDisabled = info.tooltipWhileDisabled;
  button.noTooltipWhileEnabled = info.noTooltipWhileEnabled;
  button.tooltipOnButton = info.tooltipOnButton;
  button.noClickSound = info.noClickSound;
  button.padding = info.padding;
  button.icon = info.icon;
  button.mouseOverIcon = info.mouseOverIcon;

  if (info.value) then
    button.value = info.value;
  elseif (info.text) then
    button.value = info.text;
  else
    button.value = nil;
  end

  local expandArrow = _G[listFrameName .. "Button" .. index .. "ExpandArrow"];
  expandArrow:SetShown(info.hasArrow);
  expandArrow:SetEnabled(not info.disabled);

  -- If not checkable move everything over to the left to fill in the gap where the check would be
  local xPos = 5;
  local yPos = -((button:GetID() - 1) * CUSTOMDROPDOWNMENU_BUTTON_HEIGHT) - CUSTOMDROPDOWNMENU_BORDER_HEIGHT;
  local displayInfo = normalText;
  if (info.iconOnly) then
    displayInfo = icon;
  end

  displayInfo:ClearAllPoints();
  if (info.notCheckable) then
    if (info.justifyH and info.justifyH == "CENTER") then
      displayInfo:SetPoint("CENTER", button, "CENTER", -7, 0);
    else
      displayInfo:SetPoint("LEFT", button, "LEFT", 0, 0);
    end
    xPos = xPos + 10;

  else
    xPos = xPos + 12;
    displayInfo:SetPoint("LEFT", button, "LEFT", 20, 0);
  end

  -- Adjust offset if displayMode is menu
  local frame = CUSTOMDROPDOWNMENU_OPEN_MENU;
  if (frame and frame.displayMode == "MENU") then
    if (not info.notCheckable) then
      xPos = xPos - 6;
    end
  end

  -- If no open frame then set the frame to the currently initialized frame
  frame = frame or CUSTOMDROPDOWNMENU_INIT_MENU;

  if (info.leftPadding) then
    xPos = xPos + info.leftPadding;
  end
  button:SetPoint("TOPLEFT", button:GetParent(), "TOPLEFT", xPos, yPos);

  -- See if button is selected by id or name
  if (frame) then
    if (CustomDropDownMenu_GetSelectedName(frame)) then
      if (button:GetText() == CustomDropDownMenu_GetSelectedName(frame)) then
        info.checked = 1;
      end
    elseif (CustomDropDownMenu_GetSelectedID(frame)) then
      if (button:GetID() == CustomDropDownMenu_GetSelectedID(frame)) then
        info.checked = 1;
      end
    elseif (CustomDropDownMenu_GetSelectedValue(frame)) then
      if (button.value == CustomDropDownMenu_GetSelectedValue(frame)) then
        info.checked = 1;
      end
    end
  end

  if not info.notCheckable then
    local check = _G[listFrameName .. "Button" .. index .. "Check"];
    local uncheck = _G[listFrameName .. "Button" .. index .. "UnCheck"];
    if (info.disabled) then
      check:SetDesaturated(true);
      check:SetAlpha(0.5);
      uncheck:SetDesaturated(true);
      uncheck:SetAlpha(0.5);
    else
      check:SetDesaturated(false);
      check:SetAlpha(1);
      uncheck:SetDesaturated(false);
      uncheck:SetAlpha(1);
    end

    if info.customCheckIconAtlas or info.customCheckIconTexture then
      check:SetTexCoord(0, 1, 0, 1);
      uncheck:SetTexCoord(0, 1, 0, 1);

      if info.customCheckIconAtlas then
        check:SetAtlas(info.customCheckIconAtlas);
        uncheck:SetAtlas(info.customUncheckIconAtlas or info.customCheckIconAtlas);
      else
        check:SetTexture(info.customCheckIconTexture);
        uncheck:SetTexture(info.customUncheckIconTexture or info.customCheckIconTexture);
      end
    elseif info.isNotRadio then
      check:SetTexCoord(0.0, 0.5, 0.0, 0.5);
      check:SetTexture("Interface\\AddOns\\SeamoreSpheres\\libraries\\widgets\\CustomDropDownMenu\\textures\\RadioChecks_Brown.tga")
      uncheck:SetTexCoord(0.5, 1.0, 0.0, 0.5);
      uncheck:SetTexture("Interface\\AddOns\\SeamoreSpheres\\libraries\\widgets\\CustomDropDownMenu\\textures\\RadioChecks_Brown.tga")
    else
      check:SetTexCoord(0.0, 0.5, 0.5, 1.0);
      check:SetTexture("Interface\\AddOns\\SeamoreSpheres\\libraries\\widgets\\CustomDropDownMenu\\textures\\RadioChecks_Brown.tga")
      uncheck:SetTexCoord(0.5, 1.0, 0.5, 1.0);
      uncheck:SetTexture("Interface\\AddOns\\SeamoreSpheres\\libraries\\widgets\\CustomDropDownMenu\\textures\\RadioChecks_Brown.tga")
    end

    -- Checked can be a function now
    local checked = info.checked;
    if (type(checked) == "function") then
      checked = checked(button);
    end

    -- Show the check if checked
    if (checked) then
      button:LockHighlight();
      check:Show();
      uncheck:Hide();
    else
      button:UnlockHighlight();
      check:Hide();
      uncheck:Show();
    end
  else
    _G[listFrameName .. "Button" .. index .. "Check"]:Hide();
    _G[listFrameName .. "Button" .. index .. "UnCheck"]:Hide();
  end
  button.checked = info.checked;

  -- If has a colorswatch, show it and vertex color it
  local colorSwatch = _G[listFrameName .. "Button" .. index .. "ColorSwatch"];
  if (info.hasColorSwatch) then
    _G["CustomDropDownList" .. level .. "Button" .. index .. "ColorSwatch" .. "NormalTexture"]:SetVertexColor(info.r, info.g, info.b);
    button.r = info.r;
    button.g = info.g;
    button.b = info.b;
    colorSwatch:Show();
  else
    colorSwatch:Hide();
  end

  CustomDropDownMenu_CheckAddCustomFrame(listFrame, button, info);

  button:SetShown(button.customFrame == nil);

  button.minWidth = info.minWidth;

  width = max(CustomDropDownMenu_GetButtonWidth(button), info.minWidth or 0);
  --Set maximum button width
  if (width > listFrame.maxWidth) then
    listFrame.maxWidth = width;
  end

  -- Set the height of the listframe
  listFrame:SetHeight((index * CUSTOMDROPDOWNMENU_BUTTON_HEIGHT) + (CUSTOMDROPDOWNMENU_BORDER_HEIGHT * 2));
end

function CustomDropDownMenu_CheckAddCustomFrame(self, button, info)
  local customFrame = info.customFrame;
  button.customFrame = customFrame;
  if customFrame then
    customFrame:SetOwningButton(button);
    customFrame:ClearAllPoints();
    customFrame:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0);
    customFrame:Show();

    CustomDropDownMenu_RegisterCustomFrame(self, customFrame);
  end
end

function CustomDropDownMenu_RegisterCustomFrame(self, customFrame)
  self.customFrames = self.customFrames or {}
  table.insert(self.customFrames, customFrame);
end

function CustomDropDownMenu_GetMaxButtonWidth(self)
  local maxWidth = 0;
  for i = 1, self.numButtons do
    local button = _G[self:GetName() .. "Button" .. i];
    local width = CustomDropDownMenu_GetButtonWidth(button);
    if (width > maxWidth) then
      maxWidth = width;
    end
  end
  return maxWidth;
end

function CustomDropDownMenu_GetButtonWidth(button)
  local minWidth = button.minWidth or 0;
  if button.customFrame and button.customFrame:IsShown() then
    return math.max(minWidth, button.customFrame:GetPreferredEntryWidth());
  end

  if not button:IsShown() then
    return 0;
  end

  local width;
  local buttonName = button:GetName();
  local icon = _G[buttonName .. "Icon"];
  local normalText = _G[buttonName .. "NormalText"];

  if (button.iconOnly and icon) then
    width = icon:GetWidth();
  elseif (normalText and normalText:GetText()) then
    width = normalText:GetWidth() + 40;

    if (button.icon) then
      -- Add padding for the icon
      width = width + 10;
    end
  else
    return minWidth;
  end

  -- Add padding if has and expand arrow or color swatch
  if (button.hasArrow or button.hasColorSwatch) then
    width = width + 10;
  end
  if (button.notCheckable) then
    width = width - 30;
  end
  if (button.padding) then
    width = width + button.padding;
  end

  return math.max(minWidth, width);
end

function CustomDropDownMenu_Refresh(frame, useValue, customdropdownLevel)
  local maxWidth = 0;
  local somethingChecked = nil;
  if (not customdropdownLevel) then
    customdropdownLevel = CUSTOMDROPDOWNMENU_MENU_LEVEL;
  end

  local listFrame = _G["CustomDropDownList" .. customdropdownLevel];
  listFrame.numButtons = listFrame.numButtons or 0;
  -- Just redraws the existing menu
  for i = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS do
    local button = _G["CustomDropDownList" .. customdropdownLevel .. "Button" .. i];
    local checked = nil;

    if (i <= listFrame.numButtons) then
      -- See if checked or not
      if (CustomDropDownMenu_GetSelectedName(frame)) then
        if (button:GetText() == CustomDropDownMenu_GetSelectedName(frame)) then
          checked = 1;
        end
      elseif (CustomDropDownMenu_GetSelectedID(frame)) then
        if (button:GetID() == CustomDropDownMenu_GetSelectedID(frame)) then
          checked = 1;
        end
      elseif (CustomDropDownMenu_GetSelectedValue(frame)) then
        if (button.value == CustomDropDownMenu_GetSelectedValue(frame)) then
          checked = 1;
        end
      end
    end
    if (button.checked and type(button.checked) == "function") then
      checked = button.checked(button);
    end

    if not button.notCheckable and button:IsShown() then
      -- If checked show check image
      local checkImage = _G["CustomDropDownList" .. customdropdownLevel .. "Button" .. i .. "Check"];
      local uncheckImage = _G["CustomDropDownList" .. customdropdownLevel .. "Button" .. i .. "UnCheck"];
      if (checked) then
        somethingChecked = true;
        local icon = GetChild(frame, frame:GetName(), "Icon");
        if (button.iconOnly and icon and button.icon) then
          CustomDropDownMenu_SetIconImage(icon, button.icon, button.iconInfo);
        elseif (useValue) then
          CustomDropDownMenu_SetText(frame, button.value);
          icon:Hide();
        else
          CustomDropDownMenu_SetText(frame, button:GetText());
          icon:Hide();
        end
        button:LockHighlight();
        checkImage:Show();
        uncheckImage:Hide();
      else
        button:UnlockHighlight();
        checkImage:Hide();
        uncheckImage:Show();
      end
    end

    if (button:IsShown()) then
      local width = CustomDropDownMenu_GetButtonWidth(button);
      if (width > maxWidth) then
        maxWidth = width;
      end
    end
  end
  if (somethingChecked == nil) then
    CustomDropDownMenu_SetText(frame, VIDEO_QUALITY_LABEL6);
  end
  if (not frame.noResize) then
    for i = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS do
      local button = _G["CustomDropDownList" .. customdropdownLevel .. "Button" .. i];
      button:SetWidth(maxWidth);
    end
    CustomDropDownMenu_RefreshDropDownSize(_G["CustomDropDownList" .. customdropdownLevel]);
  end
end

function CustomDropDownMenu_RefreshAll(frame, useValue)
  for customdropdownLevel = CUSTOMDROPDOWNMENU_MENU_LEVEL, 2, -1 do
    local listFrame = _G["CustomDropDownList" .. customdropdownLevel];
    if (listFrame:IsShown()) then
      CustomDropDownMenu_Refresh(frame, nil, customdropdownLevel);
    end
  end
  -- useValue is the text on the customdropdown, only needs to be set once
  CustomDropDownMenu_Refresh(frame, useValue, 1);
end

function CustomDropDownMenu_SetIconImage(icon, texture, info)
  icon:SetTexture(texture);
  if (info.tCoordLeft) then
    icon:SetTexCoord(info.tCoordLeft, info.tCoordRight, info.tCoordTop, info.tCoordBottom);
  else
    icon:SetTexCoord(0, 1, 0, 1);
  end
  if (info.tSizeX) then
    icon:SetWidth(info.tSizeX);
  else
    icon:SetWidth(16);
  end
  if (info.tSizeY) then
    icon:SetHeight(info.tSizeY);
  else
    icon:SetHeight(16);
  end
  icon:Show();
end

function CustomDropDownMenu_SetSelectedName(frame, name, useValue)
  frame.selectedName = name;
  frame.selectedID = nil;
  frame.selectedValue = nil;
  CustomDropDownMenu_Refresh(frame, useValue);
end

function CustomDropDownMenu_SetSelectedValue(frame, value, useValue)
  -- useValue will set the value as the text, not the name
  frame.selectedName = nil;
  frame.selectedID = nil;
  frame.selectedValue = value;
  CustomDropDownMenu_Refresh(frame, useValue);
end

function CustomDropDownMenu_SetSelectedID(frame, id, useValue)
  frame.selectedID = id;
  frame.selectedName = nil;
  frame.selectedValue = nil;
  CustomDropDownMenu_Refresh(frame, useValue);
end

function CustomDropDownMenu_GetSelectedName(frame)
  return frame.selectedName;
end

function CustomDropDownMenu_GetSelectedID(frame)
  if (frame.selectedID) then
    return frame.selectedID;
  else
    -- If no explicit selectedID then try to send the id of a selected value or name
    local listFrame = _G["CustomDropDownList" .. CUSTOMDROPDOWNMENU_MENU_LEVEL];
    for i = 1, listFrame.numButtons do
      local button = _G["CustomDropDownList" .. CUSTOMDROPDOWNMENU_MENU_LEVEL .. "Button" .. i];
      -- See if checked or not
      if (CustomDropDownMenu_GetSelectedName(frame)) then
        if (button:GetText() == CustomDropDownMenu_GetSelectedName(frame)) then
          return i;
        end
      elseif (CustomDropDownMenu_GetSelectedValue(frame)) then
        if (button.value == CustomDropDownMenu_GetSelectedValue(frame)) then
          return i;
        end
      end
    end
  end
end

function CustomDropDownMenu_GetSelectedValue(frame)
  return frame.selectedValue;
end

function CustomDropDownMenuButton_OnClick(self)
  local checked = self.checked;
  if (type(checked) == "function") then
    checked = checked(self);
  end

  if (self.keepShownOnClick) then
    if not self.notCheckable then
      if (checked) then
        _G[self:GetName() .. "Check"]:Hide();
        _G[self:GetName() .. "UnCheck"]:Show();
        checked = false;
      else
        _G[self:GetName() .. "Check"]:Show();
        _G[self:GetName() .. "UnCheck"]:Hide();
        checked = true;
      end
    end
  else
    self:GetParent():Hide();
  end

  if (type(self.checked) ~= "function") then
    self.checked = checked;
  end

  -- saving this here because func might use a customdropdown, changing this self's attributes
  local playSound = true;
  if (self.noClickSound) then
    playSound = false;
  end

  local func = self.func;
  if (func) then
    func(self, self.arg1, self.arg2, checked);
  else
    return ;
  end

  if (playSound) then
    PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON);
  end
end

function HideCustomDropDownMenu(level)
  local listFrame = _G["CustomDropDownList" .. level];
  listFrame:Hide();
end

function ToggleCustomDropDownMenu(level, value, customDropDownFrame, anchorName, xOffset, yOffset, menuList, button, autoHideDelay)
  if (not level) then
    level = 1;
  end
  CustomDropDownMenuDelegate:SetAttribute("createframes-level", level);
  CustomDropDownMenuDelegate:SetAttribute("createframes-index", 0);
  CustomDropDownMenuDelegate:SetAttribute("createframes", true);
  CUSTOMDROPDOWNMENU_MENU_LEVEL = level;
  CUSTOMDROPDOWNMENU_MENU_VALUE = value;
  local listFrame = _G["CustomDropDownList" .. level];
  local listFrameName = "CustomDropDownList" .. level;
  local tempFrame;
  local point, relativePoint, relativeTo;
  if (not customDropDownFrame) then
    tempFrame = button:GetParent();
  else
    tempFrame = customDropDownFrame;
  end
  if (listFrame:IsShown() and (CUSTOMDROPDOWNMENU_OPEN_MENU == tempFrame)) then
    listFrame:Hide();
  else
    -- Set the customdropdownframe scale
    local uiScale;
    local uiParentScale = UIParent:GetScale();
    if (GetCVar("useUIScale") == "1") then
      uiScale = tonumber(GetCVar("uiscale"));
      if (uiParentScale < uiScale) then
        uiScale = uiParentScale;
      end
    else
      uiScale = uiParentScale;
    end
    listFrame:SetScale(uiScale);

    -- Hide the listframe anyways since it is redrawn OnShow()
    listFrame:Hide();

    -- Frame to anchor the customdropdown menu to
    local anchorFrame;

    -- Display stuff
    -- Level specific stuff
    if (level == 1) then
      CustomDropDownMenuDelegate:SetAttribute("openmenu", customDropDownFrame);
      listFrame:ClearAllPoints();
      -- If there's no specified anchorName then use left side of the customdropdown menu
      if (not anchorName) then
        -- See if the anchor was set manually using setanchor
        if (customDropDownFrame.xOffset) then
          xOffset = customDropDownFrame.xOffset;
        end
        if (customDropDownFrame.yOffset) then
          yOffset = customDropDownFrame.yOffset;
        end
        if (customDropDownFrame.point) then
          point = customDropDownFrame.point;
        end
        if (customDropDownFrame.relativeTo) then
          relativeTo = customDropDownFrame.relativeTo;
        else
          relativeTo = GetChild(CUSTOMDROPDOWNMENU_OPEN_MENU, CUSTOMDROPDOWNMENU_OPEN_MENU:GetName(), "Left");
        end
        if (customDropDownFrame.relativePoint) then
          relativePoint = customDropDownFrame.relativePoint;
        end
      elseif (anchorName == "cursor") then
        relativeTo = nil;
        local cursorX, cursorY = GetCursorPosition();
        cursorX = cursorX / uiScale;
        cursorY = cursorY / uiScale;

        if (not xOffset) then
          xOffset = 0;
        end
        if (not yOffset) then
          yOffset = 0;
        end
        xOffset = cursorX + xOffset;
        yOffset = cursorY + yOffset;
      else
        -- See if the anchor was set manually using setanchor
        if (customDropDownFrame.xOffset) then
          xOffset = customDropDownFrame.xOffset;
        end
        if (customDropDownFrame.yOffset) then
          yOffset = customDropDownFrame.yOffset;
        end
        if (customDropDownFrame.point) then
          point = customDropDownFrame.point;
        end
        if (customDropDownFrame.relativeTo) then
          relativeTo = customDropDownFrame.relativeTo;
        else
          relativeTo = anchorName;
        end
        if (customDropDownFrame.relativePoint) then
          relativePoint = customDropDownFrame.relativePoint;
        end
      end
      if (not xOffset or not yOffset) then
        xOffset = 8;
        yOffset = 22;
      end
      if (not point) then
        point = "TOPLEFT";
      end
      if (not relativePoint) then
        relativePoint = "BOTTOMLEFT";
      end
      listFrame:SetPoint(point, relativeTo, relativePoint, xOffset, yOffset);
    else
      if (not customDropDownFrame) then
        customDropDownFrame = CUSTOMDROPDOWNMENU_OPEN_MENU;
      end
      listFrame:ClearAllPoints();
      -- If this is a customdropdown button, not the arrow anchor it to itself
      if (strsub(button:GetParent():GetName(), 0, 18) == "CustomDropDownList" and strlen(button:GetParent():GetName()) == 19) then
        anchorFrame = button;
      else
        anchorFrame = button:GetParent();
      end
      point = "TOPLEFT";
      relativePoint = "TOPRIGHT";
      listFrame:SetPoint(point, anchorFrame, relativePoint, 0, 0);
    end

    -- Change list box appearance depending on display mode
    if (customDropDownFrame and customDropDownFrame.displayMode == "MENU") then
      _G[listFrameName .. "Backdrop"]:Hide();
      _G[listFrameName .. "MenuBackdrop"]:Show();
    else
      _G[listFrameName .. "Backdrop"]:Show();
      _G[listFrameName .. "MenuBackdrop"]:Hide();
    end
    customDropDownFrame.menuList = menuList;
    CustomDropDownMenu_Initialize(customDropDownFrame, customDropDownFrame.initialize, nil, level, menuList);
    -- If no items in the drop down don't show it
    if (listFrame.numButtons == 0) then
      return ;
    end

    -- Check to see if the customdropdownlist is off the screen, if it is anchor it to the top of the customdropdown button
    listFrame:Show();
    -- Hack since GetCenter() is returning coords relative to 1024x768
    local x, y = listFrame:GetCenter();
    -- Hack will fix this in next revision of customdropdowns
    if (not x or not y) then
      listFrame:Hide();
      return ;
    end

    listFrame.onHide = customDropDownFrame.onHide;


    --  We just move level 1 enough to keep it on the screen. We don't necessarily change the anchors.
    if (level == 1) then
      local offLeft = listFrame:GetLeft() / uiScale;
      local offRight = (GetScreenWidth() - listFrame:GetRight()) / uiScale;
      local offTop = (GetScreenHeight() - listFrame:GetTop()) / uiScale;
      local offBottom = listFrame:GetBottom() / uiScale;

      local xAddOffset, yAddOffset = 0, 0;
      if (offLeft < 0) then
        xAddOffset = -offLeft;
      elseif (offRight < 0) then
        xAddOffset = offRight;
      end

      if (offTop < 0) then
        yAddOffset = offTop;
      elseif (offBottom < 0) then
        yAddOffset = -offBottom;
      end

      listFrame:ClearAllPoints();
      if (anchorName == "cursor") then
        listFrame:SetPoint(point, relativeTo, relativePoint, xOffset + xAddOffset, yOffset + yAddOffset);
      else
        listFrame:SetPoint(point, relativeTo, relativePoint, xOffset + xAddOffset, yOffset + yAddOffset);
      end
    else
      -- Determine whether the menu is off the screen or not
      local offscreenY, offscreenX;
      if ((y - listFrame:GetHeight() / 2) < 0) then
        offscreenY = 1;
      end
      if (listFrame:GetRight() > GetScreenWidth()) then
        offscreenX = 1;
      end
      if (offscreenY and offscreenX) then
        point = gsub(point, "TOP(.*)", "BOTTOM%1");
        point = gsub(point, "(.*)LEFT", "%1RIGHT");
        relativePoint = gsub(relativePoint, "TOP(.*)", "BOTTOM%1");
        relativePoint = gsub(relativePoint, "(.*)RIGHT", "%1LEFT");
        xOffset = -11;
        yOffset = -14;
      elseif (offscreenY) then
        point = gsub(point, "TOP(.*)", "BOTTOM%1");
        relativePoint = gsub(relativePoint, "TOP(.*)", "BOTTOM%1");
        xOffset = 0;
        yOffset = -14;
      elseif (offscreenX) then
        point = gsub(point, "(.*)LEFT", "%1RIGHT");
        relativePoint = gsub(relativePoint, "(.*)RIGHT", "%1LEFT");
        xOffset = -11;
        yOffset = 14;
      else
        xOffset = 0;
        yOffset = 14;
      end

      listFrame:ClearAllPoints();
      listFrame.parentLevel = tonumber(strmatch(anchorFrame:GetName(), "CustomDropDownList(%d+)"));
      listFrame.parentID = anchorFrame:GetID();
      listFrame:SetPoint(point, anchorFrame, relativePoint, xOffset, yOffset);
    end

    if (autoHideDelay and tonumber(autoHideDelay)) then
      listFrame.showTimer = autoHideDelay;
      listFrame.isCounting = 1;
    end
  end
end

function CloseCustomDropDownMenus(level)
  if (not level) then
    level = 1;
  end
  for i = level, CUSTOMDROPDOWNMENU_MAXLEVELS do
    _G["CustomDropDownList" .. i]:Hide();
  end
end

function CustomDropDownMenu_OnHide(self)
  local id = self:GetID()
  if (self.onHide) then
    self.onHide(id + 1);
    self.onHide = nil;
  end
  CloseCustomDropDownMenus(id + 1);
  OPEN_CUSTOMDROPDOWNMENUS[id] = nil;
  if (id == 1) then
    CUSTOMDROPDOWNMENU_OPEN_MENU = nil;
  end

  if self.customFrames then
    for index, frame in ipairs(self.customFrames) do
      frame:Hide();
    end

    self.customFrames = nil;
  end
end

function CustomDropDownMenu_SetWidth(frame, width, padding)
  local frameName = frame:GetName();
  GetChild(frame, frameName, "Middle"):SetWidth(width);
  local defaultPadding = 25;
  if (padding) then
    frame:SetWidth(width + padding);
  else
    frame:SetWidth(width + defaultPadding + defaultPadding);
  end
  if (padding) then
    GetChild(frame, frameName, "Text"):SetWidth(width);
  else
    GetChild(frame, frameName, "Text"):SetWidth(width - defaultPadding);
  end
  frame.noResize = 1;
end

function CustomDropDownMenu_SetButtonWidth(frame, width)
  local frameName = frame:GetName();
  if (width == "TEXT") then
    width = GetChild(frame, frameName, "Text"):GetWidth();
  end

  GetChild(frame, frameName, "Button"):SetWidth(width);
  frame.noResize = 1;
end

function CustomDropDownMenu_SetText(frame, text)
  local frameName = frame:GetName();
  GetChild(frame, frameName, "Text"):SetText(text);
end

function CustomDropDownMenu_GetText(frame)
  local frameName = frame:GetName();
  return GetChild(frame, frameName, "Text"):GetText();
end

function CustomDropDownMenu_ClearAll(frame)
  -- Previous code refreshed the menu quite often and was a performance bottleneck
  frame.selectedID = nil;
  frame.selectedName = nil;
  frame.selectedValue = nil;
  CustomDropDownMenu_SetText(frame, "");

  local button, checkImage, uncheckImage;
  for i = 1, CUSTOMDROPDOWNMENU_MAXBUTTONS do
    button = _G["CustomDropDownList" .. CUSTOMDROPDOWNMENU_MENU_LEVEL .. "Button" .. i];
    button:UnlockHighlight();

    checkImage = _G["CustomDropDownList" .. CUSTOMDROPDOWNMENU_MENU_LEVEL .. "Button" .. i .. "Check"];
    checkImage:Hide();
    uncheckImage = _G["CustomDropDownList" .. CUSTOMDROPDOWNMENU_MENU_LEVEL .. "Button" .. i .. "UnCheck"];
    uncheckImage:Hide();
  end
end

function CustomDropDownMenu_JustifyText(frame, justification)
  local frameName = frame:GetName();
  local text = GetChild(frame, frameName, "Text");
  text:ClearAllPoints();
  if (justification == "LEFT") then
    text:SetPoint("LEFT", GetChild(frame, frameName, "Left"), "LEFT", 27, 2);
    text:SetJustifyH("LEFT");
  elseif (justification == "RIGHT") then
    text:SetPoint("RIGHT", GetChild(frame, frameName, "Right"), "RIGHT", -43, 2);
    text:SetJustifyH("RIGHT");
  elseif (justification == "CENTER") then
    text:SetPoint("CENTER", GetChild(frame, frameName, "Middle"), "CENTER", -5, 2);
    text:SetJustifyH("CENTER");
  end
end

function CustomDropDownMenu_SetAnchor(customdropdown, xOffset, yOffset, point, relativeTo, relativePoint)
  customdropdown.xOffset = xOffset;
  customdropdown.yOffset = yOffset;
  customdropdown.point = point;
  customdropdown.relativeTo = relativeTo;
  customdropdown.relativePoint = relativePoint;
end

function CustomDropDownMenu_GetCurrentDropDown()
  if (CUSTOMDROPDOWNMENU_OPEN_MENU) then
    return CUSTOMDROPDOWNMENU_OPEN_MENU;
  elseif (CUSTOMDROPDOWNMENU_INIT_MENU) then
    return CUSTOMDROPDOWNMENU_INIT_MENU;
  end
end

function CustomDropDownMenuButton_GetChecked(self)
  return _G[self:GetName() .. "Check"]:IsShown();
end

function CustomDropDownMenuButton_GetName(self)
  return _G[self:GetName() .. "NormalText"]:GetText();
end

function CustomDropDownMenuButton_OpenColorPicker(self, button)
  CloseMenus();
  if (not button) then
    button = self;
  end
  CUSTOMDROPDOWNMENU_MENU_VALUE = button.value;
  OpenColorPicker(button);
end

function CustomDropDownMenu_DisableButton(level, id)
  _G["CustomDropDownList" .. level .. "Button" .. id]:Disable();
end

function CustomDropDownMenu_EnableButton(level, id)
  _G["CustomDropDownList" .. level .. "Button" .. id]:Enable();
end

function CustomDropDownMenu_SetButtonText(level, id, text, colorCode)
  local button = _G["CustomDropDownList" .. level .. "Button" .. id];
  if (colorCode) then
    button:SetText(colorCode .. text .. "|r");
  else
    button:SetText(text);
  end
end

function CustomDropDownMenu_SetButtonNotClickable(level, id)
  _G["CustomDropDownList" .. level .. "Button" .. id]:SetDisabledFontObject(GameFontHighlightSmallLeft);
end

function CustomDropDownMenu_SetButtonClickable(level, id)
  _G["CustomDropDownList" .. level .. "Button" .. id]:SetDisabledFontObject(GameFontDisableSmallLeft);
end

function CustomDropDownMenu_DisableDropDown(customDropDown)
  local customDropDownName = customDropDown:GetName();
  local label = GetChild(customDropDown, customDropDownName, "Label");
  if (label) then
    label:SetVertexColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
  end
  GetChild(customDropDown, customDropDownName, "Text"):SetVertexColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
  GetChild(customDropDown, customDropDownName, "Button"):Disable();
  customDropDown.isDisabled = 1;
end

function CustomDropDownMenu_EnableDropDown(customDropDown)
  local customDropDownName = customDropDown:GetName();
  local label = GetChild(customDropDown, customDropDownName, "Label");
  if (label) then
    label:SetVertexColor(CUSTOMDROPDOWNMENU_NORMAL_FONT_COLOR.r, CUSTOMDROPDOWNMENU_NORMAL_FONT_COLOR.g, CUSTOMDROPDOWNMENU_NORMAL_FONT_COLOR.b)
  end
  GetChild(customDropDown, customDropDownName, "Text"):SetVertexColor(
      CUSTOMDROPDOWNMENU_HIGHLIGHT_FONT_COLOR.r,
      CUSTOMDROPDOWNMENU_HIGHLIGHT_FONT_COLOR.g,
      CUSTOMDROPDOWNMENU_HIGHLIGHT_FONT_COLOR.b)

  GetChild(customDropDown, customDropDownName, "Button"):Enable();
  customDropDown.isDisabled = nil;
end

function CustomDropDownMenu_IsEnabled(customDropDown)
  return not customDropDown.isDisabled;
end

function CustomDropDownMenu_GetValue(id)
  --Only works if the customdropdown has just been initialized, lame, I know =(
  local button = _G["CustomDropDownList1Button" .. id];
  if (button) then
    return _G["CustomDropDownList1Button" .. id].value;
  else
    return nil;
  end
end

function OpenColorPicker(info)
  ColorPickerFrame.func = info.swatchFunc;
  ColorPickerFrame.hasOpacity = info.hasOpacity;
  ColorPickerFrame.opacityFunc = info.opacityFunc;
  ColorPickerFrame.opacity = info.opacity;
  ColorPickerFrame.previousValues = { r = info.r, g = info.g, b = info.b, opacity = info.opacity };
  ColorPickerFrame.cancelFunc = info.cancelFunc;
  ColorPickerFrame.extraInfo = info.extraInfo;
  -- This must come last, since it triggers a call to ColorPickerFrame.func()
  ColorPickerFrame:SetColorRGB(info.r, info.g, info.b);
  ShowUIPanel(ColorPickerFrame);
end

function ColorPicker_GetPreviousValues()
  return ColorPickerFrame.previousValues.r, ColorPickerFrame.previousValues.g, ColorPickerFrame.previousValues.b;
end

do
  -- Add the global mouse event handler. (From SharedXML\UIDropDownMenu.lua)
  local function CustomDropDownMenu_ContainsMouse()
    for i = 1, CUSTOMDROPDOWNMENU_MAXLEVELS do
      local dropdown = _G["CustomDropDownList" .. i]

      if dropdown:IsShown() and dropdown:IsMouseOver() then
        return true
      end
    end

    return false
  end

  function CustomDropDownMenu_HandleGlobalMouseEvent(button, event)
    if event == "GLOBAL_MOUSE_DOWN" and (button == "LeftButton" or button == "RightButton") then
      if not CustomDropDownMenu_ContainsMouse() then
        CloseCustomDropDownMenus()
      end
    end
  end
end

do
  -- Handle the "GLOBAL_MOUSE_DOWN" event. (Normally done in UIParent.lua)
  local function HandlesGlobalMouseEvent(focus, buttonID, event)
    return focus and focus.HandlesGlobalMouseEvent and focus:HandlesGlobalMouseEvent(buttonID, event)
  end

  if ss.retailClient then
    CustomDropDownMenuDelegate:RegisterEvent("GLOBAL_MOUSE_DOWN")
  else
    function HandleGlobalMouseEvent(...)
      CustomDropDownMenuDelegate:GetScript("OnEvent")(CustomDropDownMenuDelegate, "GLOBAL_MOUSE_DOWN", ...)
    end
  end

  CustomDropDownMenuDelegate:HookScript("OnEvent", function(_, event, ...)
    if event == "GLOBAL_MOUSE_DOWN" then
      local mouseFocus = GetMouseFocus()
      local buttonID = ...

      if not HandlesGlobalMouseEvent(mouseFocus, buttonID, event) then
        -- Close all open CustomDropDownMenus.
        CustomDropDownMenu_HandleGlobalMouseEvent(buttonID, event)
      end
    end
  end)
end

gexport()

-- Make sure global values get cleared.
gexport("CUSTOMDROPDOWNMENU_OPEN_MENU", nil)
gexport("CUSTOMDROPDOWNMENU_INIT_MENU", nil)
gexport("CUSTOMDROPDOWNMENU_MENU_VALUE", nil)
gexport("CUSTOMDROPDOWNMENU_DEFAULT_TEXT_HEIGHT", nil)
