v70

His Dad [08-20-20 - 22:43]
v70

Signed-off-by: His Dad <John@hisdad.org>
Filename
Doc/Changelog.txt
Doc/Installation.odt
Doc/Installation.pdf
HistOffline.lua
Prog/Strings.lua
Prog/make_box.lua
Prog/utility.lua
data/Strings.lua
data/de/de.lua
data/make_box.lua
data/ru/ru.lua
data/utility.lua
diff --git a/Doc/Changelog.txt b/Doc/Changelog.txt
index c560167..c748c62 100644
--- a/Doc/Changelog.txt
+++ b/Doc/Changelog.txt
@@ -1,10 +1,13 @@
-2020-08  Version 70
+2020-08-21  Version 70
 ==Known not to work with IUP-Lua 5.4===
+Updated Install Docs (Not Video)
 Southern Elsweyr Skill Quests  (Dragonhold)  The "Reformation" quest is not available. Case opened with Zos

 Update of the Account and Character Delete Scripts
 Character Delete now gives more information to distinguish between identically named characters.

+Moved some files to a new "Prog" directory
+

 2020-08-14 Version 69
 Wasn't recording the DLC Grp events correctly.
diff --git a/Doc/Installation.odt b/Doc/Installation.odt
index 03f7e27..26a47aa 100644
Binary files a/Doc/Installation.odt and b/Doc/Installation.odt differ
diff --git a/Doc/Installation.pdf b/Doc/Installation.pdf
index d1a4656..7752f1e 100644
Binary files a/Doc/Installation.pdf and b/Doc/Installation.pdf differ
diff --git a/HistOffline.lua b/HistOffline.lua
index 6b70a9d..653a789 100644
--- a/HistOffline.lua
+++ b/HistOffline.lua
@@ -22,6 +22,9 @@ end


 --force_lang = "ru"  --	or "de" or "fr" or "ru" for debugging
+--force_lang = "de"
+--force_lang = "fr"
+

 -- ========================
 version= "70"
@@ -51,7 +54,10 @@ Grp2_Order={"Group 2N","Group 2V","Group 2VH"}
 Ach_Detail = {}		--Forward Reference
 accounts = {}

-dofile "./data/utility.lua"		-- utility functions
+dofile "./Prog/utility.lua"		-- utility functions
+dofile "./Prog/Strings.lua"		-- String Handling
+
+
 dofile "./data/keys.lua"
 dofile "./data/DLC_Grp.lua"
 dofile "./data/DLC.lua"
@@ -63,7 +69,7 @@ dofile "./data/Veteran.lua"		-- Auxiliary Achievements for Vet Dungeons
 dofile "./data/WB.lua"
 dofile "./data/Trials.lua"
 dofile "./data/Special.lua"		-- Record of non standard Achievement ID's we need to keep.
-dofile "./data/Strings.lua"		-- String Handling
+

 log("Data Files loaded Ok")

@@ -371,7 +377,8 @@ load_visibility()
 				me.leveling_box:setcell(i,6, thischar.levels[j].map)
 			end
 		end
-		dofile "./data/make_box.lua"
+
+		dofile "./Prog/make_box.lua"

 		make_a_tab(playerID, dung, accounts[acc], "Public",Pub_Dat_Char)

@@ -448,7 +455,7 @@ load_visibility()

 		me.WB_box= Location_Box()
 		Populate_Dat(me.WB_box, WB_dat,me.ach)
-		log("Done WB Box")
+		log("Done WB Box")


 -- Create SkillQuest Achievements Box==========================
@@ -789,7 +796,7 @@ function select_account()
 				return account
 			end
 		else
-
+
 		account = accounts_list[1]		-- only 1 account, no need for Dialog
 		log("Account " ..account .. " selected.")
 		return account
diff --git a/Prog/Strings.lua b/Prog/Strings.lua
new file mode 100644
index 0000000..692def7
--- /dev/null
+++ b/Prog/Strings.lua
@@ -0,0 +1,100 @@
+--Functions relating to Strings
+
+-- take a string and break by spaces to an array
+split_w = function (astring)   -- String sentence
+    if (astring == nil) then
+		print("split_w nil")
+		return {}
+	end
+
+	local words = {}
+	for w in string.gmatch(astring, "%S+") do
+		table.insert(words,w)
+	end
+	return words	--array of the words
+end
+
+
+--[[
+test = "In Veteran White-Gold Tower, become completely engulfed in flame by the Planar Inhibitor's Heat Stroke attack before it completes its Daedric Catastrophe attack. Then stay alive until the Planar Inhibitor is defeated."
+split = split_w(test)
+print("Split_w returned a table of " .. #split .. "  words.")
+
+--print(dump(split))
+--]]
+
+flow_w = function( words_t, max_length )
+--[[ given an array of words  { [1]="TENTENTEN0", [2] = "FOUR", [3]="THREE"}
+	return an array of array of words so that the length is not greater than max_length
+with max_length 10
+  {  [1] = {[1]="TENTENTEN0"},  --array of 1 value
+      [2] = {[1]="FOUR", [2]="THREE"}  --array of 2 value
+	}
+--]]
+    if words_t == nil then
+		print("words_t is nil in flow_s")
+		return nil
+	end
+
+	if type(max_length) ~= "number" then
+		print("max_length is not a number in flow_s")
+		print("max_length is type " .. type(max_length) .. " in flow_s")
+		return nil
+	end
+
+
+
+	local result = {}
+	local line_t = {}
+	local line_len = 0
+	for i, word in ipairs (words_t) do
+	--	print (word)
+		if (string.len(word) + line_len) > max_length then
+			table.insert(result,line_t)
+			line_t = {}
+			line_len = 0
+		end
+		table.insert(line_t, word)
+		if line_len >  0 then
+			line_len = line_len + string.len(word) +1		--Allow for Spaces
+		else
+			line_len = line_len + string.len(word)
+		end
+	end
+	table.insert(result,line_t)
+	return result
+end
+
+
+join_s = function (words_tt)
+--given an array of array of words (output from flow_w) Produce a string with \n
+    if words_tt == nil then
+		print("parameter nil in join_s")
+		return nil
+	end
+
+	local result = ""
+
+	for i,words_t in ipairs (words_tt) do
+
+		for _,word in ipairs (words_t) do
+			result = result .. " " ..  word
+		end
+
+		if ( i ~= #words_tt) then
+			result = result .."\n"		-- Don't add Trailing \n  on last line.
+		end
+	end
+	return result
+end
+
+--[[
+
+test = "In Veteran White-Gold Tower, become completely engulfed in flame by the Planar Inhibitor's Heat Stroke attack before it completes its Daedric Catastrophe attack. Then stay alive until the Planar Inhibitor is defeated."
+split_words = split_w(test)
+--print("Split_w returned a table of " .. #split .. "  words.")
+flow_words = flow_w(split_words, 60)
+flow_string = join_s(flow_words)
+--print(dump(split))
+print(flow_string)
+--]]
diff --git a/Prog/make_box.lua b/Prog/make_box.lua
new file mode 100644
index 0000000..393090f
--- /dev/null
+++ b/Prog/make_box.lua
@@ -0,0 +1,192 @@
+leavewindow_cb = function (self)
+	blank_detail()
+end
+
+mousemove_cb = 	function (self, l, c)
+-- this is passed the box (self)  for char mode.
+-- ignore c.
+-- if this is a dungeon self.box.ach == nil
+
+	if (self == nil) then
+		print ("box_mousemove_cb2: self == nil, WTF?")
+		return
+	end
+
+	if (l == 0) then
+		blank_detail()
+		return
+	end
+
+-- we have previously added .dat to the box, this is the data file that looks up to Ach
+
+	if self.dat == nil then
+		detail_name.title = "box.dat is nil"
+		return
+	end
+
+
+	if (self.dat.id == nil) then
+		print ("box_mousemove_cb2: self.dat.id == nil")
+		return
+	end
+
+	local ach = self.dat.id[l]
+
+	if (ach == nil) then
+		blank_detail()
+		detail_name.title = "box_mousemove_cb2: No Achievement Number for line: " .. tostring(l)		-- Normal on Group 2 and Pub panel with empty spaces
+		return
+	end
+
+	if (Ach_Detail[ach] == nil) then
+		detail_name.title = "No Achievement in Ach_Detail for: " .. ach
+		return
+	end
+
+
+	detail_desc.title = Ach_Detail[ach].description
+
+	if (self.ach == nil) then
+		-- Dungeon, no completion info
+		detail_name.title = "(ID: " .. ach .. ")  "  ..  Ach_Detail[ach].name
+		return
+	end
+
+	--character mode, but char hasn't completed it.
+	if (self.ach[ach] == nil) then
+		detail_name.title = "(ID: " .. ach .. ")  "  ..  Ach_Detail[ach].name
+		return
+	end
+
+
+	--by default, has completed it, add date.
+	detail_name.title = "(ID: " .. ach .. ")  "  .. L.Completed .. os.date(dateformat,self.ach[ach].time) .. "   " .. Ach_Detail[ach].name
+
+end
+
+
+
+make_a_tab = function(playerID, dung_t,Account_t, mebox_s,dat_t)	-- Called per char on me[" "]
+
+
+
+	me[mebox_s] = {}
+	me[mebox_s].box = iup.matrixex {READONLY="YES",numcol=1, numlin=#dat_t.id, widthdef=120}
+	-- We put a lot of stuff under box as that is what gets passed to the mouse callbacks
+	me[mebox_s].box.dat = dat_t
+	me[mebox_s].box.mousemove_cb = mousemove_cb
+	me[mebox_s].box.leavewindow_cb = leavewindow_cb
+
+
+
+	me[mebox_s].box.name= mebox_s
+	me[mebox_s].box.ach = thischar.ach		-- not on dung boxes
+
+	iup.SetAttribute(me[mebox_s].box,  "BGCOLOR" , BG_Colour_Not_Complete)
+
+	--set lines Heading
+	me[mebox_s].box:setcell(0,0, L.Achievements )
+	iup.SetAttribute(me[mebox_s].box,  "WIDTH0", 180)	-- Width of Achievement
+
+	--Load Lines
+	for line, Ach in ipairs (dat_t.id) do  -- Load text
+	if (Ach_Detail[Ach] == nil) then
+	        print(mebox_s)
+			print("Achievement " .. Ach .. " has no record in Ach_Detail")
+		else
+			me[mebox_s].box:setcell(line,0,Ach_Detail[Ach].name)
+		end
+	end
+
+	me[mebox_s].tab = iup.vbox {	["tabtitle"] =L.box[mebox_s],
+									--	iup.label{title=L.PubLab,expand="HORIZONTAL"},
+										me[mebox_s].box,
+										iup.fill{}
+									}
+
+	iup.Append(me.data_tabs,me[mebox_s].tab)
+
+
+	--Make Dungeon box, but only one
+	-- a table for each dungeon to which we add characters
+
+	if dung_t[mebox_s] == nil then
+		--create and initialise to none for all player_IDs
+		dung_t[mebox_s] = {}
+
+		dung_t[mebox_s].keys = {}
+
+	-- Set to none for all chars.
+
+
+		for line, _ in ipairs(dat_t.id) do
+			dung_t[mebox_s].keys[line] = {}
+			for _,playerID in ipairs(Account_t.AllplayerIDs) do
+			--print("Key: " .. key .. ", playerID: " .. playerID)
+			dung_t[mebox_s].keys[line][playerID] = false
+			end
+		end
+
+		---
+		dung_t[mebox_s].box = iup.matrixex {READONLY="YES", numcol=#Account_t.AllplayerIDs,  numlin=#dat_t.id, widthdef=120}
+		dung_t[mebox_s].box.dat = dat_t
+		dung_t[mebox_s].box.mousemove_cb = mousemove_cb
+		dung_t[mebox_s].box.leavewindow_cb = leavewindow_cb
+
+		dung_t[mebox_s].box.name= mebox_s		-- when we are in the box with the mouse CB its possible to see who we are.
+
+		iup.SetAttribute(dung_t[mebox_s].box,  "BGCOLOR" , BG_Colour_Not_Complete)
+
+		--set lines Heading
+		dung_t[mebox_s].box:setcell(0,0, L.Achievements )
+		iup.SetAttribute(dung_t[mebox_s].box,  "WIDTH0", 180)
+
+		--Load Lines (Dungeon Ach names)
+		for line,Ach in ipairs(dat_t.id) do
+			-- print("Dungeon Mode  Line: "..  line.. "  " .. "Ach: ".. Ach)
+			dung_t[mebox_s].box:setcell(line, 0, Ach_Detail[Ach].name)
+		end
+
+		--Create Columns for Chars
+		for col ,playerID in ipairs(Account_t.AllplayerIDs) do
+			dung_t[mebox_s].box:setcell(0, col, playerNames[playerID])
+		end
+
+		--record it
+		table.insert(Account_t.alldungeons,dung_t[mebox_s])
+
+		if L.box[mebox_s] == nil then
+			print("L.box[mebox_s] is nill")
+		end
+		-- how to display it
+		dung_t[mebox_s].tab = iup.vbox {
+				["tabtitle"] =L.box[mebox_s],
+				dung_t[mebox_s].box,
+				iup.fill{},
+			}
+		-- and record that
+		iup.Append(Account_t.dung_tabs, dung_t[mebox_s].tab)
+	end	-- unique dungeon creation
+--
+-- Ok that's the setup done, lets load the data for that "me"
+-- Goes in to the me and also the dungeon by character
+
+	for line, Ach in pairs (dat_t.id) do
+		local bgcolour = "BGCOLOR" .. tostring(line) .. ":*"
+		if thischar.ach[Ach] ~= nil then		-- yes I have it..
+			iup.SetAttribute(me[mebox_s].box, bgcolour, BG_Colour_Complete)
+			me[mebox_s].box:setcell(line,1, L.YesLabel)
+
+			-- Add char to dungeon
+			if dung_t[mebox_s].keys[line] == nil then	-- Add if needed
+				dung_t[mebox_s].keys[line]  = {}
+				print("Shouldn't be needed")
+			end
+			dung_t[mebox_s].keys[line][playerID] = true
+		else
+			iup.SetAttribute(me[mebox_s].box, bgcolour, BG_Colour_Not_Complete)
+			me[mebox_s].box:setcell(line,1, L.NoLabel)
+		end
+	end
+
+end
\ No newline at end of file
diff --git a/Prog/utility.lua b/Prog/utility.lua
new file mode 100644
index 0000000..666b432
--- /dev/null
+++ b/Prog/utility.lua
@@ -0,0 +1,440 @@
+
+dump = function (o)
+   if type(o) == 'table' then
+      local s = '{ '
+      for k,v in pairs(o) do
+         if type(k) ~= 'number' then k = '"'..k..'"' end
+         s = s .. '['..k..'] = ' .. dump(v) .. ','
+      end
+      return s .. '} '
+   else
+      return tostring(o)
+   end
+end
+
+dumpf = function (o)
+	outfile=io.open("dump-file.txt", "w")
+
+	outfile:write(dump(o))
+	outfile:close()
+end
+
+-- Minimally effective quoting
+quote = function (astring)
+    local quoted1 = string.gsub(astring,'\"', '\\"')
+    local quoted2 = '"' .. string.gsub(quoted1,"%'", "\\'") .. '"'
+	local linefeed = string.gsub(quoted2,"\n", "\\n")
+	return linefeed
+end
+
+
+write_saved = function (o)
+--	local escaped
+   if type(o) == 'table' then
+      local s = '{\n'
+      for k,v in pairs(o) do
+         if type(k) ~= 'number' then
+		 k = '"'..k..'"'
+		 end
+         s = s .. '['..k..'] = ' .. write_saved(v) .. ',\n'
+      end
+      return s .. '}\n'
+   elseif
+		type(o) == 'string' then
+		return quote(o)
+   else
+		return tostring(o)
+   end
+end
+
+_size = function (t)		-- return number of elements in table
+	local i = 0
+	for _,_ in pairs(t) do
+		i = i +1
+	end
+	return i
+end
+
+
+blank_detail = function()
+			detail_name.title = ""
+			detail_desc.title = ""
+end
+
+load_visibility=function()
+
+	local infile = io.open(Settings_s, "r")
+
+	if infile == nil then
+		print (Settings_s .. "  not there, that's ok.")
+		return false
+	end
+	io.close(infile)
+
+	local acc, playerID, visible
+
+	for line in io.lines(Settings_s) do
+
+		acc,  playerID, visible =  string.match(line, "(%S+) (%S+) (%S+)")
+		-- char might have been deleted or might be new setup.
+
+		if(accounts[acc] == nil) then return false end
+		if(accounts[acc].player == nil) then return false end
+
+		if(accounts[acc].player[playerID] ~= nil) then
+			if visible == "true" then
+				accounts[acc].player[playerID].visible= true
+			else
+				accounts[acc].player[playerID].visible= false
+			end
+		end
+	end
+end
+
+
+save_visibility=function()
+	local outfile = io.open(Settings_s, "w")
+	for acc,Account_t in pairs (accounts) do
+		for playerID, player_t in pairs (Account_t.player) do
+
+			if outfile == nil then
+				print ("Couldn't open " .. Settings_s .. "  file for writing.")
+				return false
+			end
+
+			outfile:write(acc .. " " .. playerID)
+
+			if (player_t.visible) then
+				outfile:write(" true\n")
+			else
+				outfile:write(" false\n")
+			end
+		end	 --player
+	end --ac
+	io.close(outfile)
+end
+
+generate_id=function()
+--write a combined list of achievement id we look for to add to the in-game part (cut and paste) for filtering
+    local unique_id= {}   -- (id,true}
+	print("Generating ids.lua")
+	local outfile=io.open("data/ids.lua", "w")
+
+	if outfile == nil then
+		print ("Couldn't open ids.lua file for writing.")
+	end
+	outfile:write("hist.IDVersion=" .. quote(version) .. "\n")
+
+	outfile:write("hist.IDs = {" .. "\n")
+
+	for _,i in ipairs(Group_Order) do
+		outfile:write("--  " .. i ..  "\n")
+		for _,j in ipairs (Group_Dat[i].id) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+
+		end
+	end
+
+	for _,i in ipairs(Trials_Order) do
+		outfile:write("-- Trial " .. i .. "\n")
+		for _,j in ipairs (Trials_Dat[i].id) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+		end
+	end
+
+	outfile:write("-- DLC Grp" .. "\n")
+	for _,j in pairs (DLC_Grp_Dat.id) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+	end
+
+	outfile:write("-- SQ " .. "\n")
+	for j,_ in pairs (SQ_dat) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+	end
+
+	outfile:write("-- Pub " .. "\n")
+	for _,j in ipairs (Pub_Dat_Char.id) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+	end
+
+	outfile:write("-- WB " .. "\n")
+	for j,_ in pairs (WB_dat) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+	end
+
+	outfile:write("-- Specials " .. "\n")
+	for j,_ in pairs (Special_dat) do
+		    if unique_id[j] == nil then
+				unique_id[j] = true
+				outfile:write("[" .. j .. "] = true,\n")
+			end
+	end
+
+	outfile:write("}" .. "\n")
+	outfile:close()
+end
+
+
+--Run through Location data to get the info needed to dimension the WB and SQ boxes
+Location_Box = function (name)
+
+	log("Location Box ")
+	local cols = {}
+	local lines = {}
+	local Areas = {}
+	--Summarise
+	for i,entry in ipairs(Locations2Key) do
+		if lines[entry.L] == nil then  lines[entry.L] = true end
+		if cols[entry.C] == nil then  cols[entry.C] = entry.Area end	-- Note the Area for the Col
+		if Areas[entry.Area] == nil then  Areas[entry.Area] = true end
+	end
+	--[[
+	print("Cols:  " .. tostring(#cols))
+	print("Lines: " .. #lines)
+	print("Areas: " .. #Areas)
+	--]]
+
+	local return_t = iup.matrix{}
+	return_t.numlin=#lines
+	return_t.numcol=#cols
+	return_t.widthdef=100
+
+	iup.SetAttribute(return_t, "READONLY", "YES")
+	iup.SetAttribute(return_t, "ALIGNMENT0", "ACENTER")
+	-- Set Headings
+	for col, Area in ipairs (cols) do
+	--	print("Col:  " .. col .. ",  Area:  "  .. Area )
+		return_t:setcell(0,col, Area_names[Area].long)
+	--	print("Set Heading: " .. Area_names[Area].long)
+	end
+
+	--Set Lines
+	return_t:setcell(0,0, L.Zone)
+	for line,_ in ipairs(lines) do
+		return_t:setcell(line,0, tostring(line))
+	--	iup.SetAttribute(return_t,  "FGCOLOR*:".. tostring(line), FG_Colour_Not_Complete)
+	--	iup.SetAttribute(return_t,  "BGCOLOR*:".. tostring(line), BG_Colour_Not_Complete)
+	end
+
+	--optionally set Contents
+	if (type(name) ~= "string") then	return return_t end
+--	get
+
+	return return_t
+end
+
+
+-- take WB_Dat or SQ_Dat, which are indexed on id and return array
+-- lines_t = table[key]  of  {done,of ids{id=false, id=false}}    then we can scan and set id to true if found
+Reverse_Dat = function (dat_t,ids_t)
+	if type(dat_t) ~= "table" then
+		print("Reverse_Dat dat_t is not a table.")
+		return
+	end
+
+	if type(ids_t) ~= "table" then
+		print("Reverse_Dat ids_t is not a table.")
+		return
+	end
+
+
+
+	local lines_t = {}	-- accumulate as LC Key
+	for id, location_t in pairs (dat_t) do		--location is index into Locations2key and Locations2
+		if (Locations2Key[location_t.Location] ==nil) then
+			print("Reverse_dat")
+			print("Location " .. Location .. " not found in Locations2Key")
+			return
+		end
+
+		local key = Locations2Key[location_t.Location].key
+		local link= location_t["link1"] ~= nil
+		local gotid = (type(ids_t[id]) == "table")		-- do we have this achievement? nil is no. table is yes
+		if lines_t[key] == nil then	--	add new line
+			lines_t[key] = {}
+			lines_t[key].L = Locations2Key[location_t.Location].L
+			lines_t[key].C = Locations2Key[location_t.Location].C
+			lines_t[key].ids={}
+			lines_t[key].ids[id] = gotid
+			lines_t[key].of =1
+			lines_t[key].link=link		-- true if this key has Link1 (SQ)
+			if gotid then lines_t[key].done = 1 else lines_t[key].done = 0 end
+		else
+			lines_t[key].ids[id] = gotid	--	add id to existing line
+			lines_t[key].of = lines_t[key].of + 1
+			if gotid then lines_t[key].done = lines_t[key].done + 1  end
+		end
+	end
+	return lines_t
+end
+
+Populate_Dat = function(matrix, dat_t,ids_t)
+		local detail_box={}
+		local Rev = Reverse_Dat(dat_t,ids_t)
+		matrix.Detail_zbox = iup.zbox{}		-- to which we append detail_box[key]
+
+
+		for i,key_t  in ipairs (Locations2Key) do		--	{ key="1:1",L=1,C=1,Area=1},		--	name="Stonefalls"}
+			local name = Locations2[i].name
+			if name == nil then
+				print("Populate_Dat: Locations2[i].name returns nil for i: " .. i)
+				return
+			end
+			local line =Rev[key_t.key]
+			if line == nil then 	-- this is ok. Location without a WB or SQ
+			else
+				matrix:setcell(line.L,line.C,name .. " (" .. line.done .. "/" .. line.of .. ")")
+				if line.done == line.of then
+					iup.SetAttribute(matrix, "FGCOLOR" .. key_t.key , FG_Colour_Complete)  -- Yup
+					iup.SetAttribute(matrix, "BGCOLOR" .. key_t.key , BG_Colour_Complete)
+				end
+				if line.link then
+					detail_box[key_t.key]=iup.matrix{numcol=3, numcol_visible=3,numlin=line.of}		--Detail box for that key "1:1"
+					detail_box[key_t.key]:setcell(0,3, L.Link)
+					iup.SetAttribute(detail_box[key_t.key], "WIDTH3", "200")
+					iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT3", "ALEFT")
+				else
+					detail_box[key_t.key]=iup.matrix{numcol=2, numcol_visible=2,numlin=line.of}		--Detail box for that key "1:1"
+				end
+				detail_box[key_t.key]:setcell(0,0, L.Ach_ID)
+				detail_box[key_t.key]:setcell(0,1, L.Name)
+				detail_box[key_t.key]:setcell(0,2, L.Detail)
+				iup.SetAttribute(detail_box[key_t.key], "READONLY", "YES")
+				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT0", "ACENTER")
+				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENTLIN0", "ALEFT")
+				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT1", "ALEFT")
+				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT2", "ALEFT")
+				iup.SetAttribute(detail_box[key_t.key], "WIDTH0", "30")
+				iup.SetAttribute(detail_box[key_t.key], "WIDTH1", "150")
+				iup.SetAttribute(detail_box[key_t.key], "WIDTH2", "300")
+				-- Fill in the details
+				local j=1		--count the lines
+				for id,isdone in pairs (line.ids) do
+					if Ach_Detail[id] == nil then
+						name = "UNKN"
+						Desc = ""
+					else
+						name = Ach_Detail[id].name
+						Desc = Ach_Detail[id].description
+					end
+					detail_box[key_t.key]:setcell(j,0,id)
+					detail_box[key_t.key]:setcell(j,1, name)
+					detail_box[key_t.key]:setcell(j,2, Desc)
+					if line.link then
+						detail_box[key_t.key]:setcell(j,3, dat_t[id]["link1"])
+					end
+					if isdone then
+						iup.SetAttribute(detail_box[key_t.key],"BGCOLOR" .. tostring(j) .. ":*", BG_Colour_Complete)		-- Yes
+					end
+					j=j+1
+				end
+
+				local thisDetail =detail_box[key_t.key]
+				function thisDetail:click_cb (L,C)
+					if C == 3 then
+						iup.Help(self:getcell(L,C))		-- Launch Browser
+					else
+						return IUP_IGNORE
+					end
+				end
+				iup.Append(matrix.Detail_zbox, detail_box[key_t.key])
+			end
+		end
+		function matrix:enteritem_cb(L,C)
+			matrix.Detail_zbox.value = detail_box[tostring(L) ..":" .. tostring(C)]
+		end
+end
+
+append=function (destination_t,source_t)
+--takes 2 arrays. source_t is appended to destination_t
+	if type(destination_t) ~= "table" then
+		print("error (append), destination_t is not a table")
+		return
+	end
+
+	if type(source_t) ~= "table" then
+		print("error (append), source_t is not a table")
+		return
+	end
+
+	for line, Ach in ipairs (source_t) do
+		table.insert(destination_t,Ach)
+	end
+end
+
+
+--for filtering
+select_box=function (account_t)
+
+-- account_t   is accounts[acc]
+
+local marks_t = {}
+local names_t = {}
+for _,playerID in  ipairs(account_t.AllplayerIDs) do
+
+	if account_t.player[playerID].visible then
+		table.insert(marks_t,1)
+	else
+		table.insert(marks_t,0)
+	end
+	table.insert(names_t,account_t.player[playerID].name)
+
+end
+
+local size = #names_t
+
+local error = iup.ListDialog(2,"Show Characters",size, names_t,0,1,size, marks_t)
+
+
+if error == -1 then
+	return false
+end
+
+
+for i,playerID in  ipairs(account_t.AllplayerIDs) do
+
+	if (marks_t[i] == 0) then
+		account_t.player[playerID].visible = false
+	else
+		account_t.player[playerID].visible = true
+	end
+
+end
+
+
+
+
+for _, ADung in pairs (account_t.alldungeons) do	-- for each dungeon
+
+	for col,playerID in  ipairs(account_t.AllplayerIDs) do
+
+		if account_t.player[playerID].visible then
+			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"100")
+		else
+			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"0")
+		end
+	end
+end
+
+save_visibility()
+
+return true
+end
diff --git a/data/Strings.lua b/data/Strings.lua
deleted file mode 100644
index 692def7..0000000
--- a/data/Strings.lua
+++ /dev/null
@@ -1,100 +0,0 @@
---Functions relating to Strings
-
--- take a string and break by spaces to an array
-split_w = function (astring)   -- String sentence
-    if (astring == nil) then
-		print("split_w nil")
-		return {}
-	end
-
-	local words = {}
-	for w in string.gmatch(astring, "%S+") do
-		table.insert(words,w)
-	end
-	return words	--array of the words
-end
-
-
---[[
-test = "In Veteran White-Gold Tower, become completely engulfed in flame by the Planar Inhibitor's Heat Stroke attack before it completes its Daedric Catastrophe attack. Then stay alive until the Planar Inhibitor is defeated."
-split = split_w(test)
-print("Split_w returned a table of " .. #split .. "  words.")
-
---print(dump(split))
---]]
-
-flow_w = function( words_t, max_length )
---[[ given an array of words  { [1]="TENTENTEN0", [2] = "FOUR", [3]="THREE"}
-	return an array of array of words so that the length is not greater than max_length
-with max_length 10
-  {  [1] = {[1]="TENTENTEN0"},  --array of 1 value
-      [2] = {[1]="FOUR", [2]="THREE"}  --array of 2 value
-	}
---]]
-    if words_t == nil then
-		print("words_t is nil in flow_s")
-		return nil
-	end
-
-	if type(max_length) ~= "number" then
-		print("max_length is not a number in flow_s")
-		print("max_length is type " .. type(max_length) .. " in flow_s")
-		return nil
-	end
-
-
-
-	local result = {}
-	local line_t = {}
-	local line_len = 0
-	for i, word in ipairs (words_t) do
-	--	print (word)
-		if (string.len(word) + line_len) > max_length then
-			table.insert(result,line_t)
-			line_t = {}
-			line_len = 0
-		end
-		table.insert(line_t, word)
-		if line_len >  0 then
-			line_len = line_len + string.len(word) +1		--Allow for Spaces
-		else
-			line_len = line_len + string.len(word)
-		end
-	end
-	table.insert(result,line_t)
-	return result
-end
-
-
-join_s = function (words_tt)
---given an array of array of words (output from flow_w) Produce a string with \n
-    if words_tt == nil then
-		print("parameter nil in join_s")
-		return nil
-	end
-
-	local result = ""
-
-	for i,words_t in ipairs (words_tt) do
-
-		for _,word in ipairs (words_t) do
-			result = result .. " " ..  word
-		end
-
-		if ( i ~= #words_tt) then
-			result = result .."\n"		-- Don't add Trailing \n  on last line.
-		end
-	end
-	return result
-end
-
---[[
-
-test = "In Veteran White-Gold Tower, become completely engulfed in flame by the Planar Inhibitor's Heat Stroke attack before it completes its Daedric Catastrophe attack. Then stay alive until the Planar Inhibitor is defeated."
-split_words = split_w(test)
---print("Split_w returned a table of " .. #split .. "  words.")
-flow_words = flow_w(split_words, 60)
-flow_string = join_s(flow_words)
---print(dump(split))
-print(flow_string)
---]]
diff --git a/data/de/de.lua b/data/de/de.lua
index d10ff59..b23977c 100644
--- a/data/de/de.lua
+++ b/data/de/de.lua
@@ -58,6 +58,8 @@ Area_names = {
 			long = "Scalebreaker"},
 	[19] = { short= "Wrathstone",
 			medium = "Wrathstone",
+			long = "Wrathstone"},
+
 	[20] = { short= "Gramsturms",
 			medium = "Gramsturms",
 			long = "Gramsturms"},
diff --git a/data/make_box.lua b/data/make_box.lua
deleted file mode 100644
index 393090f..0000000
--- a/data/make_box.lua
+++ /dev/null
@@ -1,192 +0,0 @@
-leavewindow_cb = function (self)
-	blank_detail()
-end
-
-mousemove_cb = 	function (self, l, c)
--- this is passed the box (self)  for char mode.
--- ignore c.
--- if this is a dungeon self.box.ach == nil
-
-	if (self == nil) then
-		print ("box_mousemove_cb2: self == nil, WTF?")
-		return
-	end
-
-	if (l == 0) then
-		blank_detail()
-		return
-	end
-
--- we have previously added .dat to the box, this is the data file that looks up to Ach
-
-	if self.dat == nil then
-		detail_name.title = "box.dat is nil"
-		return
-	end
-
-
-	if (self.dat.id == nil) then
-		print ("box_mousemove_cb2: self.dat.id == nil")
-		return
-	end
-
-	local ach = self.dat.id[l]
-
-	if (ach == nil) then
-		blank_detail()
-		detail_name.title = "box_mousemove_cb2: No Achievement Number for line: " .. tostring(l)		-- Normal on Group 2 and Pub panel with empty spaces
-		return
-	end
-
-	if (Ach_Detail[ach] == nil) then
-		detail_name.title = "No Achievement in Ach_Detail for: " .. ach
-		return
-	end
-
-
-	detail_desc.title = Ach_Detail[ach].description
-
-	if (self.ach == nil) then
-		-- Dungeon, no completion info
-		detail_name.title = "(ID: " .. ach .. ")  "  ..  Ach_Detail[ach].name
-		return
-	end
-
-	--character mode, but char hasn't completed it.
-	if (self.ach[ach] == nil) then
-		detail_name.title = "(ID: " .. ach .. ")  "  ..  Ach_Detail[ach].name
-		return
-	end
-
-
-	--by default, has completed it, add date.
-	detail_name.title = "(ID: " .. ach .. ")  "  .. L.Completed .. os.date(dateformat,self.ach[ach].time) .. "   " .. Ach_Detail[ach].name
-
-end
-
-
-
-make_a_tab = function(playerID, dung_t,Account_t, mebox_s,dat_t)	-- Called per char on me[" "]
-
-
-
-	me[mebox_s] = {}
-	me[mebox_s].box = iup.matrixex {READONLY="YES",numcol=1, numlin=#dat_t.id, widthdef=120}
-	-- We put a lot of stuff under box as that is what gets passed to the mouse callbacks
-	me[mebox_s].box.dat = dat_t
-	me[mebox_s].box.mousemove_cb = mousemove_cb
-	me[mebox_s].box.leavewindow_cb = leavewindow_cb
-
-
-
-	me[mebox_s].box.name= mebox_s
-	me[mebox_s].box.ach = thischar.ach		-- not on dung boxes
-
-	iup.SetAttribute(me[mebox_s].box,  "BGCOLOR" , BG_Colour_Not_Complete)
-
-	--set lines Heading
-	me[mebox_s].box:setcell(0,0, L.Achievements )
-	iup.SetAttribute(me[mebox_s].box,  "WIDTH0", 180)	-- Width of Achievement
-
-	--Load Lines
-	for line, Ach in ipairs (dat_t.id) do  -- Load text
-	if (Ach_Detail[Ach] == nil) then
-	        print(mebox_s)
-			print("Achievement " .. Ach .. " has no record in Ach_Detail")
-		else
-			me[mebox_s].box:setcell(line,0,Ach_Detail[Ach].name)
-		end
-	end
-
-	me[mebox_s].tab = iup.vbox {	["tabtitle"] =L.box[mebox_s],
-									--	iup.label{title=L.PubLab,expand="HORIZONTAL"},
-										me[mebox_s].box,
-										iup.fill{}
-									}
-
-	iup.Append(me.data_tabs,me[mebox_s].tab)
-
-
-	--Make Dungeon box, but only one
-	-- a table for each dungeon to which we add characters
-
-	if dung_t[mebox_s] == nil then
-		--create and initialise to none for all player_IDs
-		dung_t[mebox_s] = {}
-
-		dung_t[mebox_s].keys = {}
-
-	-- Set to none for all chars.
-
-
-		for line, _ in ipairs(dat_t.id) do
-			dung_t[mebox_s].keys[line] = {}
-			for _,playerID in ipairs(Account_t.AllplayerIDs) do
-			--print("Key: " .. key .. ", playerID: " .. playerID)
-			dung_t[mebox_s].keys[line][playerID] = false
-			end
-		end
-
-		---
-		dung_t[mebox_s].box = iup.matrixex {READONLY="YES", numcol=#Account_t.AllplayerIDs,  numlin=#dat_t.id, widthdef=120}
-		dung_t[mebox_s].box.dat = dat_t
-		dung_t[mebox_s].box.mousemove_cb = mousemove_cb
-		dung_t[mebox_s].box.leavewindow_cb = leavewindow_cb
-
-		dung_t[mebox_s].box.name= mebox_s		-- when we are in the box with the mouse CB its possible to see who we are.
-
-		iup.SetAttribute(dung_t[mebox_s].box,  "BGCOLOR" , BG_Colour_Not_Complete)
-
-		--set lines Heading
-		dung_t[mebox_s].box:setcell(0,0, L.Achievements )
-		iup.SetAttribute(dung_t[mebox_s].box,  "WIDTH0", 180)
-
-		--Load Lines (Dungeon Ach names)
-		for line,Ach in ipairs(dat_t.id) do
-			-- print("Dungeon Mode  Line: "..  line.. "  " .. "Ach: ".. Ach)
-			dung_t[mebox_s].box:setcell(line, 0, Ach_Detail[Ach].name)
-		end
-
-		--Create Columns for Chars
-		for col ,playerID in ipairs(Account_t.AllplayerIDs) do
-			dung_t[mebox_s].box:setcell(0, col, playerNames[playerID])
-		end
-
-		--record it
-		table.insert(Account_t.alldungeons,dung_t[mebox_s])
-
-		if L.box[mebox_s] == nil then
-			print("L.box[mebox_s] is nill")
-		end
-		-- how to display it
-		dung_t[mebox_s].tab = iup.vbox {
-				["tabtitle"] =L.box[mebox_s],
-				dung_t[mebox_s].box,
-				iup.fill{},
-			}
-		-- and record that
-		iup.Append(Account_t.dung_tabs, dung_t[mebox_s].tab)
-	end	-- unique dungeon creation
---
--- Ok that's the setup done, lets load the data for that "me"
--- Goes in to the me and also the dungeon by character
-
-	for line, Ach in pairs (dat_t.id) do
-		local bgcolour = "BGCOLOR" .. tostring(line) .. ":*"
-		if thischar.ach[Ach] ~= nil then		-- yes I have it..
-			iup.SetAttribute(me[mebox_s].box, bgcolour, BG_Colour_Complete)
-			me[mebox_s].box:setcell(line,1, L.YesLabel)
-
-			-- Add char to dungeon
-			if dung_t[mebox_s].keys[line] == nil then	-- Add if needed
-				dung_t[mebox_s].keys[line]  = {}
-				print("Shouldn't be needed")
-			end
-			dung_t[mebox_s].keys[line][playerID] = true
-		else
-			iup.SetAttribute(me[mebox_s].box, bgcolour, BG_Colour_Not_Complete)
-			me[mebox_s].box:setcell(line,1, L.NoLabel)
-		end
-	end
-
-end
\ No newline at end of file
diff --git a/data/ru/ru.lua b/data/ru/ru.lua
index 3f7251b..667fe27 100644
--- a/data/ru/ru.lua
+++ b/data/ru/ru.lua
@@ -1,5 +1,4 @@

-
 dateformat = "%Y-%m-%d, %H:%M"

 Area_names = {
diff --git a/data/utility.lua b/data/utility.lua
deleted file mode 100644
index 666b432..0000000
--- a/data/utility.lua
+++ /dev/null
@@ -1,440 +0,0 @@
-
-dump = function (o)
-   if type(o) == 'table' then
-      local s = '{ '
-      for k,v in pairs(o) do
-         if type(k) ~= 'number' then k = '"'..k..'"' end
-         s = s .. '['..k..'] = ' .. dump(v) .. ','
-      end
-      return s .. '} '
-   else
-      return tostring(o)
-   end
-end
-
-dumpf = function (o)
-	outfile=io.open("dump-file.txt", "w")
-
-	outfile:write(dump(o))
-	outfile:close()
-end
-
--- Minimally effective quoting
-quote = function (astring)
-    local quoted1 = string.gsub(astring,'\"', '\\"')
-    local quoted2 = '"' .. string.gsub(quoted1,"%'", "\\'") .. '"'
-	local linefeed = string.gsub(quoted2,"\n", "\\n")
-	return linefeed
-end
-
-
-write_saved = function (o)
---	local escaped
-   if type(o) == 'table' then
-      local s = '{\n'
-      for k,v in pairs(o) do
-         if type(k) ~= 'number' then
-		 k = '"'..k..'"'
-		 end
-         s = s .. '['..k..'] = ' .. write_saved(v) .. ',\n'
-      end
-      return s .. '}\n'
-   elseif
-		type(o) == 'string' then
-		return quote(o)
-   else
-		return tostring(o)
-   end
-end
-
-_size = function (t)		-- return number of elements in table
-	local i = 0
-	for _,_ in pairs(t) do
-		i = i +1
-	end
-	return i
-end
-
-
-blank_detail = function()
-			detail_name.title = ""
-			detail_desc.title = ""
-end
-
-load_visibility=function()
-
-	local infile = io.open(Settings_s, "r")
-
-	if infile == nil then
-		print (Settings_s .. "  not there, that's ok.")
-		return false
-	end
-	io.close(infile)
-
-	local acc, playerID, visible
-
-	for line in io.lines(Settings_s) do
-
-		acc,  playerID, visible =  string.match(line, "(%S+) (%S+) (%S+)")
-		-- char might have been deleted or might be new setup.
-
-		if(accounts[acc] == nil) then return false end
-		if(accounts[acc].player == nil) then return false end
-
-		if(accounts[acc].player[playerID] ~= nil) then
-			if visible == "true" then
-				accounts[acc].player[playerID].visible= true
-			else
-				accounts[acc].player[playerID].visible= false
-			end
-		end
-	end
-end
-
-
-save_visibility=function()
-	local outfile = io.open(Settings_s, "w")
-	for acc,Account_t in pairs (accounts) do
-		for playerID, player_t in pairs (Account_t.player) do
-
-			if outfile == nil then
-				print ("Couldn't open " .. Settings_s .. "  file for writing.")
-				return false
-			end
-
-			outfile:write(acc .. " " .. playerID)
-
-			if (player_t.visible) then
-				outfile:write(" true\n")
-			else
-				outfile:write(" false\n")
-			end
-		end	 --player
-	end --ac
-	io.close(outfile)
-end
-
-generate_id=function()
---write a combined list of achievement id we look for to add to the in-game part (cut and paste) for filtering
-    local unique_id= {}   -- (id,true}
-	print("Generating ids.lua")
-	local outfile=io.open("data/ids.lua", "w")
-
-	if outfile == nil then
-		print ("Couldn't open ids.lua file for writing.")
-	end
-	outfile:write("hist.IDVersion=" .. quote(version) .. "\n")
-
-	outfile:write("hist.IDs = {" .. "\n")
-
-	for _,i in ipairs(Group_Order) do
-		outfile:write("--  " .. i ..  "\n")
-		for _,j in ipairs (Group_Dat[i].id) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-
-		end
-	end
-
-	for _,i in ipairs(Trials_Order) do
-		outfile:write("-- Trial " .. i .. "\n")
-		for _,j in ipairs (Trials_Dat[i].id) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-		end
-	end
-
-	outfile:write("-- DLC Grp" .. "\n")
-	for _,j in pairs (DLC_Grp_Dat.id) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-	end
-
-	outfile:write("-- SQ " .. "\n")
-	for j,_ in pairs (SQ_dat) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-	end
-
-	outfile:write("-- Pub " .. "\n")
-	for _,j in ipairs (Pub_Dat_Char.id) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-	end
-
-	outfile:write("-- WB " .. "\n")
-	for j,_ in pairs (WB_dat) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-	end
-
-	outfile:write("-- Specials " .. "\n")
-	for j,_ in pairs (Special_dat) do
-		    if unique_id[j] == nil then
-				unique_id[j] = true
-				outfile:write("[" .. j .. "] = true,\n")
-			end
-	end
-
-	outfile:write("}" .. "\n")
-	outfile:close()
-end
-
-
---Run through Location data to get the info needed to dimension the WB and SQ boxes
-Location_Box = function (name)
-
-	log("Location Box ")
-	local cols = {}
-	local lines = {}
-	local Areas = {}
-	--Summarise
-	for i,entry in ipairs(Locations2Key) do
-		if lines[entry.L] == nil then  lines[entry.L] = true end
-		if cols[entry.C] == nil then  cols[entry.C] = entry.Area end	-- Note the Area for the Col
-		if Areas[entry.Area] == nil then  Areas[entry.Area] = true end
-	end
-	--[[
-	print("Cols:  " .. tostring(#cols))
-	print("Lines: " .. #lines)
-	print("Areas: " .. #Areas)
-	--]]
-
-	local return_t = iup.matrix{}
-	return_t.numlin=#lines
-	return_t.numcol=#cols
-	return_t.widthdef=100
-
-	iup.SetAttribute(return_t, "READONLY", "YES")
-	iup.SetAttribute(return_t, "ALIGNMENT0", "ACENTER")
-	-- Set Headings
-	for col, Area in ipairs (cols) do
-	--	print("Col:  " .. col .. ",  Area:  "  .. Area )
-		return_t:setcell(0,col, Area_names[Area].long)
-	--	print("Set Heading: " .. Area_names[Area].long)
-	end
-
-	--Set Lines
-	return_t:setcell(0,0, L.Zone)
-	for line,_ in ipairs(lines) do
-		return_t:setcell(line,0, tostring(line))
-	--	iup.SetAttribute(return_t,  "FGCOLOR*:".. tostring(line), FG_Colour_Not_Complete)
-	--	iup.SetAttribute(return_t,  "BGCOLOR*:".. tostring(line), BG_Colour_Not_Complete)
-	end
-
-	--optionally set Contents
-	if (type(name) ~= "string") then	return return_t end
---	get
-
-	return return_t
-end
-
-
--- take WB_Dat or SQ_Dat, which are indexed on id and return array
--- lines_t = table[key]  of  {done,of ids{id=false, id=false}}    then we can scan and set id to true if found
-Reverse_Dat = function (dat_t,ids_t)
-	if type(dat_t) ~= "table" then
-		print("Reverse_Dat dat_t is not a table.")
-		return
-	end
-
-	if type(ids_t) ~= "table" then
-		print("Reverse_Dat ids_t is not a table.")
-		return
-	end
-
-
-
-	local lines_t = {}	-- accumulate as LC Key
-	for id, location_t in pairs (dat_t) do		--location is index into Locations2key and Locations2
-		if (Locations2Key[location_t.Location] ==nil) then
-			print("Reverse_dat")
-			print("Location " .. Location .. " not found in Locations2Key")
-			return
-		end
-
-		local key = Locations2Key[location_t.Location].key
-		local link= location_t["link1"] ~= nil
-		local gotid = (type(ids_t[id]) == "table")		-- do we have this achievement? nil is no. table is yes
-		if lines_t[key] == nil then	--	add new line
-			lines_t[key] = {}
-			lines_t[key].L = Locations2Key[location_t.Location].L
-			lines_t[key].C = Locations2Key[location_t.Location].C
-			lines_t[key].ids={}
-			lines_t[key].ids[id] = gotid
-			lines_t[key].of =1
-			lines_t[key].link=link		-- true if this key has Link1 (SQ)
-			if gotid then lines_t[key].done = 1 else lines_t[key].done = 0 end
-		else
-			lines_t[key].ids[id] = gotid	--	add id to existing line
-			lines_t[key].of = lines_t[key].of + 1
-			if gotid then lines_t[key].done = lines_t[key].done + 1  end
-		end
-	end
-	return lines_t
-end
-
-Populate_Dat = function(matrix, dat_t,ids_t)
-		local detail_box={}
-		local Rev = Reverse_Dat(dat_t,ids_t)
-		matrix.Detail_zbox = iup.zbox{}		-- to which we append detail_box[key]
-
-
-		for i,key_t  in ipairs (Locations2Key) do		--	{ key="1:1",L=1,C=1,Area=1},		--	name="Stonefalls"}
-			local name = Locations2[i].name
-			if name == nil then
-				print("Populate_Dat: Locations2[i].name returns nil for i: " .. i)
-				return
-			end
-			local line =Rev[key_t.key]
-			if line == nil then 	-- this is ok. Location without a WB or SQ
-			else
-				matrix:setcell(line.L,line.C,name .. " (" .. line.done .. "/" .. line.of .. ")")
-				if line.done == line.of then
-					iup.SetAttribute(matrix, "FGCOLOR" .. key_t.key , FG_Colour_Complete)  -- Yup
-					iup.SetAttribute(matrix, "BGCOLOR" .. key_t.key , BG_Colour_Complete)
-				end
-				if line.link then
-					detail_box[key_t.key]=iup.matrix{numcol=3, numcol_visible=3,numlin=line.of}		--Detail box for that key "1:1"
-					detail_box[key_t.key]:setcell(0,3, L.Link)
-					iup.SetAttribute(detail_box[key_t.key], "WIDTH3", "200")
-					iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT3", "ALEFT")
-				else
-					detail_box[key_t.key]=iup.matrix{numcol=2, numcol_visible=2,numlin=line.of}		--Detail box for that key "1:1"
-				end
-				detail_box[key_t.key]:setcell(0,0, L.Ach_ID)
-				detail_box[key_t.key]:setcell(0,1, L.Name)
-				detail_box[key_t.key]:setcell(0,2, L.Detail)
-				iup.SetAttribute(detail_box[key_t.key], "READONLY", "YES")
-				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT0", "ACENTER")
-				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENTLIN0", "ALEFT")
-				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT1", "ALEFT")
-				iup.SetAttribute(detail_box[key_t.key], "ALIGNMENT2", "ALEFT")
-				iup.SetAttribute(detail_box[key_t.key], "WIDTH0", "30")
-				iup.SetAttribute(detail_box[key_t.key], "WIDTH1", "150")
-				iup.SetAttribute(detail_box[key_t.key], "WIDTH2", "300")
-				-- Fill in the details
-				local j=1		--count the lines
-				for id,isdone in pairs (line.ids) do
-					if Ach_Detail[id] == nil then
-						name = "UNKN"
-						Desc = ""
-					else
-						name = Ach_Detail[id].name
-						Desc = Ach_Detail[id].description
-					end
-					detail_box[key_t.key]:setcell(j,0,id)
-					detail_box[key_t.key]:setcell(j,1, name)
-					detail_box[key_t.key]:setcell(j,2, Desc)
-					if line.link then
-						detail_box[key_t.key]:setcell(j,3, dat_t[id]["link1"])
-					end
-					if isdone then
-						iup.SetAttribute(detail_box[key_t.key],"BGCOLOR" .. tostring(j) .. ":*", BG_Colour_Complete)		-- Yes
-					end
-					j=j+1
-				end
-
-				local thisDetail =detail_box[key_t.key]
-				function thisDetail:click_cb (L,C)
-					if C == 3 then
-						iup.Help(self:getcell(L,C))		-- Launch Browser
-					else
-						return IUP_IGNORE
-					end
-				end
-				iup.Append(matrix.Detail_zbox, detail_box[key_t.key])
-			end
-		end
-		function matrix:enteritem_cb(L,C)
-			matrix.Detail_zbox.value = detail_box[tostring(L) ..":" .. tostring(C)]
-		end
-end
-
-append=function (destination_t,source_t)
---takes 2 arrays. source_t is appended to destination_t
-	if type(destination_t) ~= "table" then
-		print("error (append), destination_t is not a table")
-		return
-	end
-
-	if type(source_t) ~= "table" then
-		print("error (append), source_t is not a table")
-		return
-	end
-
-	for line, Ach in ipairs (source_t) do
-		table.insert(destination_t,Ach)
-	end
-end
-
-
---for filtering
-select_box=function (account_t)
-
--- account_t   is accounts[acc]
-
-local marks_t = {}
-local names_t = {}
-for _,playerID in  ipairs(account_t.AllplayerIDs) do
-
-	if account_t.player[playerID].visible then
-		table.insert(marks_t,1)
-	else
-		table.insert(marks_t,0)
-	end
-	table.insert(names_t,account_t.player[playerID].name)
-
-end
-
-local size = #names_t
-
-local error = iup.ListDialog(2,"Show Characters",size, names_t,0,1,size, marks_t)
-
-
-if error == -1 then
-	return false
-end
-
-
-for i,playerID in  ipairs(account_t.AllplayerIDs) do
-
-	if (marks_t[i] == 0) then
-		account_t.player[playerID].visible = false
-	else
-		account_t.player[playerID].visible = true
-	end
-
-end
-
-
-
-
-for _, ADung in pairs (account_t.alldungeons) do	-- for each dungeon
-
-	for col,playerID in  ipairs(account_t.AllplayerIDs) do
-
-		if account_t.player[playerID].visible then
-			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"100")
-		else
-			iup.SetAttribute(ADung.box, "WIDTH"..tostring(col) ,"0")
-		end
-	end
-end
-
-save_visibility()
-
-return true
-end