changeset 32:e8679ecb48d8

ClassPlan: - Available missions are now recorded; the mission list can be toggled between in-progress and available by clicking the heading.
author Nenue
date Tue, 01 Nov 2016 10:48:50 -0400
parents d0114b51cdea
children be4db60219ca
files ClassPlan.lua ClassPlan.xml FilterBar.lua WorldPlan.lua
diffstat 4 files changed, 682 insertions(+), 542 deletions(-) [+]
line wrap: on
line diff
--- a/ClassPlan.lua	Fri Oct 28 19:54:00 2016 -0400
+++ b/ClassPlan.lua	Tue Nov 01 10:48:50 2016 -0400
@@ -1,57 +1,27 @@
 local wipe, tinsert, sort = table.wipe, tinsert, table.sort
 local pairs, ipairs = pairs, ipairs
 local floor, mod, time = floor, mod, time
+local max, min = math.max, math.min
 local GetTime = GetTime
 local GI_currentTime = time()
+local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop
 
-local BOUND_FRAMES = {}
-local blockTemplate = {
-  point = 'TOPLEFT',
-  relativePoint ='TOPLEFT',
+local CG_GetBuildings = C_Garrison.GetBuildings
+local CG_GetFollowerShipments = C_Garrison.GetFollowerShipments
+local CG_GetLooseShipments = C_Garrison.GetLooseShipments
+local CG_GetTalentTrees = C_Garrison.GetTalentTrees
+local CG_GetCompleteTalent = C_Garrison.GetCompleteTalent
+local CG_GetLandingPageShipmentInfo = C_Garrison.GetLandingPageShipmentInfo
+local CG_GetLandingPageShipmentInfoByContainerID = C_Garrison.GetLandingPageShipmentInfoByContainerID
+
+local CP_REPLACE_LANDINGPAGE = true
+local CP_HEADER_SIZE = 24
+local CP_BACKGROUND_COLOR = {
+  inProgress = {0, 0, 0, 0.5},
+  shipmentsReady = {0, 0, 0, 0.25},
+  complete = {0.5, 0.5, 0.5, 0.5}
 }
 
-SLASH_CLASSPLAN1 = "/classplan"
-SLASH_CLASSPLAN2 = "/cp"
-SlashCmdList.CLASSPLAN = function(args)
-  ClassOrderPlan:Toggle()
-end
-
-ClassOrderPlanCore = {
-  events = {},
-  freeBlocks = {},
-  blocks = {},
-  sortedItems = {},
-  timers = {},
-  shipments = {},
-  playerFirst = false,
-  prototypes = {},
-  Queued = {}
-}
-
-ClassPlanMissionMixin = {
-  templateName = 'ClassPlanMissionEntry',
-  events = {
-    'GARRISON_MISSION_LIST_UPDATE',
-    'GARRISON_MISSION_STARTED',
-    'GARRISON_MISSION_FINISHED',
-    'GARRISON_LANDINGPAGE_SHIPMENTS'},}
-ClassPlanShipmentMixin = {
-  templateName = 'ClassPlanShipmentEntry',
-  parent = false,
-  point = 'TOPRIGHT',
-  relativePoint ='TOPRIGHT',
-  events = {
-    'GARRISON_LANDINGPAGE_SHIPMENTS',
-    'GARRISON_TALENT_UPDATE',
-    "GARRISON_TALENT_COMPLETE",
-    "GARRISON_SHIPMENT_RECEIVED",
-    'GARRISON_FOLLOWER_LIST_UPDATE',
-    'GARRISON_SHOW_LANDING_PAGE'},
-}
-setmetatable(ClassPlanShipmentMixin, {__index = ClassPlanMissionMixin})
-local core, MissionsHandler, ShipmentsHandler = ClassOrderPlanCore, ClassPlanMissionMixin, ClassPlanShipmentMixin
-local print = DEVIAN_WORKSPACE and function(...) print('ClassPlan', ...) end or nop
-
 local GetTimeLeftString = function(timeLeft)
   local days = floor(timeLeft/(24*3600))
   local hours = floor(mod(timeLeft, (24*3600)) / 3600)
@@ -65,260 +35,113 @@
 end
 
 
+ClassOrderPlanCore = {
+  events = {},
+  freeBlocks = {},
+  blocks = {},
+  sortedItems = {},
+  timers = {},
+  shipments = {},
+  playerFirst = false,
+  prototypes = {},
+  Queued = {}
+}
+local MissionList = {
+  templateName = 'ClassPlanMissionEntry',
+  listKey = {'missions', 'available'},
+  listTitle = {'In Progress', 'Available'},
 
-MissionsHandler.GetPlayerData = function(self)
-  if not self.profile then
-    return
-  end
-  local items = C_Garrison.GetLandingPageItems(LE_GARRISON_TYPE_7_0)
-  wipe(self.profile.missions)
-  for index, data in ipairs(items) do
-    print(' -',data.name, '|cFF00FF00'.. data.timeLeft .. '|r', date("%A %I:%m %p", data.missionEndTime))
-    tinsert(self.profile.missions, data)
-  end
-  ClassOrderPlan.isStale = true
-end
+  point = 'TOPLEFT',
+  relativePoint ='TOPLEFT',
+  events = {
+    'GARRISON_MISSION_LIST_UPDATE',
+    'GARRISON_LANDINGPAGE_SHIPMENTS'},
+}
+local ShipmentList = {
+  templateName = 'ClassPlanShipmentEntry',
+  listKey = {'shipments'},
+  listTitle = {'Work Orders'},
+  events = {
+    'GARRISON_MISSION_LIST_UPDATE',
+    'GARRISON_LANDINGPAGE_SHIPMENTS',
+    'GARRISON_TALENT_UPDATE',
+    "GARRISON_TALENT_COMPLETE",
+    "GARRISON_SHIPMENT_RECEIVED",
+    'GARRISON_FOLLOWER_LIST_UPDATE',
+    'GARRISON_SHOW_LANDING_PAGE'},
+}
+local SharedHandlers = {
+  numBlocks = 0,
+  isStale = true,
+}
+local SharedEntry = {}
+local ShipmentEntry = {}
+local MissionEntry = {}
 
-MissionsHandler.OnGetItem = function(data)
-  if data.missionEndTime < GI_currentTime then
-    data.isComplete = true
-  end
-end
+local ClassPlan = ClassOrderPlanCore
 
-MissionsHandler.FreeBlock = function(self, block)
-end
-
-MissionsHandler.SortHandler = function (a,b)
-  local result = false
-  --if not a or not b then
-  --  return true
-  --else
-    --if (a.isMine ~= b.isMine) then
-    --  result = a.isMine
-    --else
-      --if (not b.missionEndTime) or (not a.missionEndTime) then
-      --  print('missing article', b.missionEndTime, a.missionEndTime)
-      --end
-      return ( b.missionEndTime > a.missionEndTime)
-    --end
-  --end
-end
-
-ShipmentsHandler.OnGetItem = function(data)
-  if data.shipmentsTotal then
-    local timeLeft = data.creationTime + data.duration - GI_currentTime
-    if (timeLeft <= 0) and (data.shipmentsReady < data.shipmentsTotal) then
-      local numOrders = min(-1*floor(timeLeft/data.duration), (data.shipmentsTotal - data.shipmentsReady))
-
-      if not data.originalCreationTime then
-        data.originalCreationTime = data.creationTime
-        data.originalShipmentsReady = data.shipmentsReady
-      end
-
-      data.creationTime = data.creationTime + numOrders*data.duration
-      data.shipmentsReady = data.shipmentsReady + numOrders
-      print(data.profileKey, 'shipment "'.. data.name..'" reconciling', numOrders, 'lapsed orders. -->', data.creationTime, data.shipmentsReady)
-    end
-  end
-end
-
-ShipmentsHandler.SortHandler = function(a, b)
-  if b.isComplete ~= a.isComplete then
-    return a.isComplete and true or false
-  elseif a.shipmentsReady or b.shipmentsReady then
-    return (a.shipmentsReady or 0) > (b.shipmentsReady or 0)
-  else
-    return (a.creationTime) < (b.creationTime)
-  end
-end
-
-local ShipmentsInfo = {}
-local AddShipmentInfo = function(shipmentType, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID, followerID)
-  -- early login queries may return empty tables, causing the sorter to compare nil
-  if not creationTime then
-    return
-  end
-  --print(shipmentType, name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
-  tinsert(ShipmentsInfo,
-    {
-      shipmentType = shipmentType,
-      name = name,
-      icon = texture,
-      shipmentCapacity = shipmentCapacity,
-      shipmentsReady = shipmentsReady,
-      shipmentsTotal = shipmentsTotal,
-      creationTime = creationTime,
-      duration = duration,
-      timeleftString = timeleftString,
-      itemName = itemName,
-      itemIcon = itemIcon,
-      itemQuality = itemQuality,
-      itemID = itemID,
-      followerID = followerID,
-    })
-end
-ShipmentsHandler.GetPlayerData = function (self)
-  if not self.profile then
-    return
-  end
-  wipe(ShipmentsInfo)
-
-  local garrisonType = LE_GARRISON_TYPE_7_0
-  local buildings = C_Garrison.GetBuildings(garrisonType);
-  local shipmentIndex = 0
-  --print('Buildings:')
-  for i = 1, #buildings do
-    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = C_Garrison.GetLandingPageShipmentInfo(buildingID);
-    AddShipmentInfo('Building', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID)
-  end
-
-  --print('Follower:')
-  local followerShipments = C_Garrison.GetFollowerShipments(garrisonType);
-  for i = 1, #followerShipments do
-    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = C_Garrison.GetLandingPageShipmentInfoByContainerID(followerShipments[i]);
-    AddShipmentInfo('Follower', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, nil, nil, nil, nil, followerID)
-  end
-
-  --print('Loose:')
-  local looseShipments = C_Garrison.GetLooseShipments(garrisonType)
-  for i = 1, #looseShipments do
-    local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = C_Garrison.GetLandingPageShipmentInfoByContainerID(looseShipments[i]);
-    AddShipmentInfo('Misc', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
-  end
-
-  local talentTrees = C_Garrison.GetTalentTrees(garrisonType, select(3, UnitClass("player")));
-  -- this is a talent that has completed, but has not been seen in the talent UI yet.
-  local completeTalentID = C_Garrison.GetCompleteTalent(garrisonType);
-  --print('Talents:')
-  if (talentTrees) then
-    for treeIndex, tree in ipairs(talentTrees) do
-      for talentIndex, talent in ipairs(tree) do
-        local showTalent = false;
-        if (talent.isBeingResearched) or (talent.id == completeTalentID) then
-          AddShipmentInfo('Talent', talent.name, talent.icon, 1, (talent.isBeingResearched and 0 or 1), 1, talent.researchStartTime, talent.researchDuration, talent.timeleftString)
-        end
-      end
-    end
-  end
-
-  self.profile.shipments = self.profile.shipments or {}
-  wipe(self.profile.shipments)
-  for index, data in ipairs(ShipmentsInfo) do
-    --DEFAULT_CHAT_FRAME:AddMessage(data.shipmentType ..' '.. tostring(data.name) ..' '.. tostring(data.creationTime) ..' '.. tostring(data.duration))
-    tinsert(self.profile.shipments, data)
-  end
-  ClassOrderPlan.isStale = true
-end
-
-function core:OnLoad ()
+function ClassPlan:OnLoad ()
   self:RegisterEvent('PLAYER_LOGIN')
   self:RegisterEvent('ADDON_LOADED')
   self:RegisterEvent('PLAYER_REGEN_ENABLED')
   self:RegisterEvent('PLAYER_REGEN_DISABLED')
-  -- Blizzard_GarrisonUI already fires a shipment data request for GARRISON_SHIPMENT_RECEIVED; this is unlikely to
-  self:AddHandler('missions', MissionsHandler)
-  self:AddHandler('shipments', ShipmentsHandler)
-  self:Reanchor()
-  C_Garrison.RequestLandingPageShipmentInfo();
+  self:RegisterForDrag('LeftButton')
+  self:SetMovable(true)
+  self:SetToplevel(true)
+
+
+  SLASH_CLASSPLAN1 = "/classplan"
+  SLASH_CLASSPLAN2 = "/cp"
+  SlashCmdList.CLASSPLAN = function(args)
+    self:Toggle()
+  end
+
 end
 
-local parentFrames = {'VeneerWorldState', 'OrderHallCommandBar'}
-function core:Reanchor()
-  self:ClearAllPoints()
-  self.anchorParent = UIParent
-  local anchorTo = 'TOP'
-  for i, name in ipairs(parentFrames) do
-    local frame = _G[name]
-    if frame then
-      if not BOUND_FRAMES[frame] then
-        BOUND_FRAMES[frame] = {visible = (frame:IsVisible() and frame:IsShown())}
-        hooksecurefunc(frame, 'Show', function()
-          BOUND_FRAMES[frame].visible = true
-          print(frame:GetName(), 'Show', 'reanchor trigger')
-          self:Reanchor()
-        end)
-        hooksecurefunc(frame, 'Hide', function()
-          BOUND_FRAMES[frame].visible = nil
-          print(frame:GetName(), 'Hide', 'reanchor trigger')
-          self:Reanchor()
-        end)
-      end
-      print('f:', frame:GetName(), BOUND_FRAMES[frame].visible)
-      if BOUND_FRAMES[frame].visible then
-        self.anchorParent = frame
-        anchorTo = 'BOTTOM'
-        break
-      end
-    end
-  end
-  print('|cFFFF8800Using ' .. tostring(self.anchorParent:GetName()) .. '-'..anchorTo..' as anchor point')
+function ClassPlan:GetCurrentProfile()
+  WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
+  local db = WorldPlanData.OrderHall
+  self.data = db
 
-  if self:IsShown() then
-    ClassPlanButton.Background:Show()
-    ClassPlanButton:SetWidth(600)
-  else
-    ClassPlanButton.Background:Hide()
-    ClassPlanButton:SetWidth(200)
-  end
+  local characters = db.characters or {}
+  db.characters = characters
 
-  self:SetPoint('TOP', ClassPlanButton, 'BOTTOM', 0, 0)
-  ClassPlanButton:ClearAllPoints()
-  ClassPlanButton:SetPoint('TOP', self.anchorParent, anchorTo, 0, 0)
+  local name, realm = UnitName('player')
+  realm  = realm or GetRealmName()
+  local profileName = name .. '-' .. realm
 
-  print(ClassPlanButton:GetPoint(1))
+  self.profile = characters[profileName] or {}
+  self.characters = characters
+  characters[profileName] = self.profile
+
+
+  local classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]
+  local className = UnitClass('player')
+
+  print('|cFFFFFF00Loaded:|r', classColor.hex, className, profileName)
+  self.Background:SetColorTexture(classColor.r, classColor.g, classColor.b, 0.5)
+  self.profile.classColor = classColor
+  self.profile.className = className
+  return self.profile
 end
 
-function core:AddHandler(name, prototype)
-  self.prototypes[name] = setmetatable(prototype, {
-    __index = blockTemplate,
-    __tostring = function() return name end
-    })
-
-  for i, event in ipairs(prototype.events) do
-    if not self.events[event] then
-      self:RegisterEvent(event)
-      self.events[event] = {}
-      print('|cFF00FF00registering', event)
-    end
-
-    prototype.numBlocks = 0
-    if not self.events[event][name] then
-      print('adding', name, 'to', event)
-      self.events[event][name] = prototype.GetPlayerData
-    end
+function ClassPlan:SetupHandler(handler)
+  print('|cFF00FF00'..handler:GetName()..' loaded')
+  for i, event in ipairs(handler.events) do
+    print('|cFF00FF00  event', event)
+    handler:RegisterEvent(event)
   end
-  self.sortedItems[name] = {}
+  for index, listKey in ipairs(handler.listKey) do
+    self.profile[listKey] = self.profile[listKey] or {}
+    local listTitle = handler.listTitle[index]
+    setmetatable(self.profile[listKey], { __tostring = listTitle })
+  end
+  handler:SetList(1)
+  handler.sortedItems = {}
 end
 
-function core:Setup()
-  if IsLoggedIn() then
-    WorldPlanData.OrderHall = WorldPlanData.OrderHall or {}
-    self.data = WorldPlanData.OrderHall
-    self.data.characters = self.data.characters or {}
-
-    local name, realm = UnitName('player')
-    realm  = realm or GetRealmName()
-    self.profileKey = name .. '-' .. realm
-    if not self.data.characters[self.profileKey] then
-      self.data.characters[self.profileKey] = {}
-    end
-    self.profile = self.data.characters[self.profileKey]
-
-    self.profile.shipments = self.profile.shipments or {}
-    self.profile.missions = self.profile.missions or {}
-    self.profile.classColor = RAID_CLASS_COLORS[select(2, UnitClass('player'))]
-
-    if self.data.IsShown then
-      self:Show()
-    end
-    self.initialized = true
-    self.isStale = true
-  end
-end
-
-local last_invoc = {}
-function core:OnEvent (event, ...)
-  print(event)
+function ClassPlan:OnEvent (event, arg)
+  print(event, arg)
   if event == 'PLAYER_REGEN_DISABLED' then
     if self:IsVisible() then
       self.combatHide = true
@@ -330,149 +153,82 @@
       self.combatHide = nil
       self:SetShown(true)
     end
+  elseif event == 'ADDON_LOADED' then
+    if arg == 'Blizzard_GarrisonUI' then
+      self:Reanchor()
+    end
   elseif event == 'PLAYER_LOGIN' then
     if not self.initialized then
       self:Setup()
     end
-  elseif self.initialized and self.events[event] then
-    self.isStale = true
-    if self:IsVisible() then
-
-    else
-      for handler, func in pairs(self.events[event]) do
-        if not self.Queued[handler] then
-          print('scheduling update for handler', handler)
-          self.Queued[handler] = C_Timer.NewTimer(0.25, function()
-            func(handler)
-            self.Queued[handler] = nil
-          end)
-        end
-      end
-    end
   end
 end
 
-function core:UpdateNotifications()
+function ClassPlan:Setup()
+  if IsLoggedIn() then
+    print('|cFFFFFF00'..self:GetName()..':Setup()|r')
+
+    self:GetCurrentProfile()
+    for _, handler in ipairs(self.Handlers) do
+      self:SetupHandler(handler)
+    end
+    self.initialized = true
+    self:SetShown(self.data.IsShown)
+  end
 end
 
 
-local SetOwnerData = function(self, data)
-  local name, realm = string.match(data.profileKey, "(.+)%-(.+)")
-  local ownerText = '|c'.. data.classColor.colorStr .. name .. '|r'
-  --if realm ~= GI_currentRealm then
-    --ownerText = ownerText .. ' (' .. realm .. ')'
-  --end
-  self.Owner:SetText(ownerText)
-  self.Name:SetTextColor(data.classColor.r, data.classColor.g, data.classColor.b)
-end
+--- Update space
 
-function core:RefreshItems(configKey, prototype)
-  local sortedItems = self.sortedItems[configKey]
+  local max = math.max
+  function ClassPlan:Update()
+    print('|cFF00FFFFRefresh()|r')
+    self.currentHeight = 0
+    for index, handler in pairs(self.Handlers) do
+      if handler.isStale then
+        print('  |cFF00FF00'..index..' '..handler:GetName()..'|r')
+        local sortedItems = handler.sortedItems
+        local activeKey = handler.activeKey
+        handler.profile = self.profile[handler.activeKey]
 
-  self.blocks[configKey] = self.blocks[configKey] or {}
-  local blocks = self.blocks[configKey]
+        wipe(sortedItems)
+        handler:GetPlayerData(self.profile)
+        for key, profile in pairs(self.data.characters) do
+          print('profile', key, activeKey)
+          local profileList = profile[activeKey]
+          if profileList and #profileList >= 1 then
+            local classColor = profile.classColor or defaultClassColor
+            local isMine = (profile == self.profile)
+            for index, data in ipairs(profileList) do
+              data.classColor = classColor
+              data.profileKey = key
+              data.isMine = isMine
+              if handler.OnGetItem then
+                handler.OnGetItem(data)
+              end
+              tinsert(sortedItems, data)
+            end
+          end
+        end
 
-  local lastProfile
-  local numItems = #sortedItems
-  local totalHeight = 0
-  for i, data in ipairs(sortedItems) do
-    local block = blocks[i]
-
-    if not block then
-      block = CreateFrame('Button', nil, self, prototype.templateName)
-      block:SetID(i)
-      block.handler = prototype
-      prototype.numBlocks = prototype.numBlocks + 1
-
-      if prototype.lastBlock then
-        block:SetPoint('TOPLEFT', prototype.lastBlock, 'BOTTOMLEFT', 0, 0)
-      else
-        block:SetPoint(prototype.point, self[prototype.parent] or self, prototype.relativePoint, 0, 0)
-      end
-      prototype.lastBlock = block
-      blocks[i] = block
-    end
-
-    totalHeight = totalHeight + block:GetHeight()
-    block.lastProfile = lastProfile
-    for k,v in pairs(data) do
-      if type(block[k]) ~= 'function' then
-        block[k] = v
-      end
-    end
-    block:Refresh(data)
-    print(block.isComplete, block.missionEndTime, block.name)
-    SetOwnerData(block, data)
-
-    block:Show()
-    lastProfile = data.profileKey
-  end
-
-  for i = numItems + 1, prototype.numBlocks do
-    if blocks[i] then
-      blocks[i]:Hide()
-    end
-  end
-
-  return totalHeight
-end
-
-
-local GI_profileKey, GI_profile, GI_isMine
-local defaultClassColor = {r = 0.7, g = 0.7, b =0.7, colorStr = "ffffffff"}
-local DoItemList = function (source, dest, onGetItem)
-  local numItems = 0
-  for index, data in ipairs(source) do
-    data.classColor = GI_profile.classColor or defaultClassColor
-    data.profileKey = GI_profileKey
-    data.isMine = GI_isMine
-    if onGetItem then
-      onGetItem(data)
-    end
-    numItems = numItems + 1
-    tinsert(dest, data)
-  end
-  return numItems
-end
-
-local max = math.max
-function core:Refresh()
-  print('|cFF00FFFFRefresh()|r')
-  self.currentHeight = 0
-  for name, ptype in pairs(self.prototypes) do
-    print(' |cFF00FF00'..name..'|r')
-    if self.isStale then
-      ptype.GetPlayerData(self)
-      self.sortedItems[name] = self.sortedItems[name] or {}
-
-      wipe(self.sortedItems[name])
-        --print(  'object:', ptype)
-      for key, profile in pairs(self.data.characters) do
-        if profile[name] and #profile[name] >= 1 then
-          print(' ', #profile[name], key)
-          GI_profileKey = key
-          GI_profile = profile
-          GI_isMine = (profile == self.profile)
-          local results = DoItemList(profile[name], self.sortedItems[name], ptype.OnGetItem)
+        if handler.SortHandler then
+          sort(sortedItems, handler.SortHandler)
         end
 
       end
+      handler.isStale = nil
+      local itemsHeight = handler:UpdateItems()
+      self.currentHeight = max(itemsHeight, self.currentHeight)
 
-      if ptype.SortHandler then
-        sort(self.sortedItems[name], ptype.SortHandler)
-      end
     end
 
-    local itemsHeight = self:RefreshItems(name, ptype)
-    self.currentHeight = max(itemsHeight, self.currentHeight)
+    self.isStale = nil
+    self:Reanchor()
+    self:SetHeight(self.currentHeight + CP_HEADER_SIZE)
   end
 
-  self:Reanchor()
-  self:SetHeight(self.currentHeight)
-  self.isStale = nil
-end
 
-function core:Toggle()
+function ClassPlan:Toggle()
   if self:IsShown() then
     self:Hide()
   else
@@ -484,52 +240,416 @@
   end
 end
 
-function core:OnUpdate()
+function ClassPlan:OnUpdate()
   if self.isStale then
-    print('update requested internally')
-    self:Refresh()
+    print('|cFFFF4400An illusion! What are you hiding?|r')
+    self:Update()
   end
 end
 
-function core:OnShow()
+function ClassPlan:OnShow()
   print('|cFF00FFFFShow()')
   if self.isStale then
-    print('on-show update')
-    self:Refresh()
+    self:Update()
   end
-  ClassPlanButton.Background:Show()
-  ClassPlanButton.Grip:SetShown(true)
-end
-function core:OnHide()
-  print('|cFF00FFFFHide()')
-  ClassPlanButton.Background:Hide()
-  ClassPlanButton.Grip:SetShown(false)
+  self:Reanchor()
 end
 
-
-function core:SortItems(ptype)
-  print('|cFF0088FFSortItems('..tostring(ptype)..')|r')
-  GI_currentTime = time()
-
+function ClassPlan:OnHide()
+  print('|cFF00FFFFHide()')
 end
 
-function MissionsHandler:OnComplete()
-  print('flagging complete', self.name)
-  self:Refresh()
+function ClassPlan:Reanchor()
+  self:ClearAllPoints()
+  self:SetPoint('CENTER', self.data.positionX, self.data.positionY)
+
+  for index, frame in ipairs(self.Handlers) do
+    frame:Reanchor()
+  end
 end
 
-function MissionsHandler:OnUpdate(sinceLast)
-  if self.isComplete then
+function ClassPlan:OnDragStart()
+  self:StartMoving()
+end
+function ClassPlan:OnDragStop()
+
+  self:StopMovingOrSizing()
+  local x,y = self:GetCenter()
+  if x and y then
+    x = (x - GetScreenWidth()/2)
+    y = (y - GetScreenHeight()/2) * -1
+    self.data.positionX, self.data.positionY = x,y
+    print('saving positions:', x, y)
+  end
+end
+
+function SharedHandlers:SetList(index)
+  if not index then
+    if self.currentListIndex == #self.listKey then
+      index = 1
+    else
+      index = self.currentListIndex + 1
+    end
+  end
+
+  print('|cFF0088FF'..self:GetName()..'|r:SetList()', index)
+  self.currentListIndex = index
+  self.activeKey = self.listKey[index]
+  self.activeTitle = self.listTitle[index]
+
+  self.isStale = true
+end
+
+function SharedHandlers:RequestData()
+  print('|cFF0088FF'..self:GetName()..':RequestData()')
+  self.isStale = true
+end
+
+function SharedHandlers:OnEvent(event, arg)
+  if (event == 'GARRISON_MISSION_LIST_UPDATE') and (arg ~= LE_FOLLOWER_TYPE_GARRISON_7_0) then
+    -- ignore non-OrderHall updates
     return
   end
-  self.throttle = (self.throttle or .5) + sinceLast
-  if self.throttle >= .5 then
-    self.throttle = self.throttle - .5
+  print('|cFF00FF88'..self:GetName()..':OnEvent()|r', event, arg)
+  if self:IsVisible() then
+    print('|cFF88FF00  frame visible; get busy')
+    self:RequestData()
   else
-    return
+    if not self.NextData then
+      print('|cFF88FF00  setting timer')
+      self.NextData = C_Timer.NewTimer(0.25, function()
+        if self.initialized then
+          self:RequestData()
+          self.NextData:Cancel()
+          self.NextData = nil
+          print('|cFF88FF00'..self:GetName()..' clearing timer')
+        end
+
+      end)
+    end
+  end
+end
+function SharedHandlers:OnUpdate()
+  if self.isStale then
+    self:GetParent():Update()
+  end
+end
+
+
+-- Stuff set on every list item
+function SharedHandlers:SetOwnerData (self, data)
+  local name, realm = string.match(data.profileKey, "(.+)%-(.+)")
+  local ownerText = '|c'.. data.classColor.colorStr .. name .. '|r'
+  self.Owner:SetText(ownerText)
+  self.Name:SetText(self.name)
+  self.Name:SetTextColor(data.classColor.r, data.classColor.g, data.classColor.b)
+end
+
+function SharedHandlers:UpdateItems()
+  local sortedItems = self.sortedItems
+
+  self.blocks = self.blocks or  {}
+  local blocks = self.blocks
+
+  local lastProfile
+  local numItems = #sortedItems
+  local totalHeight = 0
+  self.lastBlock = nil
+  self.numActive = 0
+  for i, data in ipairs(sortedItems) do
+    local block = blocks[i]
+    if not block then
+      block = CreateFrame('Button', nil, self, self.templateName)
+      block:SetID(i)
+      block.listType = self.activeKey
+      block.handler = self
+      self.numBlocks = self.numBlocks + 1
+      blocks[i] = block
+    end
+
+    print('RefreshItem', block)
+    self.numActive = self.numActive + 1
+
+    if self.lastBlock then
+      block:SetPoint('TOPLEFT', self.lastBlock, 'BOTTOMLEFT', 0, 0)
+      print('--', i, data.isComplete, data.missionEndTime, data.name)
+    else
+      block:SetPoint('TOPLEFT', 0, 0)
+      print('--top')
+    end
+    self.lastBlock = block
+
+    totalHeight = totalHeight + block:GetHeight()
+    block.lastProfile = lastProfile
+    -- blot out arbitrary flags
+    block.offerEndTime = nil
+    block.missionEndTime = nil
+    block.creationTime = nil
+    block.duration = nil
+    block.throttle = 5
+
+    for k,v in pairs(data) do
+      if type(block[k]) ~= 'function' then
+        block[k] = v
+      end
+    end
+
+    block:Update()
+    self:SetOwnerData(block, data)
+
+    block:Show()
+    lastProfile = data.profileKey
   end
 
-  if self.missionEndTime then
+  for i = numItems + 1, self.numBlocks do
+    if blocks[i] then
+      blocks[i]:Hide()
+    end
+  end
+
+  self:Reanchor()
+
+  return totalHeight
+end
+
+
+function ShipmentList:Reanchor()
+  print('|cFF00FFFF'..self:GetName()..':Reanchor|r')
+  self:SetPoint('TOPLEFT', 0, -24)
+  self:SetPoint('BOTTOMRIGHT', -ClassOrderPlan:GetWidth()/2, 0)
+end
+
+function MissionList:Reanchor()
+  self:SetPoint('TOPRIGHT', 0, -24)
+  self:SetPoint('BOTTOMLEFT', ClassOrderPlan:GetWidth()/2, 0)
+
+  self.ListTab:ClearAllPoints()
+  self.ListTab:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, CP_HEADER_SIZE)
+  self.ListTab:SetPoint('BOTTOMRIGHT', self, 'TOPRIGHT', 0, 0)
+  self.ListTab.Label:SetText(self.listTitle[self.currentListIndex])
+  self.ListTab:Show()
+  print(self.ListTab:GetSize())
+end
+
+function MissionList:GetPlayerData (profile)
+  wipe(profile.missions)
+  wipe(profile.available)
+
+  local items = C_Garrison.GetAvailableMissions(GetPrimaryGarrisonFollowerType(LE_GARRISON_TYPE_7_0));
+  for i = 1, #items do
+    if (not items[i].isBuilding and items[i].isZoneSupport) then
+    else
+
+      tinsert(profile.available, items[i])
+    end
+  end
+
+  local items = C_Garrison.GetLandingPageItems(LE_GARRISON_TYPE_7_0)
+  for index, data in ipairs(items) do
+    print(' -',data.name, '|cFF00FF00'.. data.timeLeft .. '|r', date("%A %I:%m %p", data.missionEndTime))
+    tinsert(profile.missions, data)
+  end
+  return true
+end
+
+do
+  local ShipmentsInfo = {}
+  local AddShipmentInfo = function(shipmentType, name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID, followerID)
+    -- early login queries may return empty tables, causing the sorter to compare nil
+    if not creationTime then
+      return
+    end
+    --print(shipmentType, name, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
+    tinsert(ShipmentsInfo,
+      {
+        shipmentType = shipmentType,
+        name = name,
+        icon = texture,
+        shipmentCapacity = shipmentCapacity,
+        shipmentsReady = shipmentsReady,
+        shipmentsTotal = shipmentsTotal,
+        creationTime = creationTime,
+        duration = duration,
+        timeleftString = timeleftString,
+        itemName = itemName,
+        itemIcon = itemIcon,
+        itemQuality = itemQuality,
+        itemID = itemID,
+        followerID = followerID,
+      })
+  end
+  function ShipmentList:GetPlayerData (profile)
+    if not profile then
+      return false
+    end
+    local profileList = profile.shipments
+    wipe(ShipmentsInfo)
+
+    local garrisonType = LE_GARRISON_TYPE_7_0
+    local buildings = CG_GetBuildings(garrisonType);
+    local shipmentIndex = 0
+    --print('Buildings:')
+    for i = 1, #buildings do
+      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID = CG_GetLandingPageShipmentInfo(buildingID);
+      AddShipmentInfo('Building', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, itemName, itemIcon, itemQuality, itemID)
+    end
+
+    --print('Follower:')
+    local followerShipments = CG_GetFollowerShipments(garrisonType);
+    for i = 1, #followerShipments do
+      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, _, _, _, _, followerID = CG_GetLandingPageShipmentInfoByContainerID(followerShipments[i]);
+      AddShipmentInfo('Follower', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString, nil, nil, nil, nil, followerID)
+    end
+
+    --print('Loose:')
+    local looseShipments = CG_GetLooseShipments(garrisonType)
+    for i = 1, #looseShipments do
+      local name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString = CG_GetLandingPageShipmentInfoByContainerID(looseShipments[i]);
+      AddShipmentInfo('Misc', name, texture, shipmentCapacity, shipmentsReady, shipmentsTotal, creationTime, duration, timeleftString)
+    end
+
+    local talentTrees = CG_GetTalentTrees(garrisonType, select(3, UnitClass("player")));
+    -- this is a talent that has completed, but has not been seen in the talent UI yet.
+    local completeTalentID = CG_GetCompleteTalent(garrisonType);
+    --print('Talents:')
+    if (talentTrees) then
+      for treeIndex, tree in ipairs(talentTrees) do
+        for talentIndex, talent in ipairs(tree) do
+          local showTalent = false;
+          if (talent.isBeingResearched) or (talent.id == completeTalentID) then
+            AddShipmentInfo('Talent', talent.name, talent.icon, 1, (talent.isBeingResearched and 0 or 1), 1, talent.researchStartTime, talent.researchDuration, talent.timeleftString)
+          end
+        end
+      end
+    end
+
+    wipe(profileList)
+    for index, data in ipairs(ShipmentsInfo) do
+      --DEFAULT_CHAT_FRAME:AddMessage(data.shipmentType ..' '.. tostring(data.name) ..' '.. tostring(data.creationTime) ..' '.. tostring(data.duration))
+      tinsert(profileList, data)
+    end
+    self.isStale = true
+    return true
+  end
+end
+MissionList.OnGetItem = function(data)
+  if data.missionEndTime and (data.missionEndTime < GI_currentTime) then
+    data.isComplete = true
+  end
+end
+
+MissionList.FreeBlock = function(self, block)
+end
+
+MissionList.SortHandler = function (a,b)
+  local result = false
+  --if not a or not b then
+  --  return true
+  --else
+    --if (a.isMine ~= b.isMine) then
+    --  result = a.isMine
+    --else
+      --if (not b.missionEndTime) or (not a.missionEndTime) then
+      --  print('missing article', b.missionEndTime, a.missionEndTime)
+      --end
+      if b.isComplete ~= a.isComplete then
+        return a.isComplete
+      elseif b.isMine ~= a.isMine then
+        return a.isMine
+      elseif b.missionEndTime then
+        return ( b.missionEndTime > a.missionEndTime)
+      else
+        return ( b.offerEndTime > a.offerEndTime)
+      end
+
+    --end
+  --end
+end
+
+
+function MissionList:OnShow()
+  print('|cFF00FF88'..self:GetName()..':OnShow()|r')
+end
+
+function ShipmentList:OnLoad()
+  C_Garrison.RequestLandingPageShipmentInfo();
+end
+function  ShipmentList:OnShow()
+  print('|cFF00FF88'..self:GetName()..':OnShow()|r')
+  C_Garrison.RequestLandingPageShipmentInfo()
+end
+
+-- Update shipment flags data
+local SetActualShipmentTime = function(self)
+
+  if self.isComplete then
+    print('|cFF00FF88isComplete '..self.name..'|r')
+    return true
+  end
+
+  local timestamp = time()
+  local timeLeft = self.creationTime + self.duration - timestamp
+  local justFinished = false
+  while (self.shipmentsReady < self.shipmentsTotal) and (timeLeft <= 0) do
+    if not self.originalReady then
+      self.originalReady = self.shipmentsReady
+      self.originalCreationTime = self.creationTime
+    end
+
+
+    self.shipmentsReady = self.shipmentsReady + 1
+    self.creationTime = self.creationTime + self.duration
+    timeLeft = timeLeft + self.duration
+    print('|cFF00FF88udpating '..self.name..'|r', 'timeLeft:', timeLeft, 'shipments:', self.shipmentsReady, self.shipmentsTotal)
+  end
+
+  if (timeLeft <= 0) and (not self.isBeingResearched) then
+    self.isComplete = true
+    self.isStale = true
+  end
+
+  return timeLeft
+end
+
+ShipmentList.OnGetItem = function(data)
+  print('OnGetItem()')
+  if data.shipmentsTotal then
+    SetActualShipmentTime(data)
+  end
+end
+
+ShipmentList.SortHandler = function(a, b)
+  if b.isComplete ~= a.isComplete then
+    return a.isComplete and true or false
+  elseif a.shipmentsReady or b.shipmentsReady then
+    return (a.shipmentsReady or 0) > (b.shipmentsReady or 0)
+  else
+    return (a.creationTime) < (b.creationTime)
+  end
+end
+
+
+function MissionEntry:OnComplete()
+  print('flagging complete', self.name)
+  self:Update()
+end
+
+function MissionEntry:OnUpdate(sinceLast)
+  self.throttle = (self.throttle or .5) + sinceLast
+  if self.throttle < .5 then
+    return
+  else
+    self.throttle = self.throttle - .5
+  end
+  if self.offerEndTime then
+    local timeLeft = self.offerEndTime
+    self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
+    self.TimeLeft:SetTextColor(1,1,1)
+  elseif self.missionEndTime then
+
+    if self.isComplete then
+      return
+    end
     local timeLeft = self.missionEndTime - time()
     if timeLeft < 0 then
       self:OnComplete()
@@ -557,7 +677,7 @@
           g = min(progress * 2, 1)
           r = 1
         end
-        self.ProgressBar:SetColorTexture(r,g,0,1)
+        self.ProgressBar:SetColorTexture(r,g,0,.4)
         self.ProgressBG:SetColorTexture(r,g,0,0.125)
       end
       self.ProgressBG:Show()
@@ -571,7 +691,18 @@
   end
 end
 
-function MissionsHandler:Refresh()
+function MissionEntry:OnLoad()
+  print('|cFFFF4400',self:GetName() or tostring(self), 'onload')
+  self.Count = self.Overlay.Count
+  self.Name = self.Overlay.Name
+  self.TimeLeft = self.Overlay.TimeLeft
+  self.Owner = self.Overlay.Owner
+
+  self.Icon:SetDesaturated(false)
+  self.Done:Hide()
+end
+
+function MissionEntry:Update()
   local r,g,b = 1, 1, 1
   if self.isRare then
     r,g,b = 0.1, 0.4, 1
@@ -587,11 +718,7 @@
     self.Icon:SetAtlas(self.typeAtlas, false)
   end
 
-  if self.isComplete then
-    self.Done:Show()
-  else
-    self.Done:Hide()
-  end
+
 
   if self.isComplete then
     self.TimeLeft:SetText('Complete!')
@@ -601,7 +728,7 @@
   end
 end
 
-function MissionsHandler:OnEnter()
+function MissionEntry:OnEnter()
   if self.rewardInfo and self.rewardInfo.itemID then
     GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
     GameTooltip:SetItemByID(self.rewardInfo.itemID)
@@ -609,61 +736,46 @@
   end
 end
 
-function MissionsHandler:OnLeave()
+function MissionEntry:OnLeave()
   if GameTooltip:IsOwned(self) then
     GameTooltip:Hide()
   end
 end
 
-function ShipmentsHandler:Refresh()
-  --[[
-  self.icon = data.icon
-  self.shipmentCapacity = data.shipmentCapacity
-  self.shipmentsReady = data.shipmentsReady
-  self.shipmentsTotal = data.shipmentsTotal
-  self.creationTime = data.creationTime
-  self.duration = data.duration
-  self.itemID = data.itemID
-  self.itemQuality = data.itemQuality
-      icon = texture,
-      shipmentCapacity = shipmentCapacity,
-      shipmentsReady = shipmentsReady,
-      shipmentsTotal = shipmentsTotal,
-      creationTime = creationTime,
-      duration = duration,
-      timeleftString = timeleftString,
-      itemName = itemName,
-      itemIcon = itemIcon,
-      itemQuality = itemQuality,
-      itemID = itemID
+function ShipmentEntry:OnLoad()
+  MissionEntry.OnLoad(self)
+end
 
-  --]]
+
+function ShipmentEntry:Update()
+  print('|cFF0088FF'.. self.name..'|r:Update()')
   self.Icon:SetTexture(self.icon)
-  self.Name:SetText(self.name)
   self.Count:SetText(self.shipmentsReady)
   self.Done:SetShown(self.shipmentsReady and (self.shipmentsReady >= 1))
 
   -- flag as complete
+
+  local bgColor = CP_BACKGROUND_COLOR.inProgress
   if ( self.shipmentsReady >= self.shipmentsTotal ) and (not self.isBeingResearched) then
     self.Swipe:SetCooldownUNIX(0, 0);
     self.Done:Show();
-    self.isComplete = true
+    bgColor = CP_BACKGROUND_COLOR.complete
   else
+    if (self.shipmentsReady >= 1) and (self.shipmentsReady < self.shipmentsTotal) then
+      bgColor = CP_BACKGROUND_COLOR.shipmentsReady
+    end
     self.Swipe:SetCooldownUNIX(self.creationTime or 0 , self.duration or 0);
   end
+  self.Background:SetColorTexture(unpack(bgColor))
 
-  if self.isComplete then
-    self.TimeLeft:SetText('Complete!')
-    self.Background:SetColorTexture(0.5,0.5,0.5)
-  elseif (self.shipmentsReady and (self.shipmentsReady > 0)) then
-    self.Background:SetColorTexture(0.5,0.5,0.5,.5)
-  else
-    self.Background:SetColorTexture(0,0,0,0.5)
+  SetActualShipmentTime(self)
+
+  if self.originalReady then
+    print('|cFF00FF88'..self.name..'|r', 'starting ready:', self.originalReady, 'starting time:', self.originalCreationTime)
   end
 end
 
-local time = time
-function ShipmentsHandler:OnUpdate(sinceLast)
+function ShipmentEntry:OnUpdate(sinceLast)
   self.throttle = (self.throttle or 1) + sinceLast
   if self.throttle >= 1 then
     self.throttle = self.throttle - 1
@@ -671,22 +783,21 @@
     return
   end
 
+
   if (self.shipmentsReady and self.shipmentsTotal) and (self.shipmentsReady < self.shipmentsTotal) then
-    local timeLeft = self.creationTime + self.duration - time()
-    if self.shipmentsReady >= 1 then
+    local timeLeft = SetActualShipmentTime(self)
+
+    if self.isComplete then
+      self.TimeLeft:SetText('Complete!')
+      self.TimeLeft:SetTextColor(0,0.5,1)
+    elseif self.shipmentsReady >= 1 then
       self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
       self.TimeLeft:SetTextColor(0,1,0)
     else
       self.TimeLeft:SetText(GetTimeLeftString(timeLeft))
       self.TimeLeft:SetTextColor(1,1,1)
     end
-    if (timeLeft < 0) then
-      if self.shipmentsReady < self.shipmentsTotal then
-        self.shipmentsReady = self.shipmentsReady + 1
-        self.creationTime = self.creationTime + self.duration
-        -- text will be set on next update
-      end
-    end
+
   elseif self.isBeingResearched then
     self.TimeLeft:SetText(GetTimeLeftString(self.researchStartTime + self.researchDuration - time()))
     self.TimeLeft:SetTextColor(1,1,0)
@@ -712,7 +823,7 @@
       g = min(progress * 2, 1)
       r = 1
     end
-    self.ProgressBar:SetColorTexture(r, g, 0, 1)
+    self.ProgressBar:SetColorTexture(r, g, 0, .4)
     self.ProgressBG:SetColorTexture(r, g, 0, .125)
     self.ProgressBG:Show()
     self.ProgressBar:Show()
@@ -722,7 +833,7 @@
   end
 end
 
-function ShipmentsHandler:OnEnter()
+function ShipmentEntry:OnEnter()
   if ( self.shipmentsReady and self.shipmentsTotal ) then
     GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
     GameTooltip:AddLine(self.Owner:GetText(), self.Owner:GetTextColor())
@@ -732,14 +843,27 @@
   end
 end
 
-function ShipmentsHandler:OnLeave()
+function ShipmentEntry:OnLeave()
   if GameTooltip:IsOwned(self) then
     GameTooltip:Hide()
   end
 end
 
-function ShipmentsHandler:OnClick(button)
+function ShipmentEntry:OnClick(button)
   if button == 'RightButton' then
     self.handler:FreeBlock(self)
   end
-end
\ No newline at end of file
+end
+
+ClassPlanMissionHandler = Mixin(MissionList, SharedHandlers)
+ClassPlanShipmentHandler = Mixin(ShipmentList, SharedHandlers)
+ClassPlanMissionEntryMixin = Mixin(MissionEntry, SharedEntry)
+ClassPlanShipmentEntryMixin = Mixin(ShipmentEntry,SharedEntry)
+
+ClassPlanHeaderMixin = {
+  OnClick = function(self)
+    self:GetParent():SetList()
+    self:GetParent().isStale = true
+    ClassOrderPlan:Update()
+  end
+}
\ No newline at end of file
--- a/ClassPlan.xml	Fri Oct 28 19:54:00 2016 -0400
+++ b/ClassPlan.xml	Tue Nov 01 10:48:50 2016 -0400
@@ -6,47 +6,6 @@
   <Font name="ClassPlanFont" font="Interface\AddOns\WorldPlan\Font\ArchivoNarrow-Regular.ttf" height="14" outline="NORMAL" virtual="true" />
   <Font name="ClassPlanNumberFont" font="Interface\AddOns\WorldPlan\Font\ArchivoNarrow-Bold.ttf" height="14" outline="NORMAL" virtual="true" />
 
-  <Button name="ClassPlanButton" parent="UIParent" frameStrata="HIGH">
-    <Size x="600" y="16" />
-    <Anchors>
-      <Anchor point="TOP" relativePoint="BOTTOM" relativeTo="ClassOrderPlan" />
-      <Anchor point="LEFT" relativeTo="ClassOrderPlan" />
-      <Anchor point="RIGHT" relativeTo="ClassOrderPlan" />
-    </Anchors>
-    <Layers>
-      <Layer level="BACKGROUND">
-        <Texture setAllPoints="true" parentKey="Background" hidden="true">
-          <Color a="1" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-      <Layer level="ARTWORK">
-
-        <Texture parentKey="Grip" alphaMode="BLEND" file="Interface\RaidFrame\Raid-Move-Down" hidden="true">
-          <Anchors>
-            <Anchor point="TOP" />
-          </Anchors>
-          <Size x="32" y="16"/>
-        </Texture>
-      </Layer>
-      <Layer level="OVERLAY">
-        <FontString parentKey="Label" inherits="ClassPlanFont">
-          <Anchors>
-            <Anchor point="BOTTOMLEFT" x="2" y="2" />
-          </Anchors>
-        </FontString>
-      </Layer>
-      <Layer level="HIGHLIGHT">
-        <Texture setAllPoints="true" alphaMode="ADD">
-          <Color a="0.25" r="1" g="1" b="1" />
-        </Texture>
-      </Layer>
-    </Layers>
-    <Scripts>
-      <OnClick>
-        ClassOrderPlan:Toggle()
-      </OnClick>
-    </Scripts>
-  </Button>
 
   <Frame name="ClassOrderPlan" mixin="ClassOrderPlanCore" parent="UIParent" hidden="true">
     <Size x="600" y="40" />
@@ -76,16 +35,59 @@
         </Texture>
       </Layer>
     </Layers>
+    <Frames>
+      <Frame name="$parentMissionList" parentKey="MissionList" parentArray="Handlers" mixin="ClassPlanMissionHandler">
+        <Scripts>
+          <OnLoad method="OnLoad" />
+          <OnUpdate method="OnUpdate" />
+          <OnEvent method="OnEvent" />
+          <OnShow method="OnShow" />
+        </Scripts>
+        <Frames>
+          <Button name="$parentTab" parentKey="ListTab" mixin="ClassPlanHeaderMixin">
+            <Scripts>
+              <OnClick method="OnClick" />
+            </Scripts>
+            <Size y="24" />
+            <Layers>
+              <Layer level="BACKGROUND">
+                <Texture parentKey="Background">
+                  <Color a="1" r="0" g="0" b="0" />
+                </Texture>
+              </Layer>
+              <Layer level="OVERLAY">
+                <FontString parentKey="Label" inherits="ClassPlanFont">
+                  <Anchors>
+                    <Anchor point="CENTER" />
+                  </Anchors>
+                </FontString>
+              </Layer>
+              <Layer level="HIGHLIGHT">
+                <Texture parentKey="Highlight">
+                  <Color a="0.5" r="1" g="1" b="1" />
+                </Texture>
+              </Layer>
+            </Layers>
+
+          </Button>
+        </Frames>
+      </Frame>
+      <Frame name="$parentShipments" parentKey="Shipments" parentArray="Handlers" mixin="ClassPlanShipmentHandler">
+        <Scripts>
+          <OnLoad method="OnLoad" />
+          <OnUpdate method="OnUpdate" />
+          <OnEvent method="OnEvent" />
+          <OnShow method="OnShow" />
+        </Scripts>
+      </Frame>
+    </Frames>
   </Frame>
 
+
+
   <Button name="ClassPlanListEntryTemplate" virtual="true" hidden="true">
     <Scripts>
-      <OnLoad>
-        self.Count = self.Overlay.Count
-        self.Name = self.Overlay.Name
-        self.TimeLeft = self.Overlay.TimeLeft
-        self.Owner = self.Overlay.Owner
-      </OnLoad>
+      <OnLoad method="OnLoad" />
       <OnClick method="OnClick" />
       <OnUpdate method="OnUpdate" />
       <OnShow method="OnShow" />
@@ -190,11 +192,18 @@
   </Button>
 
 
-  <Button name="ClassPlanMissionEntry" inherits="ClassPlanListEntryTemplate" mixin="ClassPlanMissionMixin" virtual="true">
-    <Size x="300" y="24" />
+  <Button name="ClassPlanMissionEntry" inherits="ClassPlanListEntryTemplate" mixin="ClassPlanMissionEntryMixin" virtual="true">
+    <Size y="24" />
+    <Anchors>
+      <Anchor point="RIGHT" />
+    </Anchors>
   </Button>
 
-  <Button name="ClassPlanShipmentEntry" inherits="ClassPlanListEntryTemplate" mixin="ClassPlanShipmentMixin" virtual="true">
-    <Size x="300" y="24" />
+  <Button name="ClassPlanShipmentEntry" inherits="ClassPlanListEntryTemplate" mixin="ClassPlanShipmentEntryMixin" virtual="true">
+    <Size y="24" />
+
+    <Anchors>
+      <Anchor point="RIGHT" />
+    </Anchors>
   </Button>
 </Ui>
\ No newline at end of file
--- a/FilterBar.lua	Fri Oct 28 19:54:00 2016 -0400
+++ b/FilterBar.lua	Tue Nov 01 10:48:50 2016 -0400
@@ -68,6 +68,7 @@
 
 
 function WorldPlanSummaryMixin:OnEvent(event)
+  self:Refresh()
 end
 
 local bountyIndex
@@ -81,6 +82,11 @@
 function WorldPlanSummaryMixin:OnEvent(event,...)
   self.isStale = true
 end
+function WorldPlanSummaryMixin:OnUpdate()
+  if self.isStale then
+    self:Refresh()
+  end
+end
 
 function WorldPlanSummaryMixin:OnShow()
   self:Refresh()
@@ -196,6 +202,7 @@
       wipe(questResults[i])
     end
   end
+  self.isStale = nil
 end
 
 function WorldPlanSummaryMixin:Cleanup()
@@ -213,7 +220,7 @@
       GameTooltip:AddLine(pin.title ..(pin.cheevos and " |cFFFFFF00!|R" or ''), colorInfo.r, colorInfo.g, colorInfo.b)
       GameTooltip:AddTexture(pin.itemTexture)
       local cLine = GameTooltip:NumLines() - 1
-      print(cLine, _G['GameTooltipTexture'..cLine]:GetTexture())
+      --print(cLine, _G['GameTooltipTexture'..cLine]:GetTexture())
       if type(pin.itemTexture) == 'number' then
         --- voodoo workaround for IDs getting coerced to string
         _G['GameTooltipTexture'..cLine]:Show()
--- a/WorldPlan.lua	Fri Oct 28 19:54:00 2016 -0400
+++ b/WorldPlan.lua	Tue Nov 01 10:48:50 2016 -0400
@@ -188,7 +188,7 @@
       WorldPlanData[key] = true
     end
   end
-  WorldPlan:RefreshAll()
+  WorldPlan:Refresh()
 end
 
 function WorldPlan:print(...)
@@ -897,7 +897,7 @@
   print('  |cFFFF4400IsShowable()|r', self.title)
 
   self.questId  = self:GetID()
-  if not (WorldMap_DoesWorldQuestInfoPassFilters(self)) then
+  if not (WorldMap_DoesWorldQuestInfoPassFilters(self, false, true)) then
     self.filtered = true
   end