local tinsert, tconcat, tremove, wipe = table.insert, table.concat, table.remove, wipe
local select, pairs, next, type, unpack = select, pairs, next, type, unpack
local tostring, error = tostring, error

local Type, Version = "WeakAurasDisplayButton", 28
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end

local L = WeakAuras.L;
local fullName;

local function Hide_Tooltip()
  GameTooltip:Hide();
end

local function Show_Tooltip(owner, line1, line2)
  GameTooltip:SetOwner(owner, "ANCHOR_NONE");
  GameTooltip:SetPoint("LEFT", owner, "RIGHT");
  GameTooltip:ClearLines();
  GameTooltip:AddLine(line1);
  GameTooltip:AddLine(line2, 1, 1, 1, 1);
  GameTooltip:Show();
end

local function Show_Long_Tooltip(owner, description)
  GameTooltip:SetOwner(owner, "ANCHOR_NONE");
  GameTooltip:SetPoint("LEFT", owner, "RIGHT");
  GameTooltip:ClearLines();
  local line = 1;
  for i,v in pairs(description) do
    if(type(v) == "string") then
      if(line > 1) then
        GameTooltip:AddLine(v, 1, 1, 1, 1);
      else
        GameTooltip:AddLine(v);
      end
    elseif(type(v) == "table") then
      if(i == 1) then
        GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or ""));
      else
        GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or ""), 1, 1, 1, 1, 1, 1, 1, 1);
      end
    end
    line = line + 1;
  end
  GameTooltip:Show();
end

local function ensure(t, k, v)
  return t and k and v and t[k] == v
end

--[[     Actions     ]]--

local Actions = {
  -- move source into group or top-level list / optionally place it before or after target
  ["Group"] = function(source, groupId, target, before)
    if source and not source.data.parent then
      if groupId then
        local group = WeakAuras.GetDisplayButton(groupId)
        if group and group:IsGroup() then
          local children = group.data.controlledChildren
          if target then
            local index = target:GetGroupOrder()
            if ensure(children, index, target.data.id) then
              -- account for insert position
              index = before and index or index+1
              tinsert(children, index, source.data.id)
            else
              error("Calling 'Group' with invalid target. Reload your UI to fix the display list.")
            end
          else
            -- move source into group as the first child
            tinsert(children, 1, source.data.id)
          end
          source:SetGroup(groupId)
          source.data.parent = groupId
          WeakAuras.Add(source.data)
          WeakAuras.Add(group.data)
          WeakAuras.UpdateGroupOrders(group.data)
          WeakAuras.ReloadGroupRegionOptions(group.data)
          WeakAuras.UpdateDisplayButton(group.data)
          group.callbacks.UpdateExpandButton();
          group:ReloadTooltip()
        else
          error("Calling 'Group' with invalid groupId. Reload your UI to fix the display list.")
        end
      else
        -- move source into the top-level list
        WeakAuras.Add(source.data)
      end
    else
      error("Calling 'Group' with invalid source. Reload your UI to fix the display list.")
    end
  end,
  -- remove source from its group or top-level list
  ["Ungroup"] =  function(source)
    if source and source.data.parent then
      local parent = WeakAuras.GetData(source.data.parent)
      local children = parent.controlledChildren
      local index = source:GetGroupOrder()
      if ensure(children, index, source.data.id) then
        tremove(children, index)
        source:SetGroup()
        source.data.parent = nil
        WeakAuras.Add(parent);
        WeakAuras.UpdateGroupOrders(parent);
        WeakAuras.ReloadGroupRegionOptions(parent);
        WeakAuras.UpdateDisplayButton(parent);
      else
        error("Display thinks it is a member of a group which does not control it")
      end
    else
      error("Calling 'Ungroup' with invalid source. Reload your UI to fix the display list.")
    end
  end,
  -- move source inside its own group before or after target
  ["Move"] = function(source, target, before )
    if source and source.data.parent then
      local parent = WeakAuras.GetData(source.data.parent)
      local children = parent.controlledChildren
      local i = source:GetGroupOrder()
      if ensure(children, i, source.data.id) then
        if target and target.data.parent then
          local j = target:GetGroupOrder()
          if ensure(children, j, target.data.id) then
            -- account for possible reorder
            j = i < j and j-1 or j
            -- account for insert position
            j = before and j or j+1
            tremove(children, i)
            tinsert(children, j, source.data.id)
          else
            error("Calling 'Move' with invalid target. Reload your UI to fix the display list.")
          end
        else
          tremove(children, i)
          tinsert(children, 1, source.data.id)
        end
        WeakAuras.Add(parent)
        WeakAuras.UpdateGroupOrders(parent)
        WeakAuras.UpdateDisplayButton(parent)
      else
        error("Calling 'Move' with invalid source. Reload your UI to fix the display list.")
      end
    else
      error("Calling 'Move' with invalid source. Reload your UI to fix the display list.")
    end
  end,
}

local Icons = {
  ["Group"] = "Interface\\GossipFrame\\TrainerGossipIcon",
  ["Ungroup"] = "Interface\\GossipFrame\\UnlearnGossipIcon",
  ["Move"] = nil
}

local function GetAction(target, area, source)
  if target and source and (area == "TOP" or area == "BOTTOM")then
    if target.data.parent and source.data.parent then
      if source.data.parent == target.data.parent then
        return function(_source, _target)
          Actions["Move"](_source, _target, area=="TOP")
        end,
        Icons["Move"]
      else
        return function(_source, _target)
          Actions["Ungroup"](_source)
          Actions["Group"](_source, _target.data.parent, _target, area == "TOP")
        end,
        Icons["Group"]
      end
    elseif target.data.parent then -- and not source.data.parent
      return function(_source, _target)
        Actions["Group"](_source, _target.data.parent, _target, area == "TOP")
      end,
      Icons["Group"]
    elseif source.data.parent then -- and not target.data.parent
      if area == "TOP" then
        return function(_source, _target)
          Actions["Ungroup"](_source)
          Actions["Group"](_source)
        end,
        Icons["Ungroup"]
    else -- area == "BOTTOM"
      if source.data.parent == target.data.id then
        return Actions["Move"], Icons["Move"]
    else
      return function(_source, _target)
        Actions["Ungroup"](_source)
        Actions["Group"](_source, _target.data.id)
      end,
      Icons["Group"]
    end
    end
    else -- not target.data.parent and not source.data.parent
      if target:IsGroup() and area == "BOTTOM" then
        return function(_source, _target)
          Actions["Group"](_source, _target.data.id)
        end,
        Icons["Group"]
    else
      return nil
    end
    end
  end
end

-------------------------

local function GetDropTarget()
  local buttonList = WeakAuras.displayButtons
  local id, button, pos, offset
  repeat
    repeat
      id, button = next(buttonList, id)
    until not id or not button.dragging and button:IsEnabled() and button:IsShown()
    if id and button then
      offset = (button.frame.height or button.frame:GetHeight() or 16) / 2
      pos = button.frame:IsMouseOver(1,offset) and "TOP"
        or button.frame:IsMouseOver(-offset,-1) and "BOTTOM"
    end
  until not id or pos
  return id, button, pos
end

local function Show_DropIndicator(id)
  local indicator = WeakAuras.DropIndicator()
  local source = WeakAuras.GetDisplayButton(id)
  local target, pos
  if source then
    target, pos = select(2, GetDropTarget())
  end
  indicator:ClearAllPoints()
  local action, icon = GetAction(target, pos, source)
  if action then
    -- show line
    if pos == "TOP" then
      indicator:SetPoint("BOTTOMLEFT", target.frame, "TOPLEFT", 0, -1)
      indicator:SetPoint("BOTTOMRIGHT", target.frame, "TOPRIGHT", 0, -1)
      indicator:Show()
    elseif pos == "BOTTOM" then
      indicator:SetPoint("TOPLEFT", target.frame, "BOTTOMLEFT", 0, 1)
      indicator:SetPoint("TOPRIGHT", target.frame, "BOTTOMRIGHT", 0, 1)
      indicator:Show()
    else
      error("Invalid value pos '"..tostring(pos))
    end
    -- show icon
    if icon then
      if indicator.icon.texture ~= icon then
        indicator.icon.texture = icon
        indicator.icon:SetTexture(icon)
      end
      indicator.icon:Show()
    else
      indicator.icon:Hide()
    end
  else
    indicator:Hide()
  end
end

--[[-----------------------------------------------------------------------------
Methods
-------------------------------------------------------------------------------]]
local methods = {
  ["OnAcquire"] = function(self)
    self:SetWidth(1000);
    self:SetHeight(32);
  end,
  ["Initialize"] = function(self)
    local data = self.data;
    self.callbacks = {};

    function self.callbacks.OnClickNormal(_, mouseButton)
      if(IsControlKeyDown() and not data.controlledChildren) then
        WeakAuras.PickDisplayMultiple(data.id);
        self:ReloadTooltip();
      elseif(IsShiftKeyDown()) then
        local editbox = GetCurrentKeyBoardFocus();
        if(editbox) then
          if (not fullName) then
            local name, realm = UnitFullName("player")
            fullName = name.."-"..realm
          end
          editbox:Insert("[WeakAuras: "..fullName.." - "..data.id.."]");
        end
      else
        if(mouseButton == "RightButton") then
          Hide_Tooltip();
          if(WeakAuras.IsDisplayPicked(data.id) and WeakAuras.IsPickedMultiple()) then
            EasyMenu(WeakAuras.MultipleDisplayTooltipMenu(), WeakAuras_DropDownMenu, self.frame, 0, 0, "MENU");
          else
            EasyMenu(self.menu, WeakAuras_DropDownMenu, self.frame, 0, 0, "MENU");
            if not(WeakAuras.IsDisplayPicked(data.id)) then
              WeakAuras.PickDisplay(data.id);
            end
          end
        else
          WeakAuras.PickDisplay(data.id);
          self:ReloadTooltip();
        end
      end
    end

    function self.callbacks.OnClickCopying()
      if (WeakAuras.IsImporting()) then return end;
      WeakAuras.Copy(data.id, self.copying.id);
      WeakAuras.ScanForLoads();
      WeakAuras.SetIconNames(self.copying);
      WeakAuras.SortDisplayButtons();
      WeakAuras.AddOption(self.copying.id, self.copying);
      WeakAuras.OptionsFrame():PickDisplay(self.copying.id);
      WeakAuras.UpdateDisplayButton(self.copying);
      WeakAuras.SetCopying();
      self:ReloadTooltip();
    end

    function self.callbacks.OnClickCopyingSelf()
      WeakAuras.SetCopying();
      self:ReloadTooltip();
    end

    function self.callbacks.UpdateExpandButton()
      if(#self.data.controlledChildren == 0) then
        self:DisableExpand();
      else
        self:EnableExpand();
      end
    end

    function self.callbacks.OnClickGrouping()
      if (WeakAuras.IsImporting()) then return end;
      tinsert(data.controlledChildren, self.grouping.id);
      local childButton = WeakAuras.GetDisplayButton(self.grouping.id);
      childButton:SetGroup(data.id, data.regionType == "dynamicgroup");
      childButton:SetGroupOrder(#data.controlledChildren, #data.controlledChildren);
      self.callbacks.UpdateExpandButton();
      self.grouping.parent = data.id;
      WeakAuras.Add(data);
      WeakAuras.Add(self.grouping);
      WeakAuras.SetGrouping();
      WeakAuras.UpdateDisplayButton(data);
      WeakAuras.ReloadGroupRegionOptions(data);
      WeakAuras.UpdateGroupOrders(data);
      WeakAuras.SortDisplayButtons();
      self:ReloadTooltip();
    end

    function self.callbacks.OnClickGroupingSelf()
      WeakAuras.SetGrouping();
      self:ReloadTooltip();
    end

    function self.callbacks.OnCopyClick()
      WeakAuras.PickDisplay(data.id);
      WeakAuras.SetCopying(data);
    end

    function self.callbacks.OnGroupClick()
      WeakAuras.PickDisplay(data.id);
      WeakAuras.SetGrouping(data);
    end

    function self.callbacks.OnDeleteClick()
      if (WeakAuras.IsImporting()) then return end;
      local parentData = data.parent and WeakAuras.GetData(data.parent);
      local parentButton = data.parent and WeakAuras.GetDisplayButton(data.parent);
      WeakAuras.DeleteOption(data);
      if(parentData) then
        WeakAuras.UpdateGroupOrders(parentData);
      end
      if(parentButton) then
        parentButton.callbacks.UpdateExpandButton();
      end
    end

    function self.callbacks.OnDuplicateClick()
      if (WeakAuras.IsImporting()) then return end;
      local base_id = data.id .. " ";
      local num = 2;

      -- if the old id ends with a number increment the number
      local matchName, matchNumber = string.match(data.id, "^(.-)(%d*)$")
      matchNumber = tonumber(matchNumber)
      if (matchName ~= "" and matchNumber ~= nil) then
        base_id = matchName;
        num = matchNumber + 1
      end

      local new_id = base_id .. num;
      while(WeakAuras.GetData(new_id)) do
        new_id = base_id .. num;
        num = num + 1;
      end

      local newData = {};
      WeakAuras.DeepCopy(data, newData);
      newData.id = new_id;
      newData.parent = nil;
      WeakAuras.Add(newData);
      WeakAuras.NewDisplayButton(newData);
      if(data.parent) then
        local parentData = WeakAuras.GetData(data.parent);
        local index;
        for i, childId in pairs(parentData.controlledChildren) do
          if(childId == data.id) then
            index = i;
            break;
          end
        end
        if(index) then
          local newIndex = index + 1;
          if(newIndex > #parentData.controlledChildren) then
            tinsert(parentData.controlledChildren, newData.id);
          else
            tinsert(parentData.controlledChildren, index + 1, newData.id);
          end
          newData.parent = data.parent;
          WeakAuras.Add(parentData);
          WeakAuras.Add(newData);

          for index, id in pairs(parentData.controlledChildren) do
            local childButton = WeakAuras.GetDisplayButton(id);
            childButton:SetGroup(parentData.id, parentData.regionType == "dynamicgroup");
            childButton:SetGroupOrder(index, #parentData.controlledChildren);
          end

          local button = WeakAuras.GetDisplayButton(parentData.id);
          button.callbacks.UpdateExpandButton();
          WeakAuras.UpdateDisplayButton(parentData);
          WeakAuras.ReloadGroupRegionOptions(parentData);
        end
      end
      WeakAuras.SortDisplayButtons();
      WeakAuras.DoConfigUpdate();
      WeakAuras.PickAndEditDisplay(new_id);
    end

    function self.callbacks.OnDeleteAllClick()
      if (WeakAuras.IsImporting()) then return end;
      if(data.controlledChildren) then

        local region = WeakAuras.regions[data.id];
        if (region.ControlChildren) then
          region:Pause();
        end

        local toDelete = {};
        for index, id in pairs(data.controlledChildren) do
          toDelete[index] = WeakAuras.GetData(id);
        end
        for index, childData in pairs(toDelete) do
          WeakAuras.DeleteOption(childData);
        end
      end
      WeakAuras.DeleteOption(data);
    end

    function self.callbacks.OnUngroupClick()
      if (WeakAuras.IsImporting()) then return end;
      local parentData = WeakAuras.GetData(data.parent);
      local index;
      for childIndex, childId in pairs(parentData.controlledChildren) do
        if(childId == data.id) then
          index = childIndex;
          break;
        end
      end
      if(index) then
        tremove(parentData.controlledChildren, index);
        WeakAuras.Add(parentData);
        WeakAuras.ReloadGroupRegionOptions(parentData);
      else
        error("Display thinks it is a member of a group which does not control it");
      end
      self:SetGroup();
      data.parent = nil;
      WeakAuras.Add(data);
      WeakAuras.UpdateGroupOrders(parentData);
      WeakAuras.UpdateDisplayButton(parentData);
      WeakAuras.SortDisplayButtons();
    end

    function self.callbacks.OnUpGroupClick()
      if (WeakAuras.IsImporting()) then return end;
      if(data.parent) then
        local id = data.id;
        local parentData = WeakAuras.GetData(data.parent);
        local index;
        for childIndex, childId in pairs(parentData.controlledChildren) do
          if(childId == id) then
            index = childIndex;
            break;
          end
        end
        if(index) then
          if(index <= 1) then
            error("Attempt to move up the first element in a group");
          else
            tremove(parentData.controlledChildren, index);
            tinsert(parentData.controlledChildren, index - 1, id);
            WeakAuras.Add(parentData);
            self:SetGroupOrder(index - 1, #parentData.controlledChildren);
            local otherbutton = WeakAuras.GetDisplayButton(parentData.controlledChildren[index]);
            otherbutton:SetGroupOrder(index, #parentData.controlledChildren);
            WeakAuras.SortDisplayButtons();
            local updata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = -32};
            local downdata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = 32};
            WeakAuras.Animate("button", parentData.controlledChildren[index-1], "main", updata, self.frame, true, function() WeakAuras.SortDisplayButtons() end);
            WeakAuras.Animate("button", parentData.controlledChildren[index], "main", downdata, otherbutton.frame, true, function() WeakAuras.SortDisplayButtons() end);
            WeakAuras.UpdateDisplayButton(parentData);
          end
        else
          error("Display thinks it is a member of a group which does not control it");
        end
      else
        error("This display is not in a group. You should not have been able to click this button");
      end
    end

    function self.callbacks.OnDownGroupClick()
      if (WeakAuras.IsImporting()) then return end;
      if(data.parent) then
        local id = data.id;
        local parentData = WeakAuras.GetData(data.parent);
        local index;
        for childIndex, childId in pairs(parentData.controlledChildren) do
          if(childId == id) then
            index = childIndex;
            break;
          end
        end
        if(index) then
          if(index >= #parentData.controlledChildren) then
            error("Attempt to move down the last element in a group");
          else
            tremove(parentData.controlledChildren, index);
            tinsert(parentData.controlledChildren, index + 1, id);
            WeakAuras.Add(parentData);
            self:SetGroupOrder(index + 1, #parentData.controlledChildren);
            local otherbutton = WeakAuras.GetDisplayButton(parentData.controlledChildren[index]);
            otherbutton:SetGroupOrder(index, #parentData.controlledChildren);
            WeakAuras.SortDisplayButtons()
            local updata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = -32};
            local downdata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = 32};
            WeakAuras.Animate("button", parentData.controlledChildren[index+1], "main", downdata, self.frame, true, function() WeakAuras.SortDisplayButtons() end);
            WeakAuras.Animate("button", parentData.controlledChildren[index], "main", updata, otherbutton.frame, true, function() WeakAuras.SortDisplayButtons() end);
            WeakAuras.UpdateDisplayButton(parentData);
          end
        else
          error("Display thinks it is a member of a group which does not control it");
        end
      else
        error("This display is not in a group. You should not have been able to click this button");
      end
    end

    function self.callbacks.OnViewClick()
      WeakAuras.PauseAllDynamicGroups();

      if(self.view.func() == 2) then
        for index, childId in ipairs(data.controlledChildren) do
          WeakAuras.GetDisplayButton(childId):PriorityHide(2);
        end
      else
        for index, childId in ipairs(data.controlledChildren) do
          WeakAuras.GetDisplayButton(childId):PriorityShow(2);
        end
      end

      WeakAuras.ResumeAllDynamicGroups();
    end

    function self.callbacks.ViewTest()
      local none, all = true, true;
      for index, childId in ipairs(data.controlledChildren) do
        local childButton = WeakAuras.GetDisplayButton(childId);
        if(childButton) then
          if(childButton:GetVisibility() ~= 2) then
            all = false;
          end
          if(childButton:GetVisibility() ~= 0) then
            none = false;
          end
        end
      end
      if(all) then
        return 2;
      elseif(none) then
        return 0;
      else
        return 1;
      end
    end

    function self.callbacks.OnRenameClick()
      if (WeakAuras.IsImporting()) then return end;
      if(self.title:IsVisible()) then
        self.title:Hide();
        self.renamebox:SetText(self.title:GetText());
        self.renamebox:Show();
      else
        self.title:Show();
        self.renamebox:Hide();
      end
    end

    function self.callbacks.OnRenameAction(newid)
      if (WeakAuras.IsImporting()) then return end;
      local oldid = data.id;
      if not(newid == oldid) then
        local temp;

        WeakAuras.Rename(data, newid);

        WeakAuras.thumbnails[newid] = WeakAuras.thumbnails[oldid];
        WeakAuras.thumbnails[oldid] = nil;
        WeakAuras.displayButtons[newid] = WeakAuras.displayButtons[oldid];
        WeakAuras.displayButtons[oldid] = nil;
        WeakAuras.displayOptions[oldid] = nil;
        WeakAuras.AddOption(newid, data);

        WeakAuras.displayButtons[newid]:SetTitle(newid);

        if(data.controlledChildren) then
          for index, childId in pairs(data.controlledChildren) do
            WeakAuras.displayButtons[childId]:SetGroup(newid);
          end
        end

        WeakAuras.SetCopying();
        WeakAuras.SetGrouping();
        WeakAuras.SortDisplayButtons();
        WeakAuras.PickDisplay(newid);
      end
    end

    function self.callbacks.OnDragStart()
      if WeakAuras.IsImporting() or self:IsGroup() then return end;
      WeakAuras.PickDisplay(data.id)
      WeakAuras.SetDragging(data)
    end

    function self.callbacks.OnDragStop()
      if not self.dragging then return end
      WeakAuras.SetDragging(data, true)
    end

    function self.callbacks.OnKeyDown(self, key)
      if (key == "ESCAPE") then
        WeakAuras.SetDragging();
      end
    end

    self.frame.terribleCodeOrganizationHackTable = {};

    function self.frame.terribleCodeOrganizationHackTable.IsGroupingOrCopying()
      return self.grouping or self.copying;
    end

    function self.frame.terribleCodeOrganizationHackTable.SetNormalTooltip()
      self:SetNormalTooltip();
    end

    function self.frame.terribleCodeOrganizationHackTable.OnShow()
      WeakAuras.UpdateCloneConfig(data);
    end

    function self.frame.terribleCodeOrganizationHackTable.OnHide()
      WeakAuras.CollapseAllClones(data.id);
    end

    self:SetTitle(data.id);
    self.menu = {
      {
        text = L["Rename"],
        notCheckable = 1,
        func = self.callbacks.OnRenameClick
      },
      {
        text = L["Copy settings from..."],
        notCheckable = 1,
        func = self.callbacks.OnCopyClick
      },
    };
    if (not data.controlledChildren) then
      local convertMenu = {};
      for regionType, regionData in pairs(WeakAuras.regionOptions) do
        if(regionType ~= "group" and regionType ~= "dynamicgroup" and regionType ~= "timer" and regionType ~= data.regionType) then
          tinsert(convertMenu, {
            text = regionData.displayName,
            notCheckable = 1,
            func = function()
              WeakAuras.ConvertDisplay(data, regionType);
              WeakAuras_DropDownMenu:Hide();
            end
          });
        end
      end
      tinsert(self.menu, {
        text = L["Convert to..."],
        notCheckable = 1,
        hasArrow = true,
        menuList = convertMenu
      });
      tinsert(self.menu, {
        text = L["Duplicate"],
        notCheckable = 1,
        func = self.callbacks.OnDuplicateClick
      });
    end

    tinsert(self.menu, {
      text = L["Set tooltip description"],
      notCheckable = 1,
      func = function() WeakAuras.ShowDisplayTooltip(data, nil, nil, nil, nil, nil, "desc") end
    });


    if (data.url and data.url ~= "") then
      tinsert(self.menu, {
        text = L["Copy URL"],
        notCheckable = 1,
        func = function() WeakAuras.ShowDisplayTooltip(data, nil, nil, nil, nil, nil, "url") end
      });
    end

    tinsert(self.menu, {
      text = L["Export to string..."],
      notCheckable = 1,
      func = function() WeakAuras.ExportToString(data.id) end
    });
    tinsert(self.menu, {
      text = L["Export to Lua table..."],
      notCheckable = 1,
      func = function() WeakAuras.ExportToTable(data.id) end
    });
    tinsert(self.menu, {
      text = " ",
      notClickable = 1,
      notCheckable = 1,
    });
    tinsert(self.menu, {
      text = L["Delete"],
      notCheckable = 1,
      func = self.callbacks.OnDeleteClick
    });

    if (data.controlledChildren) then
      tinsert(self.menu, {
        text = L["Delete children and group"],
        notCheckable = 1,
        func = self.callbacks.OnDeleteAllClick
      });
    end
    tinsert(self.menu, {
      text = " ",
      notClickable = 1,
      notCheckable = 1,
    });
    tinsert(self.menu, {
      text = L["Close"],
      notCheckable = 1,
      func = function() WeakAuras_DropDownMenu:Hide() end
    });
    if(data.controlledChildren) then
      self:SetViewClick(self.callbacks.OnViewClick);
      self:SetViewTest(self.callbacks.ViewTest);
      self:DisableGroup();
      self.callbacks.UpdateExpandButton();
      self:SetOnExpandCollapse(function() WeakAuras.SortDisplayButtons(nil, true) end);
    else
      self:SetViewRegion(WeakAuras.regions[data.id].region);
      self:EnableGroup();
    end
    self:SetNormalTooltip();
    self.frame:SetScript("OnClick", self.callbacks.OnClickNormal);
    self.frame:SetScript("OnKeyDown", self.callbacks.OnKeyDown);
    self.frame:EnableKeyboard(false);
    self.frame:SetMovable(true);
    self.frame:RegisterForDrag("LeftButton");
    self.frame:SetScript("OnDragStart", self.callbacks.OnDragStart);
    self.frame:SetScript("OnDragStop", self.callbacks.OnDragStop);

    self:Enable();
    self:SetRenameAction(self.callbacks.OnRenameAction);
    self.group:SetScript("OnClick", self.callbacks.OnGroupClick);
    self.ungroup:SetScript("OnClick", self.callbacks.OnUngroupClick);
    self.upgroup:SetScript("OnClick", self.callbacks.OnUpGroupClick);
    self.downgroup:SetScript("OnClick", self.callbacks.OnDownGroupClick);
    if(data.parent) then
      local parentData = WeakAuras.GetData(data.parent);
      local index;
      for childIndex, childId in pairs(parentData.controlledChildren) do
        if(childId == data.id) then
          index = childIndex;
          break;
        end
      end
      if(index) then
        self:SetGroup(data.parent);
        self:SetGroupOrder(index, #parentData.controlledChildren);
      else
        error("Display \""..data.id.."\" thinks it is a member of group \""..data.parent.."\" which does not control it");
      end
    end
  end,
  ["SetNormalTooltip"] = function(self)
    local data = self.data;
    local namestable = {};
    if(data.controlledChildren) then
      for index, childId in pairs(data.controlledChildren) do
        tinsert(namestable, {" ", childId});
      end
      if(#namestable > 0) then
        namestable[1][1] = L["Children:"];
      else
        namestable[1] = L["No Children"];
      end
    else
      for triggernum = 0, data.numTriggers or 9 do
        local trigger;
        if(triggernum == 0) then
          trigger = data.trigger;
        elseif(data.additional_triggers and data.additional_triggers[triggernum]) then
          trigger = data.additional_triggers[triggernum].trigger;
        end
        if(trigger) then
          if(trigger.type == "aura") then
            if(trigger.fullscan) then
              tinsert(namestable, {L["Aura:"], L["Full Scan"]});
            else
              for index, name in pairs(trigger.names) do
                local left = " ";
                if(index == 1) then
                  if(#trigger.names > 0) then
                    if(#trigger.names > 1) then
                      left = L["Auras:"];
                    else
                      left = L["Aura:"];
                    end
                  end
                end
                local icon = WeakAuras.spellCache.GetIcon(name) or "Interface\\Icons\\INV_Misc_QuestionMark";
                tinsert(namestable, {left, name, icon});
              end
            end
          elseif(trigger.type == "event" or trigger.type == "status") then
            if(trigger.type == "event") then
              tinsert(namestable, {L["Trigger:"], (WeakAuras.event_types[trigger.event] or L["Undefined"])});
            else
              tinsert(namestable, {L["Trigger:"], (WeakAuras.status_types[trigger.event] or L["Undefined"])});
            end
            if(trigger.event == "Combat Log" and trigger.subeventPrefix and trigger.subeventSuffix) then
              tinsert(namestable, {L["Message type:"], (WeakAuras.subevent_prefix_types[trigger.subeventPrefix] or L["Undefined"]).." "..(WeakAuras.subevent_suffix_types[trigger.subeventSuffix] or L["Undefined"])});
            end
          else
            tinsert(namestable, {L["Trigger:"], L["Custom"]});
          end
        end
      end
    end
    if(WeakAuras.CanHaveClones(data)) then
      tinsert(namestable, {" ", "|cFF00FF00"..L["Auto-cloning enabled"]})
    end
    if(WeakAuras.IsDefinedByAddon(data.id)) then
      tinsert(namestable, " ");
      tinsert(namestable, {" ", "|cFF00FFFF"..L["Addon"]..": "..WeakAuras.IsDefinedByAddon(data.id)});
    end

    local hasDescription = data.desc and data.desc ~= "";
    local hasUrl = data.url and data.url ~= "";

    if(hasDescription or hasUrl) then
      tinsert(namestable, " ");
    end

    if(hasDescription) then
      tinsert(namestable, "|cFFFFD100\""..data.desc.."\"");
    end

    if (hasUrl) then
      tinsert(namestable, "|cFFFFD100".. data.url);
    end

    tinsert(namestable, " ");
    tinsert(namestable, {" ", "|cFF00FFFF"..L["Right-click for more options"]});
    if not(data.controlledChildren) then
      tinsert(namestable, {" ", "|cFF00FFFF"..L["Drag to move"]});
      tinsert(namestable, {" ", "|cFF00FFFF"..L["Control-click to select multiple displays"]});
    end
    tinsert(namestable, {" ", "|cFF00FFFF"..L["Shift-click to create chat link"]});
    local regionData = WeakAuras.regionOptions[data.regionType or ""]
    local displayName = regionData and regionData.displayName or "";
    self:SetDescription({data.id, displayName}, unpack(namestable));
  end,
  ["ReloadTooltip"] = function(self)if(
    WeakAuras.IsPickedMultiple() and WeakAuras.IsDisplayPicked(self.data.id)) then
    Show_Long_Tooltip(self.frame, WeakAuras.MultipleDisplayTooltipDesc());
  else
    Show_Long_Tooltip(self.frame, self.frame.description);
  end
  end,
  ["SetCopying"] = function(self, copyingData)
    self.copying = copyingData;
    if(self.copying) then
      if(self.data.id == self.copying.id) then
        self:SetDescription(L["Cancel"], L["Do not copy any settings"]);
        self.frame:SetScript("OnClick", self.callbacks.OnClickCopyingSelf);
      else
        if(self.data.regionType == self.copying.regionType) then
          self:SetDescription(self.data.id, L["Copy settings from %s"]:format(self.data.id));
          self.frame:SetScript("OnClick", self.callbacks.OnClickCopying);
        else
          self:Disable();
        end
      end
    else
      self:SetNormalTooltip();
      self.frame:SetScript("OnClick", self.callbacks.OnClickNormal);
      self:Enable();
    end
  end,
  ["SetGrouping"] = function(self, groupingData)
    self.grouping = groupingData;
    if(self.grouping) then
      if(self.data.id == self.grouping.id) then
        self.frame:SetScript("OnClick", self.callbacks.OnClickGroupingSelf);
        self:SetDescription(L["Cancel"], L["Do not group this display"]);
      else
        if(self.data.regionType == "group" or self.data.regionType == "dynamicgroup") then
          self.frame:SetScript("OnClick", self.callbacks.OnClickGrouping);
          self:SetDescription(self.data.id, L["Add to group %s"]:format(self.data.id));
        else
          self:Disable();
        end
      end
    else
      self:SetNormalTooltip();
      self.frame:SetScript("OnClick", self.callbacks.OnClickNormal);
      self:Enable();
    end
  end,
  ["SetDragging"] = function(self, data, drop)
    if data then
      -- self
      if self.data.id == data.id then
        if drop then
          self:Drop()
          self.frame:SetScript("OnClick", self.callbacks.OnClickNormal)
          self.frame:EnableKeyboard(false); -- disables self.callbacks.OnKeyDown
        else
          Hide_Tooltip()
          self.frame:SetScript("OnClick", nil)
          self.frame:EnableKeyboard(true); -- enables self.callbacks.OnKeyDown
          self:Drag()
        end
        -- invalid targets
      elseif not self.data.parent and not self:IsGroup()
      then
        if drop then
          self:Enable()
        else
          self:Disable()
        end
        -- valid target
      else
        if drop then
          self.frame:SetScript("OnClick", self.callbacks.OnClickNormal)
        else
          self.frame:SetScript("OnClick", nil)
        end
      end
    else
      -- restore events and layout
      self.frame:SetScript("OnClick", self.callbacks.OnClickNormal)
      self.frame:EnableKeyboard(false);
      self:Enable()
      if (self.dragging) then
        self:Drop(true)
      end
    end
  end,
  ["ShowTooltip"] = function(self)
  end,
  ["Drag"] = function(self)
    local uiscale, _, y = UIParent:GetScale(), GetCursorPosition()
    local scale, x, w = self.frame:GetEffectiveScale(), self.frame:GetLeft(), self.frame:GetWidth()
    -- hide "visual clutter"
    self.downgroup:Hide()
    self.group:Hide()
    self.loaded:Hide()
    self.ungroup:Hide()
    self.upgroup:Hide()
    self.view:Hide()
    -- mark as being dragged, attach to mouse and raise frame strata
    self.dragging = true
    self.frame:StartMoving()
    self.frame:ClearAllPoints()
    self.frame.temp = {
      parent = self.frame:GetParent(),
      strata = self.frame:GetFrameStrata(),
    }
    self.frame:SetParent(UIParent)
    self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
    self.frame:SetPoint("Center", UIParent, "BOTTOMLEFT", (x+w/2)*scale/uiscale, y/uiscale)
    -- attach OnUpdate event to update drop indicator
    local id = self.data.id
    self.frame:SetScript("OnUpdate", function(self,elapsed)
      self.elapsed = (self.elapsed or 0) + elapsed
      if self.elapsed > 0.1 then
        Show_DropIndicator(id)
        self.elapsed = 0
      end
    end)
    Show_DropIndicator(id)
    WeakAuras.UpdateButtonsScroll()
  end,
  ["Drop"] = function(self, reset)
    self.frame:StopMovingOrSizing()
    self.frame:SetScript("OnUpdate", nil)
    if self.dragging then
      self.frame:SetParent(self.frame.temp.parent)
      self.frame:SetFrameStrata(self.frame.temp.strata)
      self.frame.tmp = nil
      if self.data.parent then
        self.downgroup:Show()
        self.ungroup:Show()
        self.upgroup:Show()
      else
        self.group:Show()
      end
      self.loaded:Show()
      self.view:Show()
    end
    Show_DropIndicator()
    local target, area = select(2, GetDropTarget())
    self.dragging = false
    -- exit if we have no target or only want to reset
    if reset or not target then return WeakAuras.UpdateButtonsScroll() end
    -- get action and execute it
    local action = GetAction(target, area, self)
    if action then
      action(self,target)
    end
    WeakAuras.SortDisplayButtons()
  end,
  ["GetGroupOrCopying"] = function(self)
    return self.group or self.copying;
  end,
  ["SetTitle"] = function(self, title)
    self.titletext = title;
    self.title:SetText(title);
  end,
  ["GetTitle"] = function(self)
    return self.titletext;
  end,
  ["SetDescription"] = function(self, ...)
    self.frame.description = {...};
  end,
  ["SetIcon"] = function(self, icon)
    if(type(icon) == "string" or type(icon) == "number") then
      self.icon:SetTexture(icon);
      self.icon:Show();
      if(self.iconRegion and self.iconRegion.Hide) then
        self.iconRegion:Hide();
      end
    else
      self.iconRegion = icon;
      icon:SetAllPoints(self.icon);
      icon:SetParent(self.frame);
      self.icon:Hide();
    end
  end,
  ["SetViewRegion"] = function(self, region)
    self.view.region = region;
    self.view.func = function() return self.view.visibility end;
    self.view:SetScript("OnClick", function()
      if(self.view.visibility < 2) then
        self.view:PriorityShow(2);
      else
        self.view:PriorityHide(2);
      end
    end);
  end,
  ["SetViewClick"] = function(self, func)
    self.view:SetScript("OnClick", func);
  end,
  ["SetViewTest"] = function(self, func)
    self.view.func = func;
  end,
  ["SetRenameAction"] = function(self, func)
    self.renamebox.func = function()
      func(self.renamebox:GetText());
    end
  end,
  ["DisableGroup"] = function(self)
    self.group:Hide();
    self.loaded:Hide();
    self.expand:Show();
  end,
  ["EnableGroup"] = function(self)
    self.group:Show();
    self.loaded:Show();
    self.expand:Hide();
  end,
  ["SetIds"] = function(self, ids)
    self.renamebox.ids = ids;
  end,
  ["SetGroup"] = function(self, group)
    self.frame.dgroup = group;
    if(group) then
      self.icon:SetPoint("LEFT", self.ungroup, "RIGHT");
      self.background:SetPoint("LEFT", self.ungroup, "RIGHT");
      self.ungroup:Show();
      self.group:Hide();
      self.upgroup:Show();
      self.downgroup:Show();
    else
      self.icon:SetPoint("LEFT", self.frame, "LEFT");
      self.background:SetPoint("LEFT", self.frame, "LEFT");
      self.ungroup:Hide();
      self.group:Show();
      self.upgroup:Hide();
      self.downgroup:Hide();
    end
  end,
  ["GetGroup"] = function(self)
    return self.frame.dgroup;
  end,
  ["IsGroup"] = function(self)
    return self.data.regionType == "group" or self.data.regionType == "dynamicgroup"
  end,
  ["SetData"] = function(self, data)
    self.data = data;
    self.frame.id = data.id;
  end,
  ["GetData"] = function(self)
    return self.data;
  end,
  ["Expand"] = function(self, reloadTooltip)
    self.expand:Enable();
    self.data.expanded = true;
    self.expand:SetNormalTexture("Interface\\BUTTONS\\UI-MinusButton-Up.blp");
    self.expand:SetPushedTexture("Interface\\BUTTONS\\UI-MinusButton-Down.blp");
    self.expand.title = L["Collapse"];
    self.expand.desc = L["Hide this group's children"];
    self.expand:SetScript("OnClick", function() self:Collapse(true) end);
    self.expand.func();
    if(reloadTooltip) then
      Hide_Tooltip();
      Show_Tooltip(self.frame, self.expand.title, self.expand.desc);
    end
  end,
  ["Collapse"] = function(self, reloadTooltip)
    self.expand:Enable();
    self.data.expanded = false;
    self.expand:SetNormalTexture("Interface\\BUTTONS\\UI-PlusButton-Up.blp");
    self.expand:SetPushedTexture("Interface\\BUTTONS\\UI-PlusButton-Down.blp");
    self.expand.title = L["Expand"];
    self.expand.desc = L["Show this group's children"];
    self.expand:SetScript("OnClick", function() self:Expand(true) end);
    self.expand.func();
    if(reloadTooltip) then
      Hide_Tooltip();
      Show_Tooltip(self.frame, self.expand.title, self.expand.desc);
    end
  end,
  ["SetOnExpandCollapse"] = function(self, func)
    self.expand.func = func;
  end,
  ["GetExpanded"] = function(self)
    return self.data.expanded;
  end,
  ["DisableExpand"] = function(self)
    self.expand:Disable();
    self.expand.disabled = true;
    self.expand.expanded = false;
    self.expand:SetNormalTexture("Interface\\BUTTONS\\UI-PlusButton-Disabled.blp");
  end,
  ["EnableExpand"] = function(self)
    self.expand.disabled = false;
    if(self:GetExpanded()) then
      self:Expand();
    else
      self:Collapse();
    end
  end,
  ["SetGroupOrder"] = function(self, order, max)
    if(order == 1) then
      self:DisableUpGroup();
    else
      self:EnableUpGroup();
    end
    if(order == max) then
      self:DisableDownGroup();
    else
      self:EnableDownGroup();
    end
    self.frame.dgrouporder = order;
  end,
  ["GetGroupOrder"] = function(self)
    return self.frame.dgrouporder;
  end,
  ["DisableUpGroup"] = function(self)
    self.upgroup:Disable();
    self.upgroup.texture:SetVertexColor(0.3, 0.3, 0.3);
  end,
  ["EnableUpGroup"] = function(self)
    self.upgroup:Enable();
    self.upgroup.texture:SetVertexColor(1, 1, 1);
  end,
  ["DisableDownGroup"] = function(self)
    self.downgroup:Disable();
    self.downgroup.texture:SetVertexColor(0.3, 0.3, 0.3);
  end,
  ["EnableDownGroup"] = function(self)
    self.downgroup:Enable();
    self.downgroup.texture:SetVertexColor(1, 1, 1);
  end,
  ["DisableLoaded"] = function(self)
    self.loaded.title = "Not Loaded";
    self.loaded.desc = L["This display is not currently loaded"];
    self.loaded:SetNormalTexture("Interface\\BUTTONS\\UI-GuildButton-OfficerNote-Disabled.blp");
  end,
  ["EnableLoaded"] = function(self)
    self.loaded.title = "Loaded";
    self.loaded.desc = L["This display is currently loaded"];
    self.loaded:SetNormalTexture("Interface\\BUTTONS\\UI-GuildButton-OfficerNote-Up.blp");
  end,
  ["Pick"] = function(self)
    self.frame:LockHighlight();
    self.view:PriorityShow(1);
  end,
  ["ClearPick"] = function(self)
    self.frame:UnlockHighlight();
    self.view:PriorityHide(1);
  end,
  ["PriorityShow"] = function(self, priority)
    self.view:PriorityShow(priority);
  end,
  ["PriorityHide"] = function(self, priority)
    self.view:PriorityHide(priority);
  end,
  ["GetVisibility"] = function(self)
    return self.view.visibility;
  end,
  ["Disable"] = function(self)
    self.background:Hide();
    self.frame:Disable();
    self.view:Disable();
    self.group:Disable();
    self.ungroup:Disable();
    self.upgroup:Disable();
    self.downgroup:Disable();
    self.loaded:Disable();
    self.expand:Disable();
  end,
  ["Enable"] = function(self)
    self.background:Show();
    self.frame:Enable();
    self.view:Enable();
    self.group:Enable();
    self.ungroup:Enable();
    self.upgroup:Enable();
    self.downgroup:Enable();
    self.loaded:Enable();
    if not(self.expand.disabled) then
      self.expand:Enable();
    end
  end,
  ["IsEnabled"] = function(self)
    return self.frame:IsEnabled();
  end,
  ["OnRelease"] = function(self)
    self:SetViewRegion();
    self:Enable();
    self:SetGroup();
    self:EnableGroup();
    self.renamebox:Hide();
    self.title:Show();
    local id = self.data.id;
    self.frame:SetScript("OnEnter", nil);
    self.frame:SetScript("OnLeave", nil);
    self.frame:SetScript("OnClick", nil);
    self.frame:SetScript("OnDragStart", nil);
    self.frame:SetScript("OnDragStop", nil);
    --self.frame:EnableMouse(false);
    self.frame:ClearAllPoints();
    self.frame:Hide();
    self.frame = nil;
    self.data = nil;
  end
}

--[[-----------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------]]

local function Constructor()
  local name = "WeakAurasDisplayButton"..AceGUI:GetNextWidgetNum(Type);
  local button = CreateFrame("BUTTON", name, UIParent, "OptionsListButtonTemplate");
  button:SetHeight(32);
  button:SetWidth(1000);
  button.dgroup = nil;
  button.data = {};

  local background = button:CreateTexture(nil, "BACKGROUND");
  button.background = background;
  background:SetTexture("Interface\\BUTTONS\\UI-Listbox-Highlight2.blp");
  background:SetBlendMode("ADD");
  background:SetVertexColor(0.5, 0.5, 0.5, 0.25);
  background:SetPoint("TOP", button, "TOP");
  background:SetPoint("BOTTOM", button, "BOTTOM");
  background:SetPoint("LEFT", button, "LEFT");
  background:SetPoint("RIGHT", button, "RIGHT");

  local icon = button:CreateTexture(nil, "OVERLAY");
  button.icon = icon;
  icon:SetWidth(32);
  icon:SetHeight(32);
  icon:SetPoint("LEFT", button, "LEFT");

  local title = button:CreateFontString(nil, "OVERLAY", "GameFontNormal");
  button.title = title;
  title:SetHeight(14);
  title:SetJustifyH("LEFT");
  title:SetPoint("TOP", button, "TOP", 0, -2);
  title:SetPoint("LEFT", icon, "RIGHT", 2, 0);
  title:SetPoint("RIGHT", button, "RIGHT");

  button.description = {};

  button:SetScript("OnEnter", function()
    if(WeakAuras.IsPickedMultiple() and WeakAuras.IsDisplayPicked(button.id)) then
      Show_Long_Tooltip(button, WeakAuras.MultipleDisplayTooltipDesc());
    else
      if not(button.terribleCodeOrganizationHackTable.IsGroupingOrCopying()) then
        button.terribleCodeOrganizationHackTable.SetNormalTooltip();
      end
      Show_Long_Tooltip(button, button.description);
    end
  end);
  button:SetScript("OnLeave", Hide_Tooltip);

  local view = CreateFrame("BUTTON", nil, button);
  button.view = view;
  view:SetWidth(16);
  view:SetHeight(16);
  view:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 0);
  local viewTexture = view:CreateTexture()
  view.texture = viewTexture;
  viewTexture:SetTexture("Interface\\LFGFrame\\BattlenetWorking1.blp");
  viewTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9);
  viewTexture:SetAllPoints(view);
  view:SetNormalTexture(viewTexture);
  view:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  view:SetScript("OnEnter", function() Show_Tooltip(button, L["View"], L["Toggle the visibility of this display"]) end);
  view:SetScript("OnLeave", Hide_Tooltip);
  view.visibility = 0;
  view.PriorityShow = function(self, priority)
    if(priority >= self.visibility) then
      self.visibility = priority;
      if(self.region and self.region.Expand) then
        button.terribleCodeOrganizationHackTable.OnShow();
        self.region:Expand();
        if (WeakAuras.personalRessourceDisplayFrame) then
          WeakAuras.personalRessourceDisplayFrame:expand(self.region.id);
        end
        if (WeakAuras.mouseFrame) then
          WeakAuras.mouseFrame:expand(self.region.id);
        end
      end
    end
  end
  view.PriorityHide = function(self, priority)
    if(priority >= self.visibility) then
      self.visibility = 0;
      if(self.region and self.region.Collapse) then
        button.terribleCodeOrganizationHackTable.OnHide();
        self.region:Collapse();
        if (WeakAuras.personalRessourceDisplayFrame) then
          WeakAuras.personalRessourceDisplayFrame:collapse(self.region.id);
        end
        if (WeakAuras.mouseFrame) then
          WeakAuras.mouseFrame:collapse(self.region.id);
        end
      end
    end
  end
  view.func = function() return view.visibility end;
  view:SetScript("OnUpdate", function()
    if(view.func() == 2) then
      view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking0.blp");
    elseif(view.func() == 1) then
      view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking2.blp");
    else
      view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp");
    end
  end);

  local loaded = CreateFrame("BUTTON", nil, button);
  button.loaded = loaded;
  loaded:SetWidth(16);
  loaded:SetHeight(16);
  loaded:SetPoint("BOTTOM", button, "BOTTOM");
  loaded:SetPoint("LEFT", icon, "RIGHT", 0, 0);
  loaded:SetNormalTexture("Interface\\BUTTONS\\UI-GuildButton-OfficerNote-Up.blp");
  loaded:SetDisabledTexture("Interface\\BUTTONS\\UI-GuildButton-OfficerNote-Disabled.blp");
  --loaded:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  loaded.title = L["Loaded"];
  loaded.desc = L["This display is currently loaded"];
  loaded:SetScript("OnEnter", function() Show_Tooltip(button, loaded.title, loaded.desc) end);
  loaded:SetScript("OnLeave", Hide_Tooltip);

  local renamebox = CreateFrame("EDITBOX", nil, button, "InputBoxTemplate");
  renamebox:SetHeight(14);
  renamebox:SetPoint("TOP", button, "TOP");
  renamebox:SetPoint("LEFT", icon, "RIGHT", 6, 0);
  renamebox:SetPoint("RIGHT", button, "RIGHT", -4, 0);
  renamebox:SetFont("Fonts\\FRIZQT__.TTF", 10);
  renamebox:Hide();

  renamebox.func = function() --[[By default, do nothing!]] end;
  renamebox:SetScript("OnEnterPressed", function()
    local oldid = button.title:GetText();
    local newid = renamebox:GetText();
    if(newid == "" or (newid ~= oldid and WeakAuras.GetData(newid))) then
      renamebox:SetText(button.title:GetText());
    else
      renamebox.func();
      title:SetText(renamebox:GetText());
      title:Show();
      renamebox:Hide();
    end
  end);

  renamebox:SetScript("OnEscapePressed", function()
    title:Show();
    renamebox:Hide();
  end);

  local group = CreateFrame("BUTTON", nil, button);
  button.group = group;
  group:SetWidth(16);
  group:SetHeight(16);
  group:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -18, 0);
  local grouptexture = group:CreateTexture(nil, "OVERLAY");
  group.texture = grouptexture;
  grouptexture:SetTexture("Interface\\GLUES\\CharacterCreate\\UI-RotationRight-Big-Up.blp");
  grouptexture:SetTexCoord(0.15, 0.85, 0.15, 0.85);
  grouptexture:SetAllPoints(group);
  group:SetNormalTexture(grouptexture);
  group:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  group:SetScript("OnEnter", function() Show_Tooltip(button, L["Group (verb)"], L["Put this display in a group"]) end);
  group:SetScript("OnLeave", Hide_Tooltip);

  local ungroup = CreateFrame("BUTTON", nil, button);
  button.ungroup = ungroup;
  ungroup:SetWidth(11);
  ungroup:SetHeight(11);
  ungroup:SetPoint("LEFT", button, "LEFT", 0, 0);
  local ungrouptexture = group:CreateTexture(nil, "OVERLAY");
  ungrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp");
  ungrouptexture:SetTexCoord(0.5, 0, 0.5, 1, 1, 0, 1, 1);
  ungrouptexture:SetAllPoints(ungroup);
  ungroup:SetNormalTexture(ungrouptexture);
  ungroup:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  ungroup:SetScript("OnEnter", function() Show_Tooltip(button, L["Ungroup"], L["Remove this display from its group"]) end);
  ungroup:SetScript("OnLeave", Hide_Tooltip);
  ungroup:Hide();

  local upgroup = CreateFrame("BUTTON", nil, button);
  button.upgroup = upgroup;
  upgroup:SetWidth(11);
  upgroup:SetHeight(11);
  upgroup:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0);
  local upgrouptexture = group:CreateTexture(nil, "OVERLAY");
  upgroup.texture = upgrouptexture;
  upgrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp");
  upgrouptexture:SetTexCoord(0.5, 1, 1, 1, 0.5, 0, 1, 0);
  upgrouptexture:SetVertexColor(1, 1, 1);
  upgrouptexture:SetAllPoints(upgroup);
  upgroup:SetNormalTexture(upgrouptexture);
  upgroup:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  upgroup:SetScript("OnEnter", function() Show_Tooltip(button, L["Move Up"], L["Move this display up in its group's order"]) end);
  upgroup:SetScript("OnLeave", Hide_Tooltip);
  upgroup:Hide();

  local downgroup = CreateFrame("BUTTON", nil, button);
  button.downgroup = downgroup;
  downgroup:SetWidth(11);
  downgroup:SetHeight(11);
  downgroup:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", 0, 0);
  local downgrouptexture = group:CreateTexture(nil, "OVERLAY");
  downgroup.texture = downgrouptexture;
  downgrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp");
  downgrouptexture:SetTexCoord(1, 0, 0.5, 0, 1, 1, 0.5, 1);
  downgrouptexture:SetAllPoints(downgroup);
  downgroup:SetNormalTexture(downgrouptexture);
  downgroup:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  downgroup:SetScript("OnEnter", function() Show_Tooltip(button, L["Move Down"], L["Move this display down in its group's order"]) end);
  downgroup:SetScript("OnLeave", Hide_Tooltip);
  downgroup:Hide();

  local expand = CreateFrame("BUTTON", nil, button);
  button.expand = expand;
  expand.expanded = true;
  expand.disabled = true;
  expand.func = function() end;
  expand:SetNormalTexture("Interface\\BUTTONS\\UI-PlusButton-Disabled.blp");
  expand:Disable();
  expand:SetWidth(16);
  expand:SetHeight(16);
  expand:SetPoint("BOTTOM", button, "BOTTOM");
  expand:SetPoint("LEFT", icon, "RIGHT", 0, 0);
  expand:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp");
  expand.title = L["Disabled"];
  expand.desc = L["Expansion is disabled because this group has no children"];
  expand:SetScript("OnEnter", function() Show_Tooltip(button, expand.title, expand.desc) end);
  expand:SetScript("OnLeave", Hide_Tooltip);

  local widget = {
    frame = button,
    title = title,
    icon = icon,
    --delete = delete, -- There is no variable called delete?
    --copy = copy, -- There is no variable called copy?
    view = view,
    --rename = rename, -- There is no variable called rename?
    renamebox = renamebox,
    --descbox = descbox, -- There is no variable called descbox?
    group = group,
    ungroup = ungroup,
    upgroup = upgroup,
    downgroup = downgroup,
    loaded = loaded,
    background = background,
    expand = expand,
    type = Type
  }
  for method, func in pairs(methods) do
    widget[method] = func
  end

  return AceGUI:RegisterAsWidget(widget);
end

AceGUI:RegisterWidgetType(Type, Constructor, Version)
