· 9 years ago · Feb 01, 2017, 08:30 AM
1-- Author : Potdisc
2-- Create Date : 8/6/2015
3-- DefaultModule
4-- lootHistory.lua Adds the interface for displaying the collected loot history
5
6local addon = LibStub("AceAddon-3.0"):GetAddon("RCLootCouncil")
7local LootHistory = addon:NewModule("RCLootHistory")
8local L = LibStub("AceLocale-3.0"):GetLocale("RCLootCouncil")
9local AG = LibStub("AceGUI-3.0")
10local lootDB, scrollCols, data, db, numLootWon;
11--[[ data structure:
12data[date][playerName] = {
13 ["class"] = CLASS,
14 [i] = { -- Num item given to player, lowest first
15 -- Remaining content in lootDB[playerName]
16 }
17}
18]]
19local selectedDate, selectedName, filterMenu, moreInfo
20local ROW_HEIGHT = 20;
21local NUM_ROWS = 15;
22
23function LootHistory:OnInitialize()
24 self.exportSelection = "lua"
25 -- Pointer to export functions. Expected to return a string containing the export
26 self.exports = {
27 lua = { func = self.ExportLua, name = "Lua", tip = L["Raw lua output. Doesn't work well with date selection."]},
28 csv = { func = self.ExportCSV, name = "CSV", tip = L["Standard .csv output."]},
29 tsv = {func = self.ExportTSV, name = "TSV (Excel)", tip = "A tab delimited output for Excel. Might work with outher spreadsheets."},
30 bbcode = { func = self.ExportBBCode, name = "BBCode", tip = L["Simple BBCode output."]},
31 bbcodeSmf = {func = self.ExportBBCodeSMF, name = "BBCode SMF", tip = L["BBCode export, tailored for SMF."],},
32 eqxml = { func = self.ExportEQXML, name = "EQdkp-Plus XML", tip = L["EQdkp-Plus XML output, tailored for Enjin import."]},
33 player = { func = self.PlayerExport, name = "Player Export", tip = "A format to copy/paste to another player."},
34 --html = self.ExportHTML
35 }
36 scrollCols = {
37 {name = "", width = ROW_HEIGHT, }, -- Class icon, should be same row as player
38 {name = L["Name"], width = 100, }, -- Name of the player
39 {name = L["Time"], width = 125, comparesort = self.DateTimeSort }, -- Time of awarding
40 {name = "", width = ROW_HEIGHT, }, -- Item at index icon
41 {name = L["Item"], width = 250, }, -- Item string
42 {name = L["Reason"], width = 220, comparesort = self.ResponseSort, sort = "asc", sortnext = 2}, -- Response aka the text supplied to lootDB...response
43 {name = "", width = ROW_HEIGHT}, -- Delete button
44 }
45 filterMenu = CreateFrame("Frame", "RCLootCouncil_LootHistory_FilterMenu", UIParent, "Lib_UIDropDownMenuTemplate")
46 Lib_UIDropDownMenu_Initialize(filterMenu, self.FilterMenu, "MENU")
47 --MoreInfo
48 self.moreInfo = CreateFrame( "GameTooltip", "RCLootHistoryMoreInfo", nil, "GameTooltipTemplate" )
49end
50
51function LootHistory:OnEnable()
52 addon:Debug("OnEnable()")
53 moreInfo = true
54 db = addon:Getdb()
55 lootDB = addon:GetHistoryDB()
56 self.frame = self:GetFrame()
57 self:BuildData()
58 self:Show()
59end
60
61function LootHistory:OnDisable()
62 self:Hide()
63 self.frame:SetParent(nil)
64 self.frame = nil
65 data = {}
66end
67
68function LootHistory:Show()
69 self.frame:Show()
70end
71
72function LootHistory:Hide()
73 self.frame:Hide()
74 self.moreInfo:Hide()
75 moreInfo = false
76end
77
78function LootHistory:BuildData()
79 addon:Debug("LootHistory:BuildData()")
80 data = {}
81 numLootWon = {} -- playerName = #
82 local date
83 -- We want to rebuild lootDB to the "data" format:
84 --local i = 1
85 for name, v in pairs(lootDB) do
86 numLootWon[name] = 0
87 -- Now we actually add the data
88 for i,v in ipairs(v) do
89 numLootWon[name] = numLootWon[name] + 1
90 date = v.date
91 if not date then -- Unknown date
92 date = L["Unknown date"]
93 end
94 if not data[date] then -- We haven't added the date to data, do it
95 data[date] = {}
96 end
97 if not data[date][name] then
98 data[date][name] = {}
99 end
100 if not data[date][name][i] then
101 data[date][name][i] = {}
102 end
103 for k, t in pairs(v) do
104 if k == "class" then -- we need the class at a different level
105 data[date][name].class = t
106 elseif k ~= "date" then -- We don't need date
107 data[date][name][i][k] = t
108 end
109 end
110 if not data[date][name][i].instance then
111 data[date][name][i].instance = L["Unknown"]
112 end
113 end
114 end
115 table.sort(data)
116 -- Now create a blank data table for lib-st to init with
117 self.frame.rows = {}
118 local dateData, nameData, insertedNames = {}, {}, {}
119 local row = 1;
120 for date, v in pairs(data) do
121 for name, x in pairs(v) do
122 for num, i in pairs(x) do
123 if num ~= "class" then
124 self.frame.rows[row] = {
125 date = date,
126 class = x.class,
127 name = name,
128 num = num,
129 response = i.responseID,
130 cols = {
131 {DoCellUpdate = addon.SetCellClassIcon, args = {x.class}},
132 {value = addon.Ambiguate(name), color = addon:GetClassColor(x.class)},
133 {value = date.. "-".. i.time or "", args = {time = i.time, date = date},},
134 {DoCellUpdate = self.SetCellGear, args={i.lootWon}},
135 {value = i.lootWon},
136 {DoCellUpdate = self.SetCellResponse, args = {color = i.color, response = i.response, responseID = i.responseID or 0, isAwardReason = i.isAwardReason}},
137 {DoCellUpdate = self.SetCellDelete},
138 }
139 }
140 row = row + 1
141 end
142 end
143 if not tContains(insertedNames, name) then -- we only want each name added once
144 tinsert(nameData,
145 {
146 {DoCellUpdate = addon.SetCellClassIcon, args = {x.class}},
147 {value = addon.Ambiguate(name), color = addon:GetClassColor(x.class), name = name}
148 }
149 )
150 tinsert(insertedNames, name)
151 elseif x.class then -- it already exists, but we might need to add the class which we now have
152 for i in pairs(nameData) do
153 if nameData[i][2].name == name then
154 nameData[i][1].args = {x.class}
155 end
156 end
157 end
158 end
159 tinsert(dateData, {date})
160 end
161 self.frame.st:SetData(self.frame.rows)
162 self.frame.date:SetData(dateData, true) -- True for minimal data format
163 self.frame.name:SetData(nameData, true)
164end
165
166function LootHistory.FilterFunc(table, row)
167 local nameAndDate = true -- default to show everything
168 if selectedName and selectedDate then
169 nameAndDate = row.name == selectedName and row.date == selectedDate
170 elseif selectedName then
171 nameAndDate = row.name == selectedName
172 elseif selectedDate then
173 nameAndDate = row.date == selectedDate
174 end
175
176 local responseFilter = true -- default to show
177 if not db.modules["RCLootHistory"].filters then return nameAndDate end -- db hasn't been initialized
178 local response = row.response
179 if response == "AUTOPASS" or response == "PASS" or type(response) == "number" then
180 responseFilter = db.modules["RCLootHistory"].filters[response]
181 else -- Filter out the status texts
182 responseFilter = db.modules["RCLootHistory"].filters["STATUS"]
183 end
184
185 return nameAndDate and responseFilter -- Either one can filter the entry
186end
187
188function LootHistory.SetCellGear(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
189 local gear = data[realrow].cols[column].args[1] -- gear1 or gear2
190 if gear then
191 local texture = select(10, GetItemInfo(gear))
192 frame:SetNormalTexture(texture)
193 frame:SetScript("OnEnter", function() addon:CreateHypertip(gear) end)
194 frame:SetScript("OnLeave", function() addon:HideTooltip() end)
195 frame:SetScript("OnClick", function()
196 if IsModifiedClick() then
197 HandleModifiedItemClick(gear);
198 end
199 end)
200 frame:Show()
201 else
202 frame:Hide()
203 end
204end
205
206function LootHistory.SetCellResponse(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
207 local args = data[realrow].cols[column].args
208 frame.text:SetText(args.response)
209
210 if args.color then -- Never version saves the color with the entry
211 frame.text:SetTextColor(unpack(args.color))
212 elseif args.responseID and args.responseID > 0 then -- try to recreate color from ID
213 frame.text:SetTextColor(addon:GetResponseColor(args.responseID))
214 else -- default to white
215 frame.text:SetTextColor(1,1,1,1)
216 end
217end
218
219function LootHistory.SetCellDelete(rowFrame, frame, data, cols, row, realrow, column, fShow, table, ...)
220 if not frame.created then
221 frame:SetNormalTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
222 frame:SetScript("OnEnter", function()
223 addon:CreateTooltip(L["Double click to delete this entry."])
224 end)
225 frame:SetScript("OnLeave", function() addon:HideTooltip() end)
226 frame.created = true
227 end
228 frame:SetScript("OnClick", function()
229 local name, num = data[realrow].name, data[realrow].num
230 if frame.lastClick and GetTime() - frame.lastClick <= 0.5 then
231 frame.lastClick = nil
232 -- Do deleting
233 addon:Debug("Deleting:", name, lootDB[name][num].lootWon)
234 tremove(lootDB[name], num)
235 tremove(data, realrow)
236 table:SortData()
237 else
238 frame.lastClick = GetTime()
239 end
240 end)
241end
242
243local sinceEpoch = {} -- Stores epoch seconds for both, we should reuse it
244function LootHistory.DateTimeSort(table, rowa, rowb, sortbycol)
245 local cella, cellb = table:GetCell(rowa, sortbycol), table:GetCell(rowb, sortbycol);
246 local timea, datea, timeb, dateb = cella.args.time, cella.args.date, cellb.args.time, cellb.args.date
247 local d, m, y = strsplit("/", datea, 3)
248 local h, min, s = strsplit(":", timea, 3)
249 sinceEpoch.a = time({year = "20"..y, month = m, day = d, hour = h, min = min, sec = s})
250 d, m, y = strsplit("/", dateb, 3)
251 h, min, s = strsplit(":", timeb, 3)
252 sinceEpoch.b = time({year = "20"..y, month = m, day = d, hour = h, min = min, sec = s})
253 local direction = table.cols[sortbycol].sort or table.cols[sortbycol].defaultsort or "asc";
254 if direction:lower() == "asc" then
255 return sinceEpoch.a < sinceEpoch.b;
256 else
257 return sinceEpoch.a > sinceEpoch.b;
258 end
259end
260
261function LootHistory.DateSort(table, rowa, rowb, sortbycol)
262 local column = table.cols[sortbycol]
263 rowa, rowb = table:GetRow(rowa), table:GetRow(rowb);
264 local a, b = rowa[1], rowb[1]
265 if not (a and b) then return false end
266 local d, m, y = strsplit("/", a, 3)
267 local aTime = time({year = "20"..y, month = m, day = d})
268 d, m, y = strsplit("/", b, 3)
269 local bTime = time({year = "20"..y, month = m, day = d})
270 local direction = column.sort or column.defaultsort or "asc";
271 if direction:lower() == "asc" then
272 return aTime < bTime;
273 else
274 return aTime > bTime;
275 end
276end
277
278function LootHistory.ResponseSort(table, rowa, rowb, sortbycol)
279 local column = table.cols[sortbycol]
280 rowa, rowb = table:GetRow(rowa), table:GetRow(rowb);
281 local a,b
282 local aID, bID = data[rowa.date][rowa.name][rowa.num].responseID, data[rowb.date][rowb.name][rowb.num].responseID
283
284 -- NOTE: I'm pretty sure it can only be an awardReason when responseID is nil or 0
285 if aID and aID ~= 0 then
286 if data[rowa.date][rowa.name][rowa.num].isAwardReason then
287 a = db.awardReasons[aID].sort
288 else
289 a = addon:GetResponseSort(aID)
290 end
291 else
292 -- 500 will be below award reasons and just above status texts
293 a = 500
294 end
295
296 if bID and bID ~= 0 then
297 if data[rowb.date][rowb.name][rowb.num].isAwardReason then
298 b = db.awardReasons[bID].sort
299 else
300 b = addon:GetResponseSort(bID)
301 end
302
303 else
304 b = 500
305 end
306
307 local direction = column.sort or column.defaultsort or "asc";
308 if direction:lower() == "asc" then
309 return a < b;
310 else
311 return a > b;
312 end
313end
314
315function LootHistory:EscapeItemLink(link)
316 return gsub(link, "\124", "\124\124")
317end
318
319function LootHistory:ExportHistory()
320 --debugprofilestart()
321 local export = self.exports[self.exportSelection].func(self)
322 --addon:Debug("Export time:", debugprofilestop(), "ms")
323 if export and export ~= "" then -- do something
324 --debugprofilestart()
325 self.frame.exportFrame:Show()
326 self:SetExportText(export)
327 --addon:Debug("Display time:", debugprofilestop(), "ms")
328 end
329end
330
331function LootHistory:ImportHistory(import)
332 addon:Debug("Initiating import")
333 lootDB = addon:GetHistoryDB()
334 -- Start with validating the import:
335 if type(import) ~= "string" then addon:Print("The imported text wasn't a string"); return addon:DebugLog("No string") end
336 import = gsub(import, "\124\124", "\124") -- De escape itemslinks
337 local test, import = addon:Deserialize(import)
338 if not test then addon:Print("Deserialization failed - maybe wrong import type?"); return addon:DebugLog("Deserialization failed") end
339 addon:Debug("Validation completed", #lootDB == 0, #lootDB)
340 -- Now import should be a copy of the orignal exporter's lootDB, so insert any changes
341 -- Start by seeing if we even have a lootDB
342 --if #lootDB == 0 then lootDB = import; return end
343 local number = 0
344 for name, data in pairs(import) do
345 if lootDB[name] then -- We've registered the name, so check all the awards
346 for _, v in pairs(data) do
347 local found = false
348 for _, d in pairs(lootDB[name]) do
349 -- Check if the time matches. If it does, we already have the data and can skip to the next
350 if d.time == v.time then found = true; break end
351 end
352 if not found then -- add it
353 tinsert(lootDB[name], v)
354 number = number + 1
355 end
356 end
357 else -- It's a new name, so add everything and move on to the next
358 lootDB[name] = data
359 number = number + #data
360 end
361 end
362 addon.lootDB.factionrealm = lootDB -- save it
363 addon:Print("Successfully imported "..number.." entries.")
364 addon:Debug("Import successful")
365 self:BuildData()
366end
367
368function LootHistory:GetWowheadLinkFromItemLink(link)
369 local color, itemType, itemID, enchantID, gemID1, gemID2, gemID3, gemID4, suffixID, uniqueID, linkLevel, specializationID,
370 upgradeTypeID, upgradeID, instanceDifficultyID, numBonuses, bonusIDs = addon:DecodeItemLink(link)
371
372 local itemurl = "https://www.wowhead.com/item="..itemID
373
374 -- It seems bonus id 1487 (and basically any other id that's -5 below Wowheads first ilvl upgrade doesn't work)
375 -- Neither does Warforged items it seems
376 if numBonuses > 0 then
377 itemurl = itemurl.."&bonus="
378 for i, b in pairs(bonusIDs) do
379 itemurl = itemurl..b
380 if i < numBonuses then
381 itemurl = itemurl..":"
382 end
383 end
384 end
385
386 return itemurl
387end
388
389---------------------------------------------------
390-- Visauls
391---------------------------------------------------
392function LootHistory:Update()
393 self.frame.st:SortData()
394end
395
396function LootHistory:GetFrame()
397 if self.frame then return self.frame end
398 local f = addon:CreateFrame("DefaultRCLootHistoryFrame", "history", L["RCLootCouncil Loot History"], 250, 480)
399 local st = LibStub("ScrollingTable"):CreateST(scrollCols, NUM_ROWS, ROW_HEIGHT, { ["r"] = 1.0, ["g"] = 0.9, ["b"] = 0.0, ["a"] = 0.5 }, f.content)
400 st.frame:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 10, 10)
401 st:SetFilter(self.FilterFunc)
402 st:EnableSelection(true)
403 st:RegisterEvents({
404 ["OnClick"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, table, button, ...)
405 if row or realrow then
406 self:UpdateMoreInfo(rowFrame, cellFrame, data, cols, row, realrow, column, table, button, ...)
407 end
408 return false
409 end
410 })
411 f.st = st
412
413 --Date selection
414 f.date = LibStub("ScrollingTable"):CreateST({{name = L["Date"], width = 70, comparesort = self.DateSort, sort = "desc"}}, 5, ROW_HEIGHT, { ["r"] = 1.0, ["g"] = 0.9, ["b"] = 0.0, ["a"] = 0.5 }, f.content)
415 f.date.frame:SetPoint("TOPLEFT", f, "TOPLEFT", 10, -20)
416 f.date:EnableSelection(true)
417 f.date:RegisterEvents({
418 ["OnClick"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, table, button, ...)
419 if button == "LeftButton" and row then
420 selectedDate = data[realrow][column] ~= selectedDate and data[realrow][column] or nil
421 self:Update()
422 end
423 return false
424 end
425 })
426
427 --Name selection
428 f.name = LibStub("ScrollingTable"):CreateST({{name = "", width = ROW_HEIGHT},{name = L["Name"], width = 100, sort = "desc"}}, 5, ROW_HEIGHT, { ["r"] = 1.0, ["g"] = 0.9, ["b"] = 0.0, ["a"] = 0.5 }, f.content)
429 f.name.frame:SetPoint("TOPLEFT", f.date.frame, "TOPRIGHT", 20, 0)
430 f.name:EnableSelection(true)
431 f.name:RegisterEvents({
432 ["OnClick"] = function(rowFrame, cellFrame, data, cols, row, realrow, column, table, button, ...)
433 if button == "LeftButton" and row then
434 selectedName = selectedName ~= data[realrow][column].name and data[realrow][column].name or nil
435 self:Update()
436 end
437 return false
438 end
439 })
440
441 -- Abort button
442 local b1 = addon:CreateButton(L["Close"], f.content)
443 b1:SetPoint("TOPRIGHT", f, "TOPRIGHT", -10, -100)
444 b1:SetScript("OnClick", function() self:Disable() end)
445 f.closeBtn = b1
446
447 -- More info button
448 local b2 = CreateFrame("Button", nil, f.content, "UIPanelButtonTemplate")
449 b2:SetSize(25,25)
450 b2:SetPoint("BOTTOMRIGHT", b1, "TOPRIGHT", 0, 10)
451 b2:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Up")
452 b2:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Down")
453 b2:SetScript("OnClick", function(button)
454 moreInfo = not moreInfo
455 self.frame.st:ClearSelection()
456 self:UpdateMoreInfo()
457 if moreInfo then -- show the more info frame
458 button:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Up");
459 button:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-PrevPage-Down");
460 self.moreInfo:Show()
461 else -- hide it
462 button:SetNormalTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Up")
463 button:SetPushedTexture("Interface\\Buttons\\UI-SpellbookIcon-NextPage-Down")
464 self.moreInfo:Hide()
465 end
466 addon:Debug("moreInfo =",moreInfo)
467 end)
468 b2:SetScript("OnEnter", function() addon:CreateTooltip(L["Click to expand/collapse more info"]) end)
469 b2:SetScript("OnLeave", function() addon:HideTooltip() end)
470 f.moreInfoBtn = b2
471
472 -- Export
473 local b3 = addon:CreateButton(L["Export"], f.content)
474 b3:SetPoint("RIGHT", b1, "LEFT", -10, 0)
475 b3:SetScript("OnClick", function() self:ExportHistory() end)
476 f.exportBtn = b3
477
478 -- Import
479 local b5 = addon:CreateButton("Import", f.content)
480 b5:SetPoint("RIGHT", b3, "LEFT", -10, 0)
481 b5:SetScript("OnClick", function() self.frame.importFrame:Show() end)
482 f.importBtn = b5
483
484 -- Filter
485 local b4 = addon:CreateButton(L["Filter"], f.content)
486 b4:SetPoint("RIGHT", f.importBtn, "LEFT", -10, 0)
487 b4:SetScript("OnClick", function(self) Lib_ToggleDropDownMenu(1, nil, filterMenu, self, 0, 0) end )
488 f.filter = b4
489 Lib_UIDropDownMenu_Initialize(b4, self.FilterMenu)
490 f.filter:SetSize(125,25) -- Needs extra convincing to stay at 25px height
491
492 -- Export selection (AceGUI-3.0)
493 local sel = AG:Create("Dropdown")
494 sel:SetPoint("BOTTOMLEFT", f.importBtn, "TOPLEFT", 0, 10)
495 sel:SetPoint("TOPRIGHT", b2, "TOPLEFT", -10, 0)
496 local values = {}
497 for k, v in pairs(self.exports) do
498 values[k] = v.name
499 end
500 sel:SetList(values)
501 sel:SetValue(self.exportSelection)
502 sel:SetText(self.exports[self.exportSelection].name)
503 sel:SetCallback("OnValueChanged", function(_,_, key)
504 self.exportSelection = key
505 end)
506 sel:SetCallback("OnEnter", function()
507 addon:CreateTooltip(self.exports[self.exportSelection].tip)
508 end)
509 sel:SetCallback("OnLeave", function()
510 addon:HideTooltip()
511 end)
512 sel:SetParent(f)
513 sel.frame:Show()
514 f.moreInfoDropdown = sel
515
516 -- Clear selection
517 local b6 = addon:CreateButton(L["Clear Selection"], f.content)
518 b6:SetPoint("RIGHT", sel.frame, "LEFT", -10, 0)
519 b6:SetScript("OnClick", function()
520 selectedDate, selectedName = nil, nil
521 self.frame.date:ClearSelection()
522 self.frame.name:ClearSelection()
523 self:Update()
524 end)
525 b6:SetWidth(125)
526 f.clearSelectionBtn = b6
527
528 -- Export string
529 local s = f.content:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
530 s:SetPoint("BOTTOMRIGHT", sel.frame, "TOPRIGHT", 0, 5)
531 s:SetPoint("BOTTOMLEFT", sel.frame, "TOPLEFT", 0, 5)
532 s:SetHeight(10)
533 s:SetTextColor(1,1,1)
534 s:SetJustifyH("LEFT")
535 s:SetText(L["Note: Huge exports will cause lag."])
536 f.exportString = s
537
538 -- Export frame
539 local exp = AG:Create("Window")
540 exp:SetLayout("Flow")
541 exp:SetTitle("RCLootCouncil "..L["Export"])
542 exp:SetWidth(400)
543 exp:SetHeight(550)
544
545 local edit = AG:Create("MultiLineEditBox")
546 edit:SetNumLines(20)
547 edit:SetFullWidth(true)
548 edit:SetLabel(L["Export"])
549 edit:SetFullHeight(true)
550 exp:AddChild(edit)
551 exp:Hide()
552 f.exportFrame = exp
553 self.SetExportText = function(self, text)
554 edit:SetText(text)
555 edit:HighlightText()
556 edit:SetFocus()
557 end
558
559 -- Import frame
560 local imp = AG:Create("Window")
561 imp:SetLayout("Flow")
562 imp:SetTitle("RCLootCouncil Import")
563 imp:SetWidth(400)
564 imp:SetHeight(550)
565
566 local edit = AG:Create("MultiLineEditBox")
567 edit:SetNumLines(20)
568 edit:SetFullWidth(true)
569 edit:SetLabel("Import")
570 edit:SetFullHeight(true)
571 edit:SetCallback("OnEnterPressed", function()
572 self:ImportHistory(edit:GetText())
573 imp:Hide()
574 end)
575 imp:AddChild(edit)
576 imp:Hide()
577 f.importFrame = imp
578
579 -- Set a proper width
580 f:SetWidth(st.frame:GetWidth() + 20)
581 return f;
582end
583
584function LootHistory:UpdateMoreInfo(rowFrame, cellFrame, dat, cols, row, realrow, column, table, button, ...)
585 if not dat then return end
586 local tip = self.moreInfo -- shortening
587 tip:SetOwner(self.frame, "ANCHOR_RIGHT")
588 local row = dat[realrow]
589 local color = addon:GetClassColor(row.class)
590 local data = data[row.date][row.name][row.num]
591 tip:AddLine(addon.Ambiguate(row.name), color.r, color.g, color.b)
592 tip:AddLine("")
593 tip:AddDoubleLine(L["Time"]..":", (data.time or L["Unknown"]) .." ".. row.date or L["Unknown"], 1,1,1, 1,1,1)
594 tip:AddDoubleLine(L["Loot won:"], data.lootWon or L["Unknown"], 1,1,1, 1,1,1)
595 if data.itemReplaced1 then
596 tip:AddDoubleLine(L["Item(s) replaced:"], data.itemReplaced1, 1,1,1)
597 if data.itemReplaced2 then
598 tip:AddDoubleLine(" ", data.itemReplaced2)
599 end
600 end
601 tip:AddDoubleLine(L["Dropped by:"], data.boss or L["Unknown"], 1,1,1, 0.862745, 0.0784314, 0.235294)
602 tip:AddDoubleLine(L["From:"], data.instance or L["Unknown"], 1,1,1, 0.823529, 0.411765, 0.117647)
603 tip:AddDoubleLine(L["Votes"]..":", data.votes or L["Unknown"], 1,1,1, 1,1,1)
604 tip:AddDoubleLine(L["Total items won:"], numLootWon[row.name], 1,1,1, 0,1,0)
605
606 -- Debug stuff
607 if addon.debug then
608 tip:AddLine("\nDebug:")
609 tip:AddDoubleLine("ResponseID", tostring(data.responseID), 1,1,1, 1,1,1)
610 tip:AddDoubleLine("Response:", data.response, 1,1,1, 1,1,1)
611 tip:AddDoubleLine("isAwardReason:", tostring(data.isAwardReason), 1,1,1, 1,1,1)
612 tip:AddDoubleLine("color:", data.color and data.color[1]..", "..data.color[2]..", "..data.color[3] or "none", 1,1,1, 1,1,1)
613 tip:AddDoubleLine("DataIndex:", row.num, 1,1,1, 1,1,1)
614 end
615 tip:SetScale(db.UI.history.scale)
616 if moreInfo then
617 tip:Show()
618 else
619 tip:Hide()
620 end
621 tip:SetAnchorType("ANCHOR_RIGHT", 0, -tip:GetHeight())
622end
623
624
625
626---------------------------------------------------
627-- Dropdowns
628---------------------------------------------------
629function LootHistory.FilterMenu(menu, level)
630 local info = Lib_UIDropDownMenu_CreateInfo()
631 if level == 1 then -- Redundant
632 -- Build the data table:
633 local data = {["STATUS"] = true, ["PASS"] = true, ["AUTOPASS"] = true}
634 for i = 1, addon.mldb.numButtons or db.numButtons do
635 data[i] = i
636 end
637 if not db.modules["RCLootHistory"].filters then -- Create the db entry
638 addon:DebugLog("Created LootHistory filters")
639 db.modules["RCLootHistory"].filters = {}
640 end
641 for k in pairs(data) do -- Update the db entry to make sure we have all buttons in it
642 if type(db.modules["RCLootHistory"].filters[k]) ~= "boolean" then
643 addon:Debug("Didn't contain "..k)
644 db.modules["RCLootHistory"].filters[k] = true -- Default as true
645 end
646 end
647 info.text = L["Filter"]
648 info.isTitle = true
649 info.notCheckable = true
650 info.disabled = true
651 Lib_UIDropDownMenu_AddButton(info, level)
652 info = Lib_UIDropDownMenu_CreateInfo()
653
654 for k in ipairs(data) do -- Make sure normal responses are on top
655 info.text = addon:GetResponseText(k)
656 info.colorCode = "|cff"..addon:RGBToHex(addon:GetResponseColor(k))
657 info.func = function()
658 addon:Debug("Update Filter")
659 db.modules["RCLootHistory"].filters[k] = not db.modules["RCLootHistory"].filters[k]
660 LootHistory:Update()
661 end
662 info.checked = db.modules["RCLootHistory"].filters[k]
663 Lib_UIDropDownMenu_AddButton(info, level)
664 end
665 for k in pairs(data) do -- A bit redundency, but it makes sure these "specials" comes last
666 if type(k) == "string" then
667 if k == "STATUS" then
668 info.text = L["Status texts"]
669 info.colorCode = "|cffde34e2" -- purpleish
670 else
671 info.text = addon:GetResponseText(k)
672 info.colorCode = "|cff"..addon:RGBToHex(addon:GetResponseColor(k))
673 end
674 info.func = function()
675 addon:Debug("Update Filter")
676 db.modules["RCLootHistory"].filters[k] = not db.modules["RCLootHistory"].filters[k]
677 LootHistory:Update()
678 end
679 info.checked = db.modules["RCLootHistory"].filters[k]
680 Lib_UIDropDownMenu_AddButton(info, level)
681 end
682 end
683 end
684end
685
686
687---------------------------------------------------------------
688-- Exports
689---------------------------------------------------------------
690-- Lua
691function LootHistory:ExportLua()
692 local export = ""
693 for player, v in pairs(lootDB) do
694 if selectedName and selectedName == player or not selectedName then
695 export = export .. "[\""..player.."\"] = {\r\n"
696 for i, d in pairs(v) do
697 if selectedDate and selectedDate == d.date or not selectedDate then
698 export = export .."\t{\r\n"
699 for label, d in pairs(d) do
700 if label == "color" then -- thats a table
701 export = export .. "\t\t[\""..label.."\"] = {\r\n"
702 for i,d in pairs(d) do
703 export = export .. "\t\t\t"..d..", --"..i.."\r\n"
704 end
705 export = export .."\t\t}\r\n"
706 elseif label == "lootWon" or label == "itemReplaced1" or label == "itemReplaced2" then
707 export = export .. "\t\t[\""..label.."\"] = "..self:EscapeItemLink(d).."\r\n"
708 else
709 export = export .. "\t\t[\""..label.."\"] = "..tostring(d).."\r\n"
710 end
711 end
712 export = export .."\t} --"..i.."\r\n"
713 end
714 end
715 export = export .."}\r\n"
716 end
717 end
718return export
719end
720
721-- CSV with all stored data
722-- ~14 ms (74%) improvement by switching to table and concat
723function LootHistory:ExportCSV()
724 -- Add headers
725 local export = {}
726 local ret = "player, date, time, item, itemID, itemString, response, votes, class, instance, boss, gear1, gear2, responseID, isAwardReason\r\n"
727 for player, v in pairs(lootDB) do
728 if selectedName and selectedName == player or not selectedName then
729 for i, d in pairs(v) do
730 if selectedDate and selectedDate == d.date or not selectedDate then
731 -- We might have commas in various things here :/
732 tinsert(export, tostring(player))
733 tinsert(export, tostring(d.date))
734 tinsert(export, tostring(d.time))
735 tinsert(export, (gsub(tostring(d.lootWon),",","")))
736 tinsert(export, addon:GetItemIDFromLink(d.lootWon))
737 tinsert(export, addon:GetItemStringFromLink(d.lootWon))
738 tinsert(export, (gsub(tostring(d.response),",","")))
739 tinsert(export, tostring(d.votes))
740 tinsert(export, tostring(d.class))
741 tinsert(export, (gsub(tostring(d.instance),",","")))
742 tinsert(export, (gsub(tostring(d.boss),",","")))
743 tinsert(export, (gsub(tostring(d.itemReplaced1), ",","")))
744 tinsert(export, (gsub(tostring(d.itemReplaced2), ",","")))
745 tinsert(export, tostring(d.responseID))
746 tinsert(export, tostring(d.isAwardReason))
747 ret = ret..table.concat(export, ",").."\r\n"
748 export = {}
749 end
750 end
751 end
752 end
753 return ret
754end
755
756-- TSV (Tab Seperated Values) for Excel
757-- Made specificly with excel in mind, but might work with other spreadsheets
758function LootHistory:ExportTSV()
759 -- Add headers
760 local export = {}
761 local ret = "player\tdate\ttime\titem\titemID\titemString\tresponse\tvotes\tclass\tinstance\tboss\tgear1\tgear2\tresponseID\tisAwardReason\r\n"
762 for player, v in pairs(lootDB) do
763 if selectedName and selectedName == player or not selectedName then
764 for i, d in pairs(v) do
765 if selectedDate and selectedDate == d.date or not selectedDate then
766 tinsert(export, tostring(player))
767 tinsert(export, tostring(d.date))
768 tinsert(export, tostring(d.time))
769 tinsert(export, "=HYPERLINK(\""..self:GetWowheadLinkFromItemLink(d.lootWon).."\",\""..tostring(d.lootWon).."\")")
770 tinsert(export, addon:GetItemIDFromLink(d.lootWon))
771 tinsert(export, addon:GetItemStringFromLink(d.lootWon))
772 tinsert(export, tostring(d.response))
773 tinsert(export, tostring(d.votes))
774 tinsert(export, tostring(d.class))
775 tinsert(export, tostring(d.instance))
776 tinsert(export, tostring(d.boss))
777 tinsert(export, "=HYPERLINK(\""..self:GetWowheadLinkFromItemLink(tostring(d.itemReplaced1)).."\",\""..tostring(d.itemReplaced1).."\")")
778 tinsert(export, "=HYPERLINK(\""..self:GetWowheadLinkFromItemLink(tostring(d.itemReplaced1)).."\",\""..tostring(d.itemReplaced2).."\")")
779 tinsert(export, tostring(d.responseID))
780 tinsert(export, tostring(d.isAwardReason))
781 ret = ret..table.concat(export, "\t").."\r\n"
782 export = {}
783 end
784 end
785 end
786 end
787 return ret
788end
789
790-- Simplified BBCode, as supported by CurseForge
791-- ~24 ms (84%) improvement by switching to table and concat
792function LootHistory:ExportBBCode()
793 local export = {}
794 for player, v in pairs(lootDB) do
795 if selectedName and selectedName == player or not selectedName then
796 tinsert(export, "[b]"..addon.Ambiguate(player)..":[/b]\r\n")
797 tinsert(export, "[list=1]")
798 local first = true
799 for i, d in pairs(v) do
800 if selectedDate and selectedDate == d.date or not selectedDate then
801 if first then
802 first = false
803 else
804 tinsert(export, "[*]")
805 end
806 tinsert(export, "[url="..self:GetWowheadLinkFromItemLink(d.lootWon).."]"..d.lootWon.."[/url]"
807 .." Response: "..tostring(d.response)..".\r\n")
808 end
809 end
810 tinsert(export, "[/list]\r\n\r\n")
811 end
812 end
813 return table.concat(export)
814end
815
816-- BBCode, as supported by SMF
817function LootHistory:ExportBBCodeSMF()
818 local export = ""
819 for player, v in pairs(lootDB) do
820 if selectedName and selectedName == player or not selectedName then
821 export = export.."[b]"..addon.Ambiguate(player)..":[/b]\r\n"
822 export = export.."[list]"
823 for i, d in pairs(v) do
824 if selectedDate and selectedDate == d.date or not selectedDate then
825 export = export.."[*]"
826 export=export.."[url="..self:GetWowheadLinkFromItemLink(d.lootWon).."]"..d.lootWon.."[/url]"
827 .." Response: "..tostring(d.response)..".\r\n"
828 end
829 end
830 export=export.."[/list]\r\n\r\n"
831 end
832 end
833 return export
834end
835
836-- EQdkp Plus XML, primarily for Enjin import
837function LootHistory:ExportEQXML()
838 local export = "<raidlog><head><export><name>EQdkp Plus XML</name><version>1.0</version></export>"
839 .."<tracker><name>RCLootCouncil</name><version>"..addon.version.."</version></tracker>"
840 .."<gameinfo><game>World of Warcraft</game><language>"..GetLocale().."</language><charactername>"..UnitName("Player").."</charactername></gameinfo></head>\r\n"
841 .."<raiddata>\r\n"
842 local bossData = "\t<bosskills>\r\n"
843 local zoneData = "\t<zones>\r\n"
844 local itemsData = "\t<items>\r\n"
845 local membersData = {}
846 local raidData = {}
847 local earliest = 9999999999
848 local latest = 0
849 for player, v in pairs(lootDB) do
850 if selectedName and selectedName == player or not selectedName then
851 for i, d in pairs(v) do
852 if selectedDate and selectedDate == d.date or not selectedDate then
853 local day, month, year = strsplit("/", d.date, 3)
854 local hour,minute,second = strsplit(":",d.time,3)
855 local sinceEpoch = time({year = "20"..year, month = month, day = day,hour = hour,min = minute,sec=second})
856 itemsData = itemsData.."\t\t<item>\r\n"
857 .."\t\t\t<itemid>" .. addon:GetItemStringFromLink(d.lootWon) .. "</itemid>\r\n"
858 .."\t\t\t<name>" .. addon:GetItemNameFromLink(d.lootWon) .. "</name>\r\n"
859 .."\t\t\t<member>" .. addon.Ambiguate(player) .. "</member>\r\n"
860 .."\t\t\t<time>" .. sinceEpoch .. "</time>\r\n"
861 .."\t\t\t<count>1</count>\r\n"
862 .."\t\t\t<cost>" .. tostring(d.votes) .. "</cost>\r\n"
863 .."\t\t\t<note>" .. tostring(d.response) .. "</note>\r\n"
864 membersData[addon.Ambiguate(player)] = true
865 bossData = bossData .. "\t\t<bosskill>\r\n"
866 if d.boss then
867 itemsData = itemsData .. "\t\t\t<boss>" .. gsub(tostring(d.boss),",","").. "</boss>\r\n"
868 bossData = bossData.. "\t\t\t<name>"..gsub(tostring(d.boss),",","").."</name>\r\n"
869 else
870 itemsData = itemsData .. "\t\t\t<boss />\r\n"
871 bossData = bossData.. "\t\t\t<name>Unknown</name>\r\n"
872 end
873 if d.instance then
874 itemsData = itemsData .. "\t\t\t<zone>" .. gsub(tostring(d.instance),",","") .. "</zone>\r\n"
875 raidData[time({year="20"..year,month=month,day=day})] = gsub(tostring(d.instance),",","")
876 bossData = bossData.."\t\t\t<time>"..sinceEpoch.."</time>\r\n"
877 else
878 itemsData = itemsData .. "\t\t\t<zone />\r\n"
879 end
880 itemsData = itemsData.."\t\t</item>\r\n"
881 bossData = bossData .. "\t\t</bosskill>\r\n"
882 end
883 end
884 end
885 end
886 bossData = bossData .."\t</bosskills>\r\n"
887 for id, name in pairs(raidData) do
888 zoneData = zoneData .. "\t\t<zone>\r\n"
889 .. "\t\t\t<enter>"..id.."</enter>\r\n"
890 .. "\t\t\t<name>"..name.."</name>\r\n"
891 .. "\t\t\t<leave>"..(id + 26000).."</leave>\r\n"
892 .. "\t\t</zone>\r\n"
893 earliest = min(earliest, id)
894 latest = max(latest, id + 26000)
895 end
896 zoneData = zoneData .."\t</zones>\r\n"
897 itemsData = itemsData.. "\t</items>\r\n"
898 export = export..zoneData..bossData..itemsData
899 .."\t<members>\r\n"
900 for name in pairs(membersData) do
901 export = export.. "\t\t<member>\r\n"
902 .."\t\t\t<name>"..name.."</name>\r\n"
903 .."\t\t\t<times>\r\n"
904 .."\t\t\t\t<time type='join'>"..earliest.."</time>\r\n"
905 .."\t\t\t\t<time type='leave'>"..latest.."</time>\r\n"
906 .."\t\t\t</times>\r\n"
907 .."\t\t</member>\r\n"
908 end
909 export=export.. "\t</members>\r\n</raiddata></raidlog>\r\n"
910 return export
911end
912
913function LootHistory:ExportHTML()
914 local export = "html test"
915end
916
917-- Generates a serialized string containing the entire DB.
918-- For now it needs to be copied and pasted in another player's import field.
919function LootHistory:PlayerExport()
920 return self:EscapeItemLink(addon:Serialize(lootDB))
921end