Update 2.3.0 form Kyoma for Thieves Guild

René Welbers [02-22-16 - 14:27]
Update 2.3.0 form Kyoma for Thieves Guild
Filename
AutoInvite.lua
AutoInvite.txt
AutoInviteUI.lua
lua/queue.lua
ui/ai_enabled_fragment.lua
ui/ai_options_fragment.lua
ui/autoinvitescene.lua
ui/half_grouplist.lua
ui/half_grouplist.xml
diff --git a/AutoInvite.lua b/AutoInvite.lua
index 89ba77b..57ec669 100644
--- a/AutoInvite.lua
+++ b/AutoInvite.lua
@@ -86,6 +86,7 @@ AutoInvite.offlineEvent = function(_, unitTag, connectStatus)
         dbg(unitTag .. "/" .. unitName .. " has disconnected")
         AutoInvite.kickTable[unitName] = GetTimeStamp()
     end
+    MINI_GROUP_LIST:updateSingle(name)
 end

 -- tick function: called every 1s
diff --git a/AutoInvite.txt b/AutoInvite.txt
index ac1f710..59db793 100644
--- a/AutoInvite.txt
+++ b/AutoInvite.txt
@@ -1,6 +1,6 @@
-## APIVersion: 100013
+## APIVersion: 100014
 ## Title: AutoInvite
-## Version: 2.2.1
+## Version: 2.3.0
 ## Author: Sasky, Kyoma & |c4779cesilentgecko|r
 ## SavedVariables: AutoInviteSettings
 ## OptionalDependsOn: LibAddonMenu-2.0
@@ -30,7 +30,11 @@ lua/kick.lua
 lua/queue.lua

 AutoInviteUI.lua
+ui/half_grouplist.lua
+ui/half_grouplist.xml
+ui/ai_enabled_fragment.lua
 ui/ai_options_fragment.lua
+ui/autoinvitescene.lua
 ui/bindings.xml

 AutoInvite.lua
diff --git a/AutoInviteUI.lua b/AutoInviteUI.lua
index ff46bda..d487565 100644
--- a/AutoInviteUI.lua
+++ b/AutoInviteUI.lua
@@ -28,26 +28,10 @@ function AutoInviteUI.refresh()
     ui.fragmentOptions.max:UpdateValue()
 end

-function AutoInviteUI:AddGroupMenu()
-    local categories = WINDOW_MANAGER:GetControlByName("ZO_GroupMenu_KeyboardCategories" , "")
-    local divider = CreateControlFromVirtual("ZO_GroupMenu_KeyboardAUIDivider", ZO_GroupMenu_Keyboard, "ZO_DynamicHorizontalDivider")
-    divider:SetAnchor(TOPLEFT, categories, TOPRIGHT, -275, 114)
-    divider:SetDimensions(270,10)
-    divider:SetHidden(false)
-
-    local data = {
-        name = GetString(SI_AUTO_INVITE),
-        categoryFragment = AUTO_INVITE_OPTIONS_FRAGMENT,
-        normalIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_up.dds",
-        pressedIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_down.dds",
-        mouseoverIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_over.dds",
-    }
-    GROUP_MENU_KEYBOARD:AddCategory(data)
-end
-
 function AutoInviteUI.init()
     if ui.created then return end
     ui.created = true
+    AutoInviteUI:CreateEnabledFragment()
     AutoInviteUI:CreateOptionFragment()
-    AutoInviteUI:AddGroupMenu()
+    AutoInviteUI:CreateScene()
 end
diff --git a/lua/queue.lua b/lua/queue.lua
index 61fc55e..245c6ef 100644
--- a/lua/queue.lua
+++ b/lua/queue.lua
@@ -36,6 +36,7 @@ function queue:push(val)
     local back = self.back
     self.vals[back] = val
     self.back = back + 1
+    MINI_GROUP_LIST:updateSingle(val)
 end

 function queue:pop()
@@ -79,6 +80,7 @@ function AutoInvite:processQueue()
         if self.player ~= name then
             GroupInviteByName(name)
         end
+        MINI_GROUP_LIST:updateSingle(name)
     end

     AutoInvite.sentInvites = math.max(sentCount, 0)
@@ -178,6 +180,8 @@ function AutoInvite.inviteResponse(_, name, responseCode)
         --TODO: Build in a retry invite for some options
         AutoInvite.sentInvite[name] = nil
         AutoInvite.sentInvites = math.max(AutoInvite.sentInvites - 1, 0)
+
+        MINI_GROUP_LIST:updateSingle(name)
     end
 end

diff --git a/ui/ai_enabled_fragment.lua b/ui/ai_enabled_fragment.lua
new file mode 100644
index 0000000..26170a0
--- /dev/null
+++ b/ui/ai_enabled_fragment.lua
@@ -0,0 +1,63 @@
+-- This file is part of AutoInvite
+--
+-- (C) 2014 Scott Yeskie (Sasky)
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+AutoInviteUI = AutoInviteUI or {}
+AutoInviteUI.fragmentEnabled = {}
+local ui = AutoInviteUI.fragmentEnabled
+local wm = WINDOW_MANAGER
+
+function AutoInviteUI:CreateEnabledFragment()
+    ui.main = wm:CreateControl("AutoInviteEnabled", AI_SmallGroupList, CT_CONTROL)     --ui.main = wm:CreateTopLevelWindow("AutoInviteEnabledFragment")
+    ui.scroll = ui.main -- For using LAM controls
+    --ui.main:SetHidden(true)
+    -- LAMr18 bugfix
+    ui.main:SetWidth(340)
+    ui.panel = ui.main
+    ui.panel.data = {}
+    -- End LAMr18 bugfix
+
+    ui.refreshList = wm:CreateControlFromVirtual(nil, ui.main, "ZO_DefaultButton")
+    ui.refreshList:SetAnchor(TOP, AI_SmallGroupList, TOP, -50, 30)
+    ui.refreshList:SetWidth(180)
+    ui.refreshList:SetText(GetString(SI_AUTO_INVITE_BTN_REFRESH))
+    ui.refreshList:SetHandler("OnClicked", function() MINI_GROUP_LIST:RefreshData() end)
+
+    ui.enabled = LAMCreateControl.checkbox(ui, {
+        type = "checkbox",
+        name = GetString(SI_AUTO_INVITE_OPT_ENABLED),
+        tooltip = GetString(SI_AUTO_INVITE_TT_ENABLED),
+        getFunc = function() return AutoInvite.listening end,
+        setFunc = function(val)
+            if val then AutoInvite.startListening() else AutoInvite.disable() end
+        end,
+    })
+    ui.enabled.checkbox:SetAnchor(LEFT, ui.enabled.container, RIGHT, -25, 0)
+    ui.enabled:SetAnchor(TOPRIGHT, ZO_GroupList, TOPRIGHT, -40, -45)
+
+    --TODO: Sanity check between enable and blank string
+    ui.text = LAMCreateControl.editbox(ui, {
+        type = "editbox",
+        name = GetString(SI_AUTO_INVITE_OPT_STRING),
+        tooltip = GetString(SI_AUTO_INVITE_TT_STRING),
+        getFunc = function() return AutoInvite.cfg.watchStr end,
+        setFunc = function(val) AutoInvite.cfg.watchStr = val end,
+    })
+    ui.text.container:SetWidth(140)
+    ui.text:SetAnchor(TOPRIGHT, ZO_GroupList, TOPRIGHT, -40, -15)
+
+    --AUTO_INVITE_ENABLED_FRAGMENT = ZO_FadeSceneFragment:New(ui.main)
+end
diff --git a/ui/ai_options_fragment.lua b/ui/ai_options_fragment.lua
index 9cff6a0..972fe0a 100644
--- a/ui/ai_options_fragment.lua
+++ b/ui/ai_options_fragment.lua
@@ -21,36 +21,16 @@ local ui = AutoInviteUI.fragmentOptions
 local wm = WINDOW_MANAGER

 function AutoInviteUI:CreateOptionFragment()
-    ui.main = wm:CreateTopLevelWindow("AutoInviteOptionsFragment")
+    ui.main = wm:CreateControl("AutoInviteOptions", AI_SmallGroupList, CT_CONTROL) --wm:CreateTopLevelWindow("AutoInviteOptionsFragment")
     ui.scroll = ui.main -- For using LAM controls
-    ui.main:SetAnchor(TOPRIGHT, ZO_GroupList, TOPRIGHT, -100, -40)
-    ui.main:SetHidden(true)
+    ui.main:SetAnchor(TOPRIGHT, ZO_GroupList, TOPRIGHT, -40, 45)
+    --ui.main:SetHidden(true)
     -- LAMr18 bugfix
-    ui.main:SetWidth(500)
+    ui.main:SetWidth(340)
     ui.panel = ui.main
     ui.panel.data = {}
     -- End LAMr18 bugfix

-    ui.enabled = LAMCreateControl.checkbox(ui, {
-        type = "checkbox",
-        name = GetString(SI_AUTO_INVITE_OPT_ENABLED),
-        tooltip = GetString(SI_AUTO_INVITE_TT_ENABLED),
-        getFunc = function() return AutoInvite.listening end,
-        setFunc = function(val)
-            if val then AutoInvite.startListening() else AutoInvite.disable() end
-        end
-    })
-    ui.enabled:SetAnchor(TOPRIGHT, ui.main, TOPRIGHT, 0, 40)
-
-    ui.text = LAMCreateControl.editbox(ui, {
-        type = "editbox",
-        name = GetString(SI_AUTO_INVITE_OPT_STRING),
-        tooltip = GetString(SI_AUTO_INVITE_TT_STRING),
-        getFunc = function() return AutoInvite.cfg.watchStr end,
-        setFunc = function(val) AutoInvite.cfg.watchStr = val end,
-    })
-    ui.text:SetAnchor(TOPRIGHT, ui.enabled, TOPRIGHT, 0, 40)
-
     ui.max = LAMCreateControl.slider(ui, {
         type = "slider",
         name = GetString(SI_AUTO_INVITE_OPT_MAX_SIZE),
@@ -61,7 +41,7 @@ function AutoInviteUI:CreateOptionFragment()
         setFunc = function(val) AutoInvite.cfg.maxSize = val end,
         default = 24,
     })
-    ui.max:SetAnchor(TOPLEFT, ui.text, BOTTOMLEFT, 0, 25)
+    ui.max:SetAnchor(TOPRIGHT, ui.main, TOPRIGHT, 0, 15)

     ui.restart = LAMCreateControl.checkbox(ui, {
         type = "checkbox",
@@ -70,7 +50,8 @@ function AutoInviteUI:CreateOptionFragment()
         getFunc = function() return AutoInvite.cfg.restart end,
         setFunc = function(val) AutoInvite.cfg.restart = val  end,
     })
-    ui.restart:SetAnchor(TOPLEFT, ui.max, BOTTOMLEFT, 0, 25)
+    ui.restart.checkbox:SetAnchor(LEFT, ui.restart.container, RIGHT, -25, 0)
+    ui.restart:SetAnchor(TOPLEFT, ui.max, BOTTOMLEFT, 0, 15)

     ui.cyr = LAMCreateControl.checkbox(ui, {
         type = "checkbox",
@@ -79,7 +60,8 @@ function AutoInviteUI:CreateOptionFragment()
         getFunc = function() return AutoInvite.cfg.cyrCheck end,
         setFunc = function(val) AutoInvite.cfg.cyrCheck = val end,
     })
-    ui.cyr:SetAnchor(TOPLEFT, ui.restart, BOTTOMLEFT, 0, 25)
+	ui.cyr.checkbox:SetAnchor(LEFT, ui.cyr.container, RIGHT, -25, 0)
+    ui.cyr:SetAnchor(TOPLEFT, ui.restart, BOTTOMLEFT, 0, 15)

     ui.kick = LAMCreateControl.checkbox(ui, {
         type = "checkbox",
@@ -88,7 +70,8 @@ function AutoInviteUI:CreateOptionFragment()
         getFunc = function() return AutoInvite.cfg.autoKick end,
         setFunc = function(val) AutoInvite.cfg.autoKick = val  end,
     })
-    ui.kick:SetAnchor(TOPLEFT, ui.cyr, BOTTOMLEFT, 0, 25)
+    ui.kick.checkbox:SetAnchor(LEFT, ui.kick.container, RIGHT, -25, 0)
+    ui.kick:SetAnchor(TOPLEFT, ui.cyr, BOTTOMLEFT, 0, 15)

     ui.kickTime = LAMCreateControl.slider(ui, {
         type = "slider",
@@ -99,33 +82,31 @@ function AutoInviteUI:CreateOptionFragment()
         getFunc = function() return AutoInvite.cfg.kickDelay end,
         setFunc = function(val) AutoInvite.cfg.kickDelay = val end,
         default = 300,
-        --width = "half"
     })
-    ui.kickTime:SetAnchor(TOPLEFT, ui.kick, BOTTOMLEFT, 0, 25)
+    ui.kickTime:SetAnchor(TOPLEFT, ui.kick, BOTTOMLEFT, 0, 15)

     ui.regroup = wm:CreateControlFromVirtual(nil, ui.main, "ZO_DefaultButton")
-    ui.regroup:SetAnchor(TOPLEFT, ui.kickTime, BOTTOMLEFT, 150, 50)
+    ui.regroup:SetAnchor(TOPLEFT, ui.kickTime, BOTTOMLEFT, 70, 70)
     ui.regroup:SetWidth(160)
     ui.regroup:SetText(GetString(SI_AUTO_INVITE_BTN_REFORM))
     ui.regroup:SetHandler("OnClicked", function() AutoInvite:resetGroup() end)

     ui.invite = wm:CreateControlFromVirtual(nil, ui.main, "ZO_DefaultButton")
-    ui.invite:SetAnchor(TOPLEFT, ui.kickTime, BOTTOMLEFT, 150, 85)
+    ui.invite:SetAnchor(TOPLEFT, ui.kickTime, BOTTOMLEFT, 70, 105)
     ui.invite:SetWidth(160)
     ui.invite:SetText(GetString(SI_AUTO_INVITE_BTN_REINVITE))
     ui.invite:SetHandler("OnClicked", function() AutoInvite:inviteGroup() end)

     local slashcmds = GetString(SI_AUTO_INVITE_SLASHCMD_START) ..
             "\n" .. GetString(SI_AUTO_INVITE_SLASHCMD_HELP) ..
-            "\n" .. GetString(SI_AUTO_INVITE_SLASHCMD_REGRP) ..
             "\n" .. GetString(SI_AUTO_INVITE_SLASHCMD_STOP)
     ui.note = LAMCreateControl.description(ui, {
         type = "description",
         title = GetString(SI_AUTO_INVITE_OPT_SLASHCMD),
         text = slashcmds,
     })
-    ui.note:SetAnchor(BOTTOMRIGHT, ZO_GroupList, BOTTOMRIGHT, -40, -40)
+    ui.note:SetAnchor(BOTTOMRIGHT, ZO_GroupList, BOTTOMRIGHT, 0, -40)
     ui.note.desc:SetColor(.7,.7,.7,1)

-    AUTO_INVITE_OPTIONS_FRAGMENT = ZO_FadeSceneFragment:New(ui.main)
+    --AUTO_INVITE_OPTIONS_FRAGMENT = ZO_FadeSceneFragment:New(ui.main)
 end
diff --git a/ui/autoinvitescene.lua b/ui/autoinvitescene.lua
new file mode 100644
index 0000000..155176e
--- /dev/null
+++ b/ui/autoinvitescene.lua
@@ -0,0 +1,122 @@
+-- This file is part of AutoInvite
+--
+-- (C) 2014 Scott Yeskie (Sasky)
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+AutoInviteUI = AutoInviteUI or {}
+
+--local keybindStripDescriptor =
+--{
+--    alignment = KEYBIND_STRIP_ALIGN_CENTER,
+--
+--    -- Invite to Group
+--    {
+--        name = GetString(SI_GROUP_WINDOW_INVITE_PLAYER),
+--        keybind = "UI_SHORTCUT_PRIMARY",
+--
+--        callback = function()
+--            ZO_Dialogs_ShowDialog("GROUP_INVITE")
+--        end,
+--
+--        visible = function()
+--            --return not self.playerIsGrouped or (self.playerIsLeader and self.groupSize < GROUP_SIZE_MAX)
+--            return (GetGroupSize() <= AutoInvite.cfg.maxSize)
+--        end
+--    },
+--
+--    -- Start Search
+--    {
+--        name = GetString(SI_GROUPING_TOOLS_PANEL_START_SEARCH),
+--        keybind = "UI_SHORTCUT_SECONDARY",
+--
+--        callback = function()
+--            AutoInvite.startListening()
+--        end,
+--
+--        visible = function()
+--            return not AutoInvite.enabled
+--        end
+--    },
+--
+--    -- Cancel Search
+--    {
+--        name = GetString(SI_GROUP_WINDOW_CANCEL_SEARCH),
+--        keybind = "UI_SHORTCUT_NEGATIVE",
+--
+--        callback = function()
+--            AutoInvite.disable()
+--        end,
+--
+--        visible = function()
+--            return AutoInvite.enabled
+--        end
+--    },
+--}
+--
+--local function manageKeybinds(_, newState)
+--    if(newState == SCENE_SHOWING) then
+--        KEYBIND_STRIP:AddKeybindButtonGroup(keybindStripDescriptor)
+--    elseif(newState == SCENE_HIDDEN) then
+--        KEYBIND_STRIP:RemoveKeybindButtonGroup(keybindStripDescriptor)
+--    end
+--end
+
+function AutoInviteUI:CreateScene()
+    --AUTO_INVITE_SCENE = ZO_Scene:New("autoInvite", SCENE_MANAGER)
+    --AUTO_INVITE_SCENE:AddFragmentGroup(FRAGMENT_GROUP.MOUSE_DRIVEN_UI_WINDOW)
+    --AUTO_INVITE_SCENE:AddFragmentGroup(FRAGMENT_GROUP.FRAME_TARGET_STANDARD_RIGHT_PANEL)
+    --AUTO_INVITE_SCENE:AddFragment(RIGHT_BG_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(DISPLAY_NAME_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(TITLE_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(GROUP_TITLE_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(GROUP_MEMBERS_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(GROUP_WINDOW_SOUNDS)
+    --AUTO_INVITE_SCENE:AddFragment(PLAYER_PROGRESS_BAR_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(PLAYER_PROGRESS_BAR_CURRENT_FRAGMENT)
+
+    --AutoInvite Fragments
+    --AUTO_INVITE_SCENE:AddFragment(AI_SMALL_GROUP_LIST_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(AUTO_INVITE_OPTIONS_FRAGMENT)
+    --AUTO_INVITE_SCENE:AddFragment(AUTO_INVITE_ENABLED_FRAGMENT)
+
+	local data =
+    {
+        name = GetString(SI_AUTO_INVITE),
+        categoryFragment = AI_SMALL_GROUP_LIST_FRAGMENT,
+        normalIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_up.dds",
+        pressedIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_down.dds",
+        mouseoverIcon = "EsoUI/Art/Campaign/campaign_tabIcon_summary_over.dds",
+    }
+    GROUP_MENU_KEYBOARD:AddCategory(data)
+
+--    local mainMenu = GetAPIVersion() <= 100012 and MAIN_MENU or MAIN_MENU_KEYBOARD
+--
+--    local indx = #mainMenu.sceneGroupInfo.groupSceneGroup.menuBarIconData + 1
+--    mainMenu.sceneGroupInfo.groupSceneGroup.menuBarIconData[indx] = {
+--        categoryName = SI_AUTO_INVITE,
+--        descriptor = "autoInvite",
+--        normal = "EsoUI/Art/Campaign/campaign_tabIcon_summary_up.dds",
+--        pressed = "EsoUI/Art/Campaign/campaign_tabIcon_summary_down.dds",
+--        highlight = "EsoUI/Art/Campaign/campaign_tabIcon_summary_over.dds",
+--    }
+--
+--    SCENE_MANAGER:GetSceneGroup("groupSceneGroup").scenes[indx] = "autoInvite"
+--    AUTO_INVITE_SCENE:AddFragment(ZO_FadeSceneFragment:New(mainMenu.sceneGroupBar))
+--
+----    AUTO_INVITE_SCENE:RegisterCallback("StateChange", manageKeybinds)
+--
+--    --TODO: Constant for the magic number?
+--    mainMenu:AddRawScene("autoInvite", 6, mainMenu.categoryInfo[6], "groupSceneGroup")
+end
diff --git a/ui/half_grouplist.lua b/ui/half_grouplist.lua
new file mode 100644
index 0000000..01526d1
--- /dev/null
+++ b/ui/half_grouplist.lua
@@ -0,0 +1,288 @@
+-- This file is part of AutoInvite
+--
+-- (C) 2014 Scott Yeskie (Sasky)
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+local function dbg(msg) if AutoInvite.debug then d("|c999999" .. msg) end end
+local function echo(msg) CHAT_SYSTEM.primaryContainer.currentBuffer:AddMessage("|CFFFF00"..msg) end
+
+local AI_SmallGroupListing = ZO_SortFilterList:Subclass()
+local AI_GROUP_LIST_ENTRIES = {}
+
+local AI_GROUP_DATA = 1
+
+--local ENTRY_SORT_KEYS =
+--{
+--    ["displayName"] = { },
+--}
+
+local STATUS_ORDERING = setmetatable({
+    ONLINE = 1,
+    OFFLINE = 2,
+    SENT = 3,
+    QUEUE = 4,
+    GROUPED = 5,
+    UNKNOWN = 6,
+}, { __index = function() return 6 end })
+
+function AI_SmallGroupListing:New(control)
+    local manager = ZO_SortFilterList.New(self, control)
+
+    ZO_ScrollList_AddDataType(manager.list, AI_GROUP_DATA, "AI_SmallGroupListRow", 30, function(control, data) manager:SetupEntry(control, data) end)
+    ZO_ScrollList_EnableHighlight(manager.list, "ZO_ThinListHighlight")
+
+    manager:SetEmptyText(GetString(SI_AUTO_INVITE_NO_GROUP_MESSAGE))
+    manager.emptyRow:ClearAnchors()
+    manager.emptyRow:SetAnchor(TOPLEFT, manager.control, TOPLEFT, 15, 100)
+    manager.emptyRow:SetWidth(300)
+    manager:SetAlternateRowBackgrounds(true)
+    manager:RefreshData()
+
+    manager.sortHeaderGroup:SelectHeaderByKey("displayName")
+
+    local function Update()
+        manager:RefreshData()
+    end
+
+--    local function UpdateSingle(name)
+--        manager:updateSingle(name)
+--    end
+
+    ZO_PreHook(GROUP_LIST, "FilterScrollList", function()
+        dbg("Hooked FilterScrollList")
+        if not hookedMasterList then
+            manager:RefreshData()
+        end
+    end)
+
+--    control:RegisterForEvent(EVENT_GROUP_MEMBER_LEFT, Update)
+    control:RegisterForEvent(EVENT_GROUP_MEMBER_JOINED, Update)
+--    control:RegisterForEvent(EVENT_GROUP_MEMBER_KICKED, Update)
+--    control:RegisterForEvent(EVENT_GROUP_DISBANDED, Update)
+--    control:RegisterForEvent(EVENT_PLAYER_ACTIVATED, Update)
+
+    AI_SMALL_GROUP_LIST_FRAGMENT = ZO_FadeSceneFragment:New(AI_SmallGroupList)
+
+    return manager
+end
+
+function AI_SmallGroupListing:updateSingle(name)
+    dbg("Calling AI_SmallGroupListing:updateSingle()")
+    --Used for sent invite, invite declined, add to queue
+    --Updates that one entry then
+    if AI_GROUP_LIST_ENTRIES[name] then
+        AI_GROUP_LIST_ENTRIES[name]:Update()
+    elseif name then
+        dbg("Name " .. name .. " not found.")
+       AI_GROUP_LIST_ENTRIES[name] = AI_SLG_Entry.New(name)
+    end
+
+    self:RefreshFilters()
+end
+
+function AI_SmallGroupListing:removeSingle(name)
+    dbg("Calling AI_SmallGroupListing:updateSingle()")
+    AI_GROUP_LIST_ENTRIES[name] = nil
+    self:RefreshFilters()
+end
+
+function AI_SmallGroupListing:getStatus(data)
+    --TODO: Make this a LUT
+    local status = data.status
+    if status == STATUS_ORDERING.ONLINE then
+        return "|c33CC33Online"
+    end
+
+    if status == STATUS_ORDERING.OFFLINE then
+        return "|c666666Offline"
+    end
+
+    if status == STATUS_ORDERING.SENT then
+        return "|c999966Sent"
+    end
+
+    if status == STATUS_ORDERING.QUEUE then
+        return "|c999999Queue"
+    end
+
+    if status == STATUS_ORDERING.GROUPED then
+        return "|cFB2B2BGrouped"
+    end
+
+    return ""
+end
+
+function AI_SmallGroupListing:SetupEntry(control, data)
+    ZO_SortFilterList.SetupRow(self, control, data)
+
+    control.displayName = data.displayName
+
+    GetControl(control, "DisplayName"):SetText(data.displayName)
+    GetControl(control, "Status"):SetText(self:getStatus(data))
+    --GetControl(control, "BG"):SetWidth(300)
+end
+
+function AI_SmallGroupListing.CompareMembers(listEntry1, listEntry2)
+    local d1 = listEntry1.data
+    local d2 = listEntry2.data
+
+    if d1.status == d2.status then
+        return string.lower(d1.displayName) < string.lower(d2.displayName)
+    else
+        return d1.status < d2.status
+    end
+end
+
+local function addTestCase(name, status, arg)
+    AI_GROUP_LIST_ENTRIES[name] = AI_SLG_Entry.NewDefined(name, status, arg)
+end
+
+function AI_SmallGroupListing:BuildMasterList()
+    dbg("Calling AI_SmallGroupListing:BuildMasterList()")
+    AI_GROUP_LIST_ENTRIES = {}
+
+    for name,time in pairs(AutoInvite.sentInvite) do
+        AI_GROUP_LIST_ENTRIES[name] = AI_SLG_Entry.NewDefined(name, STATUS_ORDERING.SENT, time)
+    end
+
+    for _,name in pairs(AutoInvite.__getQueue()) do
+        AI_GROUP_LIST_ENTRIES[name] = AI_SLG_Entry.NewDefined(name, STATUS_ORDERING.QUEUE)
+    end
+
+    for i=1,GetGroupSize() do
+        local tag = GetGroupUnitTagByIndex(i)
+        local name = GetUnitName(tag)
+        AI_GROUP_LIST_ENTRIES[name] = AI_SLG_Entry.New(name, tag)
+    end
+
+    addTestCase("Zaniira", 1)
+    addTestCase("Ravlor", 2)
+    addTestCase("Sasky", 1)
+    addTestCase("Jinsa", 3)
+    addTestCase("Sascii", 4)
+end
+
+function AI_SmallGroupListing:FilterScrollList()
+    dbg("Calling AI_SmallGroupListing:FilterScrollList()")
+    -- No filtering. Copy over from master list
+    local scrollData = ZO_ScrollList_GetDataList(self.list)
+    ZO_ClearNumericallyIndexedTable(scrollData)
+
+    for _,data in pairs(AI_GROUP_LIST_ENTRIES) do
+        table.insert(scrollData, ZO_ScrollList_CreateDataEntry(AI_GROUP_DATA, data))
+    end
+end
+
+function AI_SmallGroupListing:SortScrollList()
+    dbg("Calling AI_SmallGroupListing:SortScrollList()")
+    if(self.currentSortKey ~= nil and self.currentSortOrder ~= nil) then
+        local scrollData = ZO_ScrollList_GetDataList(self.list)
+        table.sort(scrollData, self.CompareMembers)
+    end
+end
+
+AI_SLG_Entry = {}
+AI_SLG_Entry.__index = AI_SLG_Entry
+
+--For debugging
+function AI_SLG_Entry.NewDefined(name, status, arg)
+    local self = setmetatable({}, AI_SLG_Entry)
+    self.status = status
+    self.displayName = name
+
+    if status == STATUS_ORDERING.queue then
+        self.position = arg
+    else
+        self.time = arg
+    end
+
+    return self
+end
+
+function AI_SLG_Entry:Update()
+    local name = self.displayName or ""
+    local tag = self.unitName
+
+    local grouped = IsPlayerInGroup(name) and not AutoInvite:IsPlayerInSameGroup(name)
+    if grouped then
+        self.status = STATUS_ORDERING.GROUPED
+        return;
+    end
+    if GetUnitName(tag) == name then
+        local offline = AutoInvite.kickTable[name]
+        if IsUnitOnline(tag) then
+            self.status = STATUS_ORDERING.ONLINE
+        else
+            self.status = STATUS_ORDERING.OFFLINE
+            self.time = offline
+        end
+    else
+        local sent = AutoInvite:IsInviteSent(name)
+        if sent then
+            self.status = STATUS_ORDERING.SENT
+            self.time = sent
+        else
+            local queue = AutoInvite:IsInQueue(name)
+            if queue then
+                self.status = STATUS_ORDERING.QUEUE
+                --self.position = queue
+            else
+                dbg("Unknown status for " .. name)
+                AI_GROUP_LIST_ENTRIES[name] = nil
+            end
+        end
+    end
+end
+
+function AI_SLG_Entry.New(name, tag)
+    local self = setmetatable({}, AI_SLG_Entry)
+    self.status = STATUS_ORDERING.UNKNOWN
+    self.displayName = name
+    self.unitName = tag
+    self:Update()
+    return self
+end
+
+--Global XML Handlers
+
+
+--function ZO_IgnoreListManager:IgnoreListPanelRow_OnMouseUp(control, button, upInside)
+--    if(button == 2 and upInside) then
+--        ClearMenu()
+--
+--        local data = ZO_ScrollList_GetData(control)
+--        if data then
+--            AddMenuItem(GetString(SI_SOCIAL_MENU_EDIT_NOTE), GetNoteEditFunction(self.control, data.displayName))
+--            AddMenuItem(GetString(SI_IGNORE_MENU_REMOVE_IGNORE), function() RemoveIgnore(data.displayName) end)
+--
+--            self:ShowMenu(control)
+--        end
+--    end
+--end
+
+function AI_SmallGroupListing_OnMouseEnter(control)
+    MINI_GROUP_LIST:Row_OnMouseEnter(control)
+end
+
+function AI_SmallGroupListing_OnMouseExit(control)
+    MINI_GROUP_LIST:Row_OnMouseExit(control)
+end
+
+--function AI_SmallGroupListing_OnMouseUp(control, button, upInside)
+--    MINI_GROUP_LIST:IgnoreListPanelRow_OnMouseUp(control, button, upInside)
+--end
+
+function AI_SmallGroupListing_OnInitialized(self)
+    MINI_GROUP_LIST = AI_SmallGroupListing:New(self)
+end
diff --git a/ui/half_grouplist.xml b/ui/half_grouplist.xml
new file mode 100644
index 0000000..9083166
--- /dev/null
+++ b/ui/half_grouplist.xml
@@ -0,0 +1,96 @@
+<!--
+  ~ This file is part of AutoInvite
+  ~
+  ~ (C) 2014 Scott Yeskie (Sasky)
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<GuiXml>
+    <Controls>
+        <Label name="AI_SmallGroupListLabel" font="ZoFontGame" wrapMode="ELLIPSIS" virtual="true"/>
+        <Control name="AI_SmallGroupListRow" mouseEnabled="true" virtual="true">
+            <Dimensions x="300" y="30"/>
+            <OnMouseEnter>
+                AI_SmallGroupListing_OnMouseEnter(self)
+            </OnMouseEnter>
+            <OnMouseExit>
+                AI_SmallGroupListing_OnMouseExit(self)
+            </OnMouseExit>
+            <!--<OnMouseUp>
+                AI_SmallGroupListing_OnMouseUp(self, button, upInside)
+            </OnMouseUp>-->
+            <Controls>
+                <Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" hidden="true">
+                    <Dimensions x="260"/>
+                </Texture>
+
+                <!--<Button name="$(parent)Cancel" inherits="ZO_IgnoreListRowButton">
+                    <Anchor point="LEFT" offsetX="5"/>
+                    <Textures
+                            normal="EsoUI/Art/Buttons/cancel_up.dds"
+                            pressed="EsoUI/Art/Buttons/cancel_down.dds"
+                            mouseOver="EsoUI/Art/Buttons/cancel_over.dds"
+                            />
+                    <OnMouseEnter>
+                        ZO_Tooltips_ShowTextTooltip(self, TOP, GetString(SI_IGNORE_LIST_REMOVE_IGNORE))
+                        ZO_IgnoreListRow_OnMouseEnter(self:GetParent())
+                    </OnMouseEnter>
+                    <OnMouseExit>
+                        ZO_Tooltips_HideTextTooltip()
+                        ZO_IgnoreListRow_OnMouseExit(self:GetParent())
+                    </OnMouseExit>
+                    <OnClicked>
+                        RemoveIgnore(self:GetParent().displayName)
+                    </OnClicked>
+                </Button>-->
+                <Label name="$(parent)Status" inherits="AI_SmallGroupListLabel">
+                    <Anchor point="LEFT" offsetX="5"/>
+                    <Dimensions x="50"/>
+                </Label>
+                <Label name="$(parent)DisplayName" inherits="AI_SmallGroupListLabel">
+                    <Anchor point="LEFT" relativeTo="$(parent)Status" relativePoint="RIGHT" offsetX="10"/>
+                    <Dimensions x="200"/>
+                </Label>
+            </Controls>
+        </Control>
+
+        <TopLevelControl name="AI_SmallGroupList" inherits="ZO_RightPanelFootPrint" hidden="true">
+            <OnInitialized>
+                AI_SmallGroupListing_OnInitialized(self)
+            </OnInitialized>
+            <Controls>
+                <Control name="$(parent)Headers">
+                    <Anchor point="TOPLEFT" offsetX="280" offsetY="67"/>
+                    <Anchor point="TOPRIGHT" offsetY="67" offsetX="300"/>
+                    <Dimensions y="32"/>
+                    <Controls>
+                        <Control name="$(parent)DisplayName" inherits="ZO_SortHeader">
+                            <OnInitialized>
+                                ZO_SortHeader_Initialize(self, GetString(SI_GROUP_LIST_PANEL_NAME_HEADER), "displayName", ZO_SORT_ORDER_UP, TEXT_ALIGN_LEFT, "ZoFontGameLargeBold")
+                            </OnInitialized>
+                            <Anchor point="TOPLEFT" offsetX="59"/>
+                            <Dimensions x="200" y="32"/>
+                        </Control>
+                    </Controls>
+                </Control>
+
+                <Control name="$(parent)List" inherits="ZO_ScrollList">
+                    <Anchor point="TOPLEFT" relativeTo="$(parent)Headers" relativePoint="BOTTOMLEFT" offsetY="3"/>
+                    <Anchor point="BOTTOMRIGHT" relativeTo="$(parent)" relativePoint="BOTTOMLEFT" offsetX="600" offsetY="-32"/>
+                </Control>
+            </Controls>
+        </TopLevelControl>
+    </Controls>
+</GuiXml>
\ No newline at end of file