changeset 1:b0447b382f36

sorting client events from maintenance logic implemented all that action buttons entail for quest items
author Nenue
date Wed, 30 Mar 2016 16:30:49 -0400
parents 3dbcad2b387d
children a2396b03ce63
files BuffButton.lua ObjectiveEvents.lua ObjectiveFrame.lua ObjectiveInfo.lua ObjectiveSecure.xml ObjectiveTracker.lua ObjectiveTracker.xml ObjectiveUI.lua
diffstat 8 files changed, 766 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/BuffButton.lua	Wed Mar 30 02:24:56 2016 -0400
+++ b/BuffButton.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -159,7 +159,7 @@
     -- Background decorations layer
     if not d[i] then
       d[i] = CreateFrame('Frame', buffName..i..'Decor', _G.UIParent, 'VeneerDecorTemplate')
-      RegisterStateDriver(d[i], "visibility", "[petbattle] [vehicleui] hide")
+      --RegisterStateDriver(d[i], "visibility", "[petbattle] [vehicleui] hide")
     end
 
     d[i]:SetPoint('BOTTOMLEFT', guide.icon, 'BOTTOMLEFT', -buffBorder, -buffBorder)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveEvents.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -0,0 +1,32 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 3/30/2016 1:23 AM
+local B = select(2,...).frame
+local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('ObjectiveEvent')
+
+--------------------------------------------------------------------
+--- Specialized event handlers
+--------------------------------------------------------------------
+print(mod:GetName())
+mod.OnQuestAccepted = function(_, questLogIndex, questID)
+  AddQuestWatch(questLogIndex)
+  SetSuperTrackedQuestID(questID)
+end
+
+mod.OnQuestComplete = function(_, questLogIndex, questID)
+  QuestPOIUpdateIcons()
+end
+
+mod.OnQuestFinished = function(_, questLogIndex, questID)
+  mod.TrackClosest()
+  RemoveQuestWatch(questLogIndex)
+end
+
+mod.OnQuestFromLocation = function(event) end
+
+mod.OnAddQuestWatch = function(questID)
+  mod.UpdateActionButtons()
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveFrame.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -0,0 +1,498 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 3/30/2016 12:49 AM
+local B = select(2,...).frame
+local ipairs, max, unpack = ipairs, max, unpack
+local CreateFrame = CreateFrame
+local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('Objectives')
+--------------------------------------------------------------------
+--- Global frame layout
+--------------------------------------------------------------------
+
+--- Upvalues
+local Wrapper = _G.VeneerObjectiveWrapper
+local Scroller = Wrapper.scrollArea
+local Scroll = _G.VeneerObjectiveScroll
+local orderedHandlers = mod.orderedHandlers
+local orderedNames = mod.orderedNames
+
+--- Temp values set during updates
+local wrapperWidth, wrapperHeight
+local scrollWidth, scrollHeight
+local previousBlock
+local currentBlock
+--- todo: map these into config table when its sorted out
+local titleFont, textFont = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Regular.ttf]]
+local titleSize, textSize = 15, 15
+local titleOutline, textOutline = "OUTLINE", "OUTLINE"
+local titleSpacing, textSpacing = 4, 3
+local textIndent = 5
+local wrapperMaxWidth, wrapperMaxHeight = 280, 490 -- these are the hard bounds, actual *Height variables are changed
+local headerFont, headerSize, headerHeight = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 18, 24
+local headerOutline, headerColor = 'OUTLINE', {1,1,1,1}
+local wrapperPosition = {'RIGHT', UIParent, 'RIGHT', -84, 0}
+
+local Scroller_OnShow = function()
+  Wrapper.watchMoneyReasons = 0;
+  mod.UpdateWrapper()
+end
+
+local Scroller_OnHide = function()
+  local self = Wrapper
+  Wrapper:UnregisterAllEvents()
+  Wrapper:SetScript('OnEvent', nil)
+  Wrapper.Background:Hide()
+  Wrapper.BackgroundR:Hide()
+end
+
+local Scroller_OnMouseWheel = function(self, delta)
+  local r = Scroll:GetHeight() - Scroller:GetHeight()
+  local s = self:GetVerticalScroll() - delta * floor(r/5+.5)
+  if r == 0 then return end
+  if s >= r then
+    s = r
+  elseif s < 1 then
+    s = 0
+  end
+  self:SetVerticalScroll(s)
+  print(s, r, self:GetVerticalScroll())
+
+  mod.UpdateActionButtons()
+end
+
+local WrapperCloseButton_OnClick = function(self)
+  if Scroller:IsVisible() then
+    Scroller:Hide()
+  else
+    Scroller:Show()
+  end
+end
+
+
+mod.InitializeTrackers = function()
+  for i, name in ipairs(orderedNames) do
+    if not mod.orderedHandlers[i] then
+      if mod.Tracker(name, i) then
+        local handler = mod[name]
+
+        local tracker = CreateFrame('Frame', 'Veneer'..name..'Tracker', Scroll, 'VeneerTrackerTemplate')
+        tracker.header:SetText(handler.name)
+        tracker.header:SetHeight(headerHeight)
+        tracker.header:SetFont(headerFont, headerSize, headerOutline)
+        tracker.header:SetTextColor(unpack(headerColor))
+
+        handler.Tracker = tracker
+        mod.orderedTrackers[i] = tracker
+        mod.namedTrackers[name] = tracker
+        mod.indexedTrackers[handler] = tracker
+        print('created new tracker frames for new handler')
+      end
+    end
+  end
+
+  Scroller:SetScrollChild(Scroll)
+  Scroller:SetScript('OnMouseWheel', Scroller_OnMouseWheel)
+  Scroller:SetScript('OnShow', Scroller_OnShow)
+  Scroller:SetScript('OnHide', Scroller_OnHide)
+  Wrapper.close:SetScript('OnClick', WrapperCloseButton_OnClick)
+  Wrapper:SetPoint(unpack(wrapperPosition))
+  Scroller_OnShow(Scroller)
+end
+
+mod.defaults = {
+  ObjectiveTrackerAnchor = {'BOTTOM', 'RIGHT'},
+  ObjectiveTrackerParent = 'DebuffButton',
+  ObjectiveTrackerSize = {250, 600},
+  ObjectiveWrapperParent = '',
+  ObjectiveTrackerStyle = {
+    Normal = {
+      Title = {
+        Gradient = { MinColor = {0.2, .4, 1, 0.45}, MaxColor = {.7, 0, 0.9, 0}},
+        Font = {titleFont, titleSize, titleOutline}, Spacing = titleSpacing,
+      },
+      Text = {
+        Gradient = { MinColor = {0.2, .4, 1, 0.25}, MaxColor = {.7, 0, 0.9, 0}},
+        Font = {textFont, textSize, textOutline}, Spacing = textSpacing,
+      },
+    },
+    Super = {
+      Title = {
+        Gradient = { MinColor = {0, .7, .6, .8}, MaxColor = {0, .7, .6, 0.2}},
+        Font = {titleFont, titleSize, titleOutline},
+        Spacing = titleSpacing, BackgroundFullWidth = true
+      },
+      Text = {
+        Gradient = { MinColor = {0, .7, .6, 0.5}, MaxColor = {0, .7, .6, 0.1} },
+        Font = {textFont, textSize, textOutline}, Spacing = textSpacing,
+      },
+    },
+    Active = {
+      Title = {
+        Gradient = {          MinColor = {0.2, .4, 1, 1}, MaxColor = {0.2, .4, 1, 1},        },
+        Font = {titleFont, titleSize, titleOutline},
+        Spacing = titleSpacing,
+        BackgroundFullWidth = true
+      },
+      Text = {
+        Gradient = {          MinColor = {0.2, .4, 1, 1}, MaxColor = {0.2, .4, 1, 1},        },
+        Font = {textFont, textSize, textOutline},
+        Spacing = textSpacing,
+        BackgroundFullWidth = true
+      }
+    },
+    Complete = {
+      Title = {
+        Gradient = {          MinColor = {0, 1, 0, 0.34},          MaxColor = {0, 1, 0, .17},        },
+        Font = {titleFont, titleSize, titleOutline},        Spacing = titleSpacing,
+        BackgroundFullWidth = true
+      },
+      Text = {
+        Gradient = {          MinColor = {0, 1, 0, .25},          MaxColor = {0, 1, 0, 0.12},        },
+        Font = {textFont, textSize, textOutline},        Spacing = textSpacing,
+        BackgroundFullWidth = true
+      }
+    },
+  }
+}
+
+
+--- Argument containers
+local a1, a2, a3, a4, b1, b2, b3, b4, f1, f2, f3, w1, w2
+mod.SetBlockStyle = function(block, name)
+  -- var names intended to reflect argument order
+  local c = mod.defaults.ObjectiveTrackerStyle[name]
+  a1, a2, a3, a4 = unpack(c.Title.Gradient.MinColor)
+  b1, b2, b3, b4 = unpack(c.Title.Gradient.MaxColor)
+  block.titlebg:SetGradientAlpha('HORIZONTAL', a1, a2, a3, a4, b1, b2, b3, b4)
+
+  a1, a2, a3, a4 = unpack(c.Text.Gradient.MinColor)
+  b1, b2, b3, b4 = unpack(c.Text.Gradient.MaxColor)
+  block.bg:SetGradientAlpha('HORIZONTAL', a1, a2, a3, a4, b1, b2, b3, b4)
+
+  f1, f2, f3 = unpack(c.Title.Font)
+  block.title:SetFont(f1, f2, f3)
+
+  f1, f2 ,f3 = unpack(c.Text.Font)
+  block.objectives:SetFont(f1,f2,f3)
+
+  w1 = Wrapper:GetWidth()
+  w2 = (c.Title.BackgroundFullWidth and w1 or  block.title:GetStringWidth())
+
+  local titleSpacing, titleSpacing2 = c.Title.Spacing, (c.Title.Spacing * 2)
+  local textSpacing, textSpacing2 = c.Text.Spacing, (c.Text.Spacing * 2)
+
+  if block.info.isTrivial then
+    block.title:SetTextColor(0.7, 0.7, 0.7, 1)
+  elseif block.info.isComplete then
+    block.title:SetTextColor(1,1,1,1)
+  else
+    block.title:SetTextColor(0,.7,1,1)
+  end
+  block.title:SetSpacing(titleSpacing)
+  block.objectives:SetSpacing(textSpacing)
+  block.objectives:SetWordWrap(true)
+
+  local titleHeight, textHeight = block.title:GetStringHeight(), block.objectives:GetStringHeight()
+  local blockHeight = titleHeight + titleSpacing2 + textHeight + textSpacing2
+  local blockWidth = wrapperMaxWidth
+
+  block.titlebg:SetSize(min(w1, w2), titleHeight + titleSpacing2)
+  block.bg:SetSize(w1, textHeight + textSpacing2)
+  block:SetSize(blockWidth, blockHeight)
+
+  block.title:SetPoint('TOPLEFT', block.titlebg, 'TOPLEFT', 0, -titleSpacing)
+  block.objectives:SetPoint('TOPLEFT', block.titlebg, 'BOTTOMLEFT', textIndent, -textSpacing)
+
+  -- store
+  block.titleHeight = titleHeight
+  block.textHeight = textHeight
+  block.width = blockWidth
+  block.height = blockHeight
+
+  print('|cFF88DDFFApplyStyle(', block:GetName(), ')|r', blockWidth, 'x', blockHeight, '(textH', textHeight,', titleH', titleHeight, ')')
+end
+
+--- Updates the selected block frame to display the given info batch
+-- If `previousBlock` is set, it will attempt to anchor to that
+-- @param blockNum the ordered block to be updated, not a watchIndex value
+-- @param info the reference returned by the GetXInfo functions
+-- REMEMBER: t.info and questData[questID] are the same table
+mod.UpdateTrackerBlock = function (handler, blockIndex, info)
+  print('|cFF00FFFFUpdateTrackerBlock('..blockIndex..'|r')
+  if not blockIndex or not info then
+    return
+  end
+
+  local tracker = handler.Tracker
+
+  local t = handler:GetBlock(blockIndex)
+  if previousBlock then
+    if blockIndex == 1 then
+      t:SetPoint('TOPLEFT', previousBlock, 'TOPLEFT', 0, -headerHeight)
+    else
+      t:SetPoint('TOPLEFT', previousBlock, 'BOTTOMLEFT', 0, 0)
+    end
+    t:SetPoint('RIGHT', tracker,'RIGHT', 0, 0)
+  end
+  print(t:GetName(), t:GetSize())
+  print(t:GetPoint(1))
+
+  t.info = info
+
+  if info.questLogIndex then handler.LogBlock[info.questLogIndex] = t end
+  if info.watchIndex then handler.WatchBlock[info.watchIndex] = t end
+
+  info.blockIndex = blockIndex
+  handler.BlockInfo[blockIndex] = info
+  t.Select = handler.Select
+  t.Open = handler.Open
+  t.Remove = handler.Remove
+  t.Link = handler.Link
+  t:SetScript('OnMouseUp', handler.OnMouseUp)
+  t:SetScript('OnMouseDown', handler.OnMouseDown)
+  t.title:SetText(info.title)
+
+  if info.isComplete then
+    t.objectives:Show()
+    t.objectives:SetText(info.completionText)
+  elseif info.numObjectives >= 1 then
+    t.objectives:Show()
+    print('objective lines:', info.numObjectives, 'can wrap:', t.objectives:CanWordWrap())
+    local text = ''
+    for o, obj in ipairs(t.info.objectives) do
+      local line = obj.text
+      if obj.type == 'monster' then
+        line = '|cFFFFFF00' .. line .. '|r'
+      elseif obj.type == 'item' then
+        line = '|cFF44BBFF' .. line .. '|r'
+      elseif obj.type == 'object' then
+        line = '|cFFFFFFFF' .. line .. '|r'
+      end
+      text = text .. ((text == '') and "" or "\n") .. line
+    end
+    t.objectives:SetText(text)
+
+
+    -- todo: set up a SecureActionButton template
+    if info.specialItem then
+      print('|cFF00FFFFretrieve item button')
+      mod.SetItemButton(t, info)
+    elseif t.itemButton then
+      print('|cFF00FF88drop item button')
+      mod.FreeItemButton(t, info)
+    end
+
+
+  elseif info.description then
+    t.objectives:SetText(info.description)
+    t.objectives:SetWordWrap(true)
+  else
+    t.objectives:SetText(nil)
+  end
+  local style = 'Normal'
+  if info.isComplete then
+    style = 'Complete'
+  elseif info.superTracked then
+    style = 'Super'
+  end
+
+  t:SetStyle(style)
+
+
+  local fullheight = t:GetHeight()
+  t:Show()
+  print('|cFF00FFFF)|r -> ', t, t:GetHeight(), fullheight)
+  return t
+end
+
+mod.UpdateTracker = function(handler)
+  print('|cFF00FF88UpdateTracker(|r|cFFFF4400' .. type(handler) .. '|r :: |cFF88FFFF' .. tostring(handler) .. '|r')
+  local tracker = handler.Tracker
+  local blockIndex = 0
+  local trackerHeight = headerHeight
+  local w = 300
+
+  previousBlock = handler.Tracker
+  local numWatched = handler.GetNumWatched()
+  local numBlocks = handler.numBlocks
+  local actualBlocks = handler.actualBlocks
+  for watchIndex = 1, 25 do
+    blockIndex = blockIndex + 1
+    if watchIndex <= numWatched then
+      local info = handler:GetInfo(watchIndex)
+      if info then
+        local currentBlock = mod.UpdateTrackerBlock(handler, blockIndex, info)
+        previousBlock = currentBlock
+        print('adding ', currentBlock.height)
+        trackerHeight = trackerHeight + currentBlock.height
+        numBlocks = max(numBlocks, watchIndex)
+        actualBlocks = actualBlocks + 1
+      else
+        print('|cFFFF0000Failed to draw info for index #'..watchIndex)
+      end
+
+    elseif watchIndex <= numBlocks then
+      local used = handler.usedBlocks
+      local free = handler.freeBlocks
+      print('clean up dead quest block')
+      if used[blockIndex] then
+        used[blockIndex]:Hide()
+        used[blockIndex]:ClearAllPoints()
+        free[#free+1]= used[blockIndex]
+        used[blockIndex] = nil
+      end
+    else
+      print('Stopping scan at', blockIndex)
+      break -- done with quest stuff
+    end
+  end
+  handler.numWatched = numWatched
+  handler.numBlocks = numBlocks
+  handler.actualBlocks = actualBlocks
+  handler:Report()
+  previousBlock = nil
+  if numBlocks > 0 then
+    tracker.height = trackerHeight
+  else
+    tracker.height = 0
+  end
+
+  print('|cFF00FF88)|r ->', numBlocks, 'blocks; height', tracker.height, 'last block: ')
+end
+
+mod.Quest.numButtons = 0
+local usedButtons = mod.Quest.itemButtons
+local freeButtons = mod.Quest.freeButtons
+mod.UpdateWrapper = function()
+  wrapperWidth = wrapperMaxWidth
+  scrollWidth = wrapperWidth
+  local wrapperBlocks = 0
+  -- Update scroll child vertical size
+  scrollHeight = 0
+  for i, handler in ipairs(orderedHandlers) do
+    mod.UpdateTracker(handler)
+    if handler.actualBlocks >= 1 then
+      local tracker = handler.Tracker
+      print('setting', handler.Tracker, 'to anchor to offset', -scrollHeight)
+      tracker:SetParent(Scroll) -- this doesn't do anything that relativeTo doesn't
+      tracker:SetPoint('TOPLEFT', Scroll, 'TOPLEFT', 0, - scrollHeight)
+      tracker:SetSize(wrapperWidth, tracker.height)
+      print('adding ', tracker.height)
+      scrollHeight = scrollHeight + tracker.height
+    end
+    wrapperBlocks = wrapperBlocks + handler.actualBlocks
+  end
+  print('final scrollHeight:', scrollHeight)
+
+
+
+  -- Update frame dimensions
+  if scrollHeight > wrapperMaxHeight then
+    print('  is larger than', wrapperMaxHeight)
+    --ScrollBar:Show()
+    --scrollWidth = wrapperMaxWidth - scrollBarWidth
+    wrapperHeight = wrapperMaxHeight
+    -- Make ThumbTexture reflect the viewing scale (smaller for longer scroll, bigger for shorter)
+    --ScrollBar:GetThumbTexture():SetHeight((wrapperMaxHeight/scrollHeight) * (wrapperMaxHeight))
+    --ScrollBar:SetWidth(scrollBarWidth)
+    --ScrollBar:SetPoint('TOPRIGHT', Scroller, 'TOPRIGHT', 0, 0)
+    --ScrollBar:SetPoint('BOTTOMLEFT', Scroller, 'BOTTOMRIGHT', -scrollBarWidth, 0)
+    --ScrollBar:SetMinMaxValues(1, scrollHeight - wrapperMaxHeight)
+  else
+    --ScrollBar:Hide()
+    wrapperHeight = scrollHeight
+  end
+  scrollWidth = floor(scrollWidth+.5)
+  scrollHeight = floor(scrollHeight+.5)
+  wrapperWidth = floor(wrapperWidth+.5)
+  wrapperHeight = floor(wrapperHeight+.5)
+  headerHeight = floor(headerHeight+.5)
+
+  if wrapperBlocks >= 1 then
+    Wrapper.Background:Show()
+    Wrapper.BackgroundR:Show()
+  else
+    Wrapper.Background:Hide()
+    Wrapper.BackgroundR:Hide()
+    return
+  end
+  --wrapperHeight = scrollHeight
+
+  print('|cFFFFFF00params:|r scroller:', scrollWidth, 'x', scrollHeight)
+  print('|cFFFFFF00params:|r scroll:', scrollWidth, 'x', scrollHeight)
+  print('|cFFFFFF00params:|r wrapper:', wrapperWidth, 'x', wrapperHeight)
+  print('|cFFFFFF00params:|r header:', headerHeight)
+
+  Scroller:SetSize(wrapperWidth, wrapperHeight)
+  Scroller:SetPoint('TOPLEFT', Wrapper, 'TOPLEFT', 0, -headerHeight)
+  Scroller:SetPoint('BOTTOMRIGHT', Wrapper, 'BOTTOMRIGHT')
+
+  Scroll:SetSize(scrollWidth, scrollHeight)
+  Scroll:SetPoint('TOPLEFT', Scroller, 'TOPLEFT', 0, 0)
+  Scroll:SetPoint('RIGHT', Scroller, 'RIGHT')
+
+  --Scroller:UpdateScrollChildRect()
+  Wrapper:SetSize(wrapperWidth, wrapperHeight + headerHeight)
+
+  -- update action buttons
+
+
+  print('scroll size:', Scroll:GetSize())
+  print('scroller size:',Scroller:GetSize())
+  print('wrapper size:', Wrapper:GetSize())
+  print('scroll range :', floor(Scroller:GetVerticalScrollRange()+.5))
+
+
+  mod.UpdateActionButtons()
+end
+
+--- Queue any active item buttons for update for that frame
+mod.UpdateActionButtons = function()
+  local previousItem
+  for questIndex, itemButton in pairs(usedButtons) do
+    print('|cFF00FFFF', questIndex, itemButton:GetName())
+    local block = mod.Quest.LogBlock[questIndex]
+    print(block:GetTop())
+    if block then
+      if IsQuestWatched(questIndex) then
+        -- Dispatch the probe
+        block:SetScript('OnUpdate', function()
+          print('|cFFFFFF00probing', block:GetName())
+            if block:GetBottom() then
+              print('|cFF00FF00ding ding ding!')
+              mod.UpdateBlockAction(block, itemButton, previousItem)
+              block:SetScript('OnUpdate', nil)
+            end
+          end)
+        return
+      else
+        mod.FreeItemButton(block, itemButton)
+      end
+    end
+  end
+end
+
+mod.UpdateBlockAction = function (block, itemButton, previousItem)
+  if block:GetBottom() < Scroller:GetBottom() then
+    print('|cFFFFFF00bottom not fully visible')
+    if previousItem then
+      previousItem:ClearAllPoints()
+      previousItem:SetPoint('BOTTOM', itemButton, 'TOP', 0, 4)
+    end
+    itemButton:ClearAllPoints()
+    itemButton:SetPoint('BOTTOMRIGHT', UIParent, 'BOTTOMLEFT', Wrapper:GetLeft(), Wrapper:GetBottom())
+    itemButton:Show()
+  else
+    print('|cFF00FF00visible positions')
+    itemButton:ClearAllPoints()
+    itemButton:SetPoint('TOPRIGHT', UIParent, 'BOTTOMLEFT', block:GetLeft(), block:GetTop())
+    itemButton:Show()
+  end
+end
+
+mod.UpdateItemButtonCooldown = function(button)
+
+end
--- a/ObjectiveInfo.lua	Wed Mar 30 02:24:56 2016 -0400
+++ b/ObjectiveInfo.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -9,13 +9,15 @@
 --------------------------------------------------------------------
 --- Tracker-specific data retrieval functions
 --------------------------------------------------------------------
-
+Quest.itemButtons = {}
+Quest.freeButtons = {}
+Quest.POI = {}
 Quest.GetNumWatched = function()
   return GetNumQuestWatches ()
 end
 Quest.GetInfo = function (self, watchIndex)
   print('|cFF00DDFFQuest|r.|cFF0088FFGetInfo(|r'.. tostring(watchIndex)..'|r)')
-  local questID, title, questLogIndex, numObjectives, requiredMoney, isComplete,
+  local questID, title, questIndex, numObjectives, requiredMoney, isComplete,
   startEvent, isAutoComplete, failureTime, timeElapsed, questType, isTask, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(watchIndex)
 print(GetQuestWatchInfo(watchIndex))
   if not questID then
@@ -29,7 +31,7 @@
   q.type = 'Quest'
   q.questID = questID
   q.title = title
-  q.questLogIndex = questLogIndex
+  q.questLogIndex = questIndex
   q.numObjectives = numObjectives
   q.requiredMoney = requiredMoney
   q.isComplete = isComplete
@@ -45,7 +47,7 @@
 
   --- Start QuestLogEntry calls
   -----------------------------------------
-  SelectQuestLogEntry(questLogIndex)
+  SelectQuestLogEntry(questIndex)
   q.greenRange = GetQuestGreenRange()
   q.isDaily = QuestIsDaily()
   q.isWeekly = QuestIsWeekly()
@@ -57,13 +59,14 @@
   q.isComplete = IsQuestComplete(questID)
   q.isBreadCrumb = IsBreadcrumbQuest(questID)
   q.isStoryQuest = IsStoryQuest(questID)
-  q.completionText= GetQuestLogCompletionText(questLogIndex)
+  q.completionText= GetQuestLogCompletionText(questIndex)
   q.trackingID = questID
   q.superTracked =  (questID == GetSuperTrackedQuestID()) -- call directly so artifact data doesn't become an issue
-  q.numObjectives = GetNumQuestLeaderBoards(questLogIndex)
+  q.numObjectives = GetNumQuestLeaderBoards(questIndex)
+  q.isWatched = IsQuestWatched(questIndex)
   q.objectives = {}
   for i = 1, q.numObjectives do
-    local text, type, finished = GetQuestLogLeaderBoard(i, questLogIndex)
+    local text, type, finished = GetQuestLogLeaderBoard(i, questIndex)
     q.objectives[i] = {
       type = type,
       text = text,
@@ -77,10 +80,12 @@
     end
   end
 
-  local link, icon, charges = GetQuestLogSpecialItemInfo(questLogIndex)
-  local start, duration, enable = GetQuestLogSpecialItemCooldown(questLogIndex)
+  local link, icon, charges = GetQuestLogSpecialItemInfo(questIndex)
+  local start, duration, enable = GetQuestLogSpecialItemCooldown(questIndex)
   if link or icon or charges then
     q.specialItem = {
+      questID = questID,
+      questIndex = questIndex,
       link = link,
       charges = charges,
       icon = icon,
@@ -90,11 +95,35 @@
     }
   end
 
-  self.LogInfo[questLogIndex] = q
-  print('|cFF0088FFGetQuestInfo('..questID..')|r', questLogIndex, title)
+
+  if QuestHasPOIInfo(questID) then
+    local distance, onContinent = GetDistanceSqToQuest(questIndex)
+      if distance ~= nil and distance > 0 then
+      self.POI[questIndex] = {
+        questIndex = questIndex,
+        questID = questID,
+        distance = distance,
+        onContinent = onContinent
+      }
+    end
+  end
+
+  self.LogInfo[questIndex] = q
+  print('|cFF0088FFGetQuestInfo('..questID..')|r', questIndex, title)
   return q
 end
 
+Quest.GetClosest = function()
+  local minID
+  local minDist = math.huge
+  for i =  1, Quest.GetNumWatched() do
+      local info = Quest.GetInfo(i)
+      if info.hasLocalPOI then
+        local distance, onContinent = GetDistanceSqToQuest(info.questIndex)
+      end
+  end
+end
+
 Cheevs.GetNumWatched = function(self)
   Cheevs.trackedCheevs = {GetTrackedAchievements()}
   return GetNumTrackedAchievements()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveSecure.xml	Wed Mar 30 16:30:49 2016 -0400
@@ -0,0 +1,66 @@
+<Ui>
+  <!--
+   The following was lifted from QuestKing, mainly to spare the horrors of writing a global space frame script
+  -->
+  <Button name="VeneerItemButtonTemplate" inherits="SecureActionButtonTemplate" virtual="true">
+    <Attributes>
+      <Attribute name="type" value="item" />
+    </Attributes>
+    <Layers>
+      <Layer level="ARTWORK">
+        <FontString parentKey="HotKey" inherits="NumberFontNormalSmallGray" justifyH="LEFT" text="RANGE_INDICATOR">
+          <Size x="29" y="10"/>
+          <Anchors>
+            <Anchor point="TOPRIGHT" x="16" y="-2"/>
+          </Anchors>
+        </FontString>
+      </Layer>
+      <Layer level="BORDER">
+        <Texture parentKey="icon"/>
+        <FontString parentKey="Count" inherits="NumberFontNormal" justifyH="RIGHT" hidden="true">
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" x="-3" y="2"/>
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+    <Frames>
+      <Cooldown parentKey="Cooldown" inherits="CooldownFrameTemplate"/>
+    </Frames>
+    <Scripts>
+      <OnLoad>
+        self:RegisterForClicks("AnyUp");
+      </OnLoad>
+      <OnEvent>
+        if (event == "PLAYER_TARGET_CHANGED") then
+          self.rangeTimer = -1;
+        elseif (event == "BAG_UPDATE_COOLDOWN") then
+          Veneer_QuestObjectiveItem_UpdateCooldown(self)
+        end
+      </OnEvent>
+      <OnUpdate function="Veneer_QuestObjectiveItem_OnUpdate" />
+      <OnShow>
+        self:RegisterEvent("PLAYER_TARGET_CHANGED");
+        self:RegisterEvent("BAG_UPDATE_COOLDOWN");
+      </OnShow>
+      <OnHide>
+        self:UnregisterEvent("PLAYER_TARGET_CHANGED");
+        self:UnregisterEvent("BAG_UPDATE_COOLDOWN");
+      </OnHide>
+      <OnEnter>
+        GameTooltip:SetOwner(self, "ANCHOR_RIGHT");
+        GameTooltip:SetQuestLogSpecialItem(self.questLogIndex);
+      </OnEnter>
+      <OnLeave>
+        GameTooltip:Hide();
+      </OnLeave>
+    </Scripts>
+    <NormalTexture parentKey="NormalTexture" file="Interface\Buttons\UI-Quickslot2">
+      <Anchors>
+        <Anchor point="CENTER"/>
+      </Anchors>
+    </NormalTexture>
+    <PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
+    <HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
+  </Button>
+</Ui>
\ No newline at end of file
--- a/ObjectiveTracker.lua	Wed Mar 30 02:24:56 2016 -0400
+++ b/ObjectiveTracker.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -207,11 +207,11 @@
 end
 
 function mod:OnInitialize()
-
   self.InitializeTrackers()
   for event, _ in pairs(SmallEvents) do
     self:RegisterEvent(event)
   end
+
   for event, _ in pairs(HandlerEvents) do
     self:RegisterEvent(event)
   end
--- a/ObjectiveTracker.xml	Wed Mar 30 02:24:56 2016 -0400
+++ b/ObjectiveTracker.xml	Wed Mar 30 16:30:49 2016 -0400
@@ -225,10 +225,7 @@
     </Layers>
   </Frame>
 
-  <Button name="VeneerItemButton" inherits="SecureActionButtonTemplate">
-    
-  </Button>
-
+  <Include file="ObjectiveSecure.xml" />
   <Script file="ObjectiveTracker.lua" />
   <Script file="ObjectiveInfo.lua" />
   <Script file="ObjectiveUI.lua" />
--- a/ObjectiveUI.lua	Wed Mar 30 02:24:56 2016 -0400
+++ b/ObjectiveUI.lua	Wed Mar 30 16:30:49 2016 -0400
@@ -20,16 +20,24 @@
 end
 
 Tracker.OnMouseUp = function(self, button)
-  if button == 'LeftButton' then
+
+  if self.initialButton == 'LeftButton' then
     self:Select()
     mod.UpdateWrapper()
+    if self.modShift then
+      self:Remove()
+    end
   elseif button == 'RightButton' then
     self:Open()
   end
+  self.initialButton = nil
+  self.modShift = nil
   print('|cFFFF8800'..tostring(self:GetName())..':MouseUp()|r ->',self.info.trackingID)
 end
 
 Tracker.OnMouseDown = function(self, button)
+  self.initialButton = button
+  self.modShift = IsShiftKeyDown()
   if button == 'LeftButton' then
     self:SetStyle('Active')
   end
@@ -48,9 +56,12 @@
   SetSuperTrackedQuestID(self.info.questID)
 end
 Quest.Open = function(self)
-  print('something something quest log')
-  QuestMapFrame_ShowQuestDetails(self.info.questID)
-  ToggleQuestLog()
+  QuestMapFrame_OpenToQuestDetails(self.info.questID)
+end
+
+Quest.Remove = function(self)
+  RemoveQuestWatch(self.info.questIndex)
+  QuestPOIUpdateIcons()
 end
 
 
@@ -62,3 +73,115 @@
 Cheevs.Open = function(self)
 end
 
+----------------------------------------------------------------------------------------
+--- frame template and scripts lifted from "QuestKing 2" by Barjack
+--- url: http://mods.curse.com/addons/wow/questking
+----------------------------------------------------------------------------------------
+local usedButtons = mod.Quest.itemButtons
+local freeButtons = mod.Quest.freeButtons
+mod.SetItemButton = function(block, info)
+  local itemInfo = info.specialItem
+  if not itemInfo then
+    return
+  end
+  --- .specialItem :: {link = link, charges = charges, icon = icon, start = start, duration = duration, enable = enable}
+
+
+  local itemButton
+  if not info.itemButton then
+    if #freeButtons >= 1 then
+      print('|cFF00FFFFfound a free button')
+      itemButton = freeButtons[#freeButtons]
+      freeButtons[#freeButtons] = nil
+    else
+      print('|cFFFF4400starting new button')
+      local buttonIndex = mod.Quest.numButtons + #freeButtons + 1
+      itemButton = CreateFrame('Button', 'VeneerQuestItemButton' .. buttonIndex, UIParent, 'VeneerItemButtonTemplate')
+      itemButton.buttonIndex = buttonIndex
+      itemButton:SetSize(48, 48)
+      itemButton:GetNormalTexture():SetSize(80,80)
+    end
+    mod.Quest.numButtons = mod.Quest.numButtons + 1
+  else
+    print('|cFF00FF00found assigned button')
+    itemButton = info.itemButton
+  end
+  -- set values
+  info.itemButton = itemButton
+  block.itemButton = itemButton
+  usedButtons[info.questLogIndex] = itemButton
+
+  itemButton:SetAttribute("type", "item")
+  itemButton:SetAttribute("item", itemInfo.link)
+
+  itemButton.questLogIndex = info.questLogIndex
+  itemButton.charges = itemInfo.charges
+  itemButton.rangeTimer = -1
+  itemButton.block = block
+
+  SetItemButtonTexture(itemButton, itemInfo.icon)
+  SetItemButtonCount(itemButton, itemInfo.charges)
+  Veneer_QuestObjectiveItem_UpdateCooldown(itemButton);
+
+  return itemButton
+end
+--- Clear an itemButton from the given block
+mod.FreeItemButton = function(block, itemButton)
+  if block.itemButton ~= itemButton then
+    print('|cFFFF0088trying to release mismatched action button')
+    return
+  end
+
+  block.itemButton = nil
+  block.icon:Hide()
+
+  itemButton.block = nil
+  itemButton:Hide()
+
+  usedButtons[itemButton.questLogIndex] = nil
+  freeButtons[#freeButtons + 1] = itemButton
+  mod.Quest.numButtons = mod.Quest.numButtons - 1
+  print('|cFFFF0088released', itemButton:GetName(),'from', block:GetName())
+end
+
+function Veneer_QuestObjectiveItem_OnUpdate (self, elapsed)
+  -- Handle range indicator
+  local rangeTimer = self.rangeTimer
+  if (rangeTimer) then
+    rangeTimer = rangeTimer - elapsed
+    if (rangeTimer <= 0) then
+      local link, item, charges, showItemWhenComplete = GetQuestLogSpecialItemInfo(self.questLogIndex)
+      if ((not charges) or (charges ~= self.charges)) then
+        QuestKing:UpdateTracker()
+        return
+      end
+
+      local count = self.HotKey
+      local valid = IsQuestLogSpecialItemInRange(self.questLogIndex)
+      if (valid == 0) then
+        count:Show()
+        count:SetVertexColor(1.0, 0.1, 0.1)
+      elseif (valid == 1) then
+        count:Show()
+        count:SetVertexColor(0.6, 0.6, 0.6)
+      else
+        count:Hide()
+      end
+      rangeTimer = TOOLTIP_UPDATE_TIME
+    end
+
+    self.rangeTimer = rangeTimer
+  end
+end
+
+function Veneer_QuestObjectiveItem_UpdateCooldown (itemButton)
+  local start, duration, enable = GetQuestLogSpecialItemCooldown(itemButton.questLogIndex)
+  if (start) then
+    CooldownFrame_SetTimer(itemButton.Cooldown, start, duration, enable)
+    if (duration > 0 and enable == 0) then
+      SetItemButtonTextureVertexColor(itemButton, 0.4, 0.4, 0.4)
+    else
+      SetItemButtonTextureVertexColor(itemButton, 1, 1, 1)
+    end
+  end
+end
\ No newline at end of file