diff --git a/Core/Channels.lua b/Core/Channels.lua index 52c866b..94a1c20 100644 --- a/Core/Channels.lua +++ b/Core/Channels.lua @@ -253,6 +253,42 @@ while ci <= 10 do ci = ci + 1 end +-- Beitreten / Verlassen (JoinChannelByName legt den Kanal bei Bedarf an). Das Spiel erlaubt hoechstens 10 Kanaele. +Ch.MAX = 10 +Ch.pendingCat = {} + +function Ch.FreeSlots() + local n = Ch.MAX - table.getn(Ch.list) + if n < 0 then n = 0 end + return n +end + +function Ch.Join(name, pw, catId) + if type(name) ~= "string" or name == "" then return false end + if Ch.FreeSlots() <= 0 then return false, "full" end + if type(G.JoinChannelByName) ~= "function" then return false end + if pw == "" then pw = nil end + pcall(G.JoinChannelByName, name, pw) + if catId then Ch.pendingCat[string.lower(name)] = catId end + Ch.rescanAt = ((G.GetTime and G.GetTime()) or 0) + 1.5 + return true +end + +function Ch.Leave(name) + if type(name) ~= "string" or name == "" then return end + if string.find(name, "^Freewood") then return end + if type(G.LeaveChannelByName) == "function" then pcall(G.LeaveChannelByName, name) end + Ch.rescanAt = ((G.GetTime and G.GetTime()) or 0) + 1.0 +end + +local rescanPump = CreateFrame("Frame") +rescanPump:SetScript("OnUpdate", function() + if Ch.rescanAt and G.GetTime and G.GetTime() >= Ch.rescanAt then + Ch.rescanAt = nil + Ch.RefreshAll() + end +end) + -- Aktionen (geschuetzt) function Ch.Whisper(name) if type(G.ChatFrame_SendTell) == "function" then pcall(G.ChatFrame_SendTell, name) end diff --git a/Freewood-OctoWoW-Core.toc b/Freewood-OctoWoW-Core.toc index ff0def0..d6fcc74 100644 --- a/Freewood-OctoWoW-Core.toc +++ b/Freewood-OctoWoW-Core.toc @@ -3,7 +3,7 @@ ## Notes: Base addon of the Freewood family: data storage, UI kit, export/import and the main window. ## Notes-deDE: Basis-Addon der Freewood-Familie: Datenspeicher, UI-Bausteine, Export/Import und Hauptfenster. ## Author: Lacustat -## Version: 0.1.232 +## Version: 0.1.233 ## SavedVariables: FreewoodDB ## SavedVariablesPerCharacter: FreewoodCharDB diff --git a/Locales.lua b/Locales.lua index a6cd008..30998ac 100644 --- a/Locales.lua +++ b/Locales.lua @@ -81,6 +81,33 @@ FW:AddLocale("enUS", { CHAT_NOWHISPER = "No open whispers. A whisper appears in the list when someone writes to you.", CHAT_CLOSE_WHISPER = "Close whisper", CHAT_KEPT = "Chats are kept up to 7 days.", + CHAT_MENU_JOIN = "Join or create channel...", + CHAT_MENU_NEWCAT = "New category...", + CHAT_DLG_JOIN = "Join or create channel", + CHAT_DLG_NAME = "Channel name", + CHAT_DLG_PW = "Password (optional)", + CHAT_DLG_NEWCAT = "New category", + CHAT_DLG_RENAME = "Rename category", + CHAT_DLG_CATNAME = "Category name", + CHAT_OK = "OK", + CHAT_CANCEL = "Cancel", + CHAT_LEAVE = "Leave channel", + CHAT_MOVE_TO = "Move to: ", + CHAT_MOVE_RESET = "Back to default place", + CHAT_FULL = "You are already in 10 channels. Leave one first.", + CHAT_SLOTS = "Free channel slots: ", + CHAT_EMPTY_CAT = "Empty. Use + here, or right-click a channel to move it in.", + CHAT_COLLAPSE = "Collapse sidebar", + CHAT_EXPAND = "Expand sidebar", + CHAT_NEWCAT_DEFAULT = "My category", + CHAT_ADD = "+ Channel / category", + CHAT_WD1 = "Sun", + CHAT_WD2 = "Mon", + CHAT_WD3 = "Tue", + CHAT_WD4 = "Wed", + CHAT_WD5 = "Thu", + CHAT_WD6 = "Fri", + CHAT_WD7 = "Sat", CHAN_REFRESH = "Refresh", CHAN_SEARCH = "Find player in all channels...", CHAN_NONE = "You are not in any channel.", @@ -222,6 +249,33 @@ FW:AddLocale("deDE", { CHAT_NOWHISPER = "Keine offenen Flüsterchats. Sobald dich jemand anflüstert, erscheint er hier in der Liste.", CHAT_CLOSE_WHISPER = "Flüsterchat schließen", CHAT_KEPT = "Chats werden bis zu 7 Tage gespeichert.", + CHAT_MENU_JOIN = "Kanal beitreten oder erstellen...", + CHAT_MENU_NEWCAT = "Neue Kategorie...", + CHAT_DLG_JOIN = "Kanal beitreten oder erstellen", + CHAT_DLG_NAME = "Kanalname", + CHAT_DLG_PW = "Passwort (optional)", + CHAT_DLG_NEWCAT = "Neue Kategorie", + CHAT_DLG_RENAME = "Kategorie umbenennen", + CHAT_DLG_CATNAME = "Kategoriename", + CHAT_OK = "OK", + CHAT_CANCEL = "Abbrechen", + CHAT_LEAVE = "Kanal verlassen", + CHAT_MOVE_TO = "Verschieben nach: ", + CHAT_MOVE_RESET = "Zurück an den Standardplatz", + CHAT_FULL = "Du bist schon in 10 Kanälen. Verlasse zuerst einen.", + CHAT_SLOTS = "Freie Kanalplätze: ", + CHAT_EMPTY_CAT = "Leer. Mit + hier einen Kanal öffnen oder per Rechtsklick einen Kanal hierher verschieben.", + CHAT_COLLAPSE = "Seitenleiste einklappen", + CHAT_EXPAND = "Seitenleiste ausklappen", + CHAT_NEWCAT_DEFAULT = "Meine Kategorie", + CHAT_ADD = "+ Kanal / Kategorie", + CHAT_WD1 = "So", + CHAT_WD2 = "Mo", + CHAT_WD3 = "Di", + CHAT_WD4 = "Mi", + CHAT_WD5 = "Do", + CHAT_WD6 = "Fr", + CHAT_WD7 = "Sa", CHAN_REFRESH = "Aktualisieren", CHAN_SEARCH = "Spieler in allen Kanälen suchen...", CHAN_NONE = "Du bist in keinem Kanal.", diff --git a/UI/ChannelsTab.lua b/UI/ChannelsTab.lua index e9e3006..7c50d9e 100644 --- a/UI/ChannelsTab.lua +++ b/UI/ChannelsTab.lua @@ -7,7 +7,7 @@ local UI = FW.UI local Ch = FW.Channels local CL = FW.ChatLog -local LEFT_W, RIGHT_W = 220, 220 +local WIDE_W, NARROW_W, RIGHT_W = 270, 56, 220 local ROW = 24 local MAX_BUBBLES = 60 local LOG_LINES = 200 @@ -58,10 +58,60 @@ end local G_date = _G.date local function TimeText(t) if not (t and G_date) then return "" end - local ok, s = pcall(G_date, "%H:%M", t) + local ok, s = pcall(G_date, "%d.%m. %H:%M", t) return ok and s or "" end +-- "Mo 02.11. 00:08" +local function Stamp(t, L) + if not (t and G_date) then return "" end + local ok, tb = pcall(G_date, "*t", t) + local wd = (ok and type(tb) == "table" and tb.wday and L["CHAT_WD" .. tb.wday]) or "" + local ok2, s = pcall(G_date, "%d.%m. %H:%M", t) + return wd .. " " .. (ok2 and s or "") +end + +local function DayKey(t) + if not (t and G_date) then return "" end + local ok, s = pcall(G_date, "%Y%m%d", t) + return ok and s or "" +end + +local function DayText(t, L) + if not (t and G_date) then return "" end + local ok, tb = pcall(G_date, "*t", t) + local wd = (ok and type(tb) == "table" and tb.wday and L["CHAT_WD" .. tb.wday]) or "" + local ok2, s = pcall(G_date, "%d.%m.%Y", t) + return wd .. " " .. (ok2 and s or "") +end + +-- Seitenleisten-Aufteilung (gespeichert im Konto) +local BUILTIN = { "std", "other", "guild", "group", "whisper", "service" } +local CHANNEL_CATS = { std = true, other = true, service = true } +local tmpLayout = { order = {}, cats = {}, assign = {} } + +local function Layout() + local d = _G.FreewoodDB + local l + if type(d) == "table" then + if type(d.chatLayout) ~= "table" then d.chatLayout = {} end + l = d.chatLayout + else + l = tmpLayout + end + if type(l.order) ~= "table" then l.order = {} end + if type(l.cats) ~= "table" then l.cats = {} end + if type(l.assign) ~= "table" then l.assign = {} end + local have = {} + local k = 1 + while k <= table.getn(l.order) do have[l.order[k]] = true; k = k + 1 end + k = 1 + while k <= table.getn(BUILTIN) do + if not have[BUILTIN[k]] then l.order[table.getn(l.order) + 1] = BUILTIN[k] end + k = k + 1 + end + return l +end local function Build(content) local L = FW.L @@ -177,15 +227,258 @@ local function Build(content) menu:Show() end + ------------------------------------------------------------------ + -- Kleine Bausteine: Listenmenue und Eingabefenster + ------------------------------------------------------------------ + local lmenu, lcatcher, ltitle, lbtns + local function HideList() + if lmenu then lmenu:Hide() end + if lcatcher then lcatcher:Hide() end + end + local function ShowList(title, items) + if not lmenu then + lcatcher = CreateFrame("Button", nil, UIParent) + lcatcher:SetAllPoints(UIParent) + lcatcher:SetFrameStrata("FULLSCREEN_DIALOG") + lcatcher:RegisterForClicks("LeftButtonUp", "RightButtonUp") + lcatcher:SetScript("OnClick", HideList) + lcatcher:Hide() + lmenu = UI.CreatePanel(UIParent, "bgDeep", "gold") + lmenu:SetWidth(280) + lmenu:SetFrameStrata("FULLSCREEN_DIALOG") + lmenu:SetFrameLevel(lcatcher:GetFrameLevel() + 5) + lmenu:Hide() + ltitle = UI.CreateLabel(lmenu, "", { size = 12, color = "gold" }) + ltitle:SetPoint("TOPLEFT", lmenu, "TOPLEFT", 10, -8) + lbtns = {} + end + ltitle:SetText(title or "") + local n = table.getn(items) + local i = 1 + while i <= n do + local b = lbtns[i] + if not b then + b = UI.CreateButton(lmenu, "", 264, 22, nil) + b:SetPoint("TOPLEFT", lmenu, "TOPLEFT", 8, -28 - (i - 1) * 24) + lbtns[i] = b + end + local it = items[i] + b:SetLabel(it[1]) + b:SetScript("OnClick", function() + HideList() + it[2]() + end) + b:Show() + i = i + 1 + end + while lbtns[i] do lbtns[i]:Hide(); i = i + 1 end + local h = 28 + n * 24 + 8 + lmenu:SetHeight(h) + local x, y = GetCursorPosition() + local sc = UIParent:GetEffectiveScale() + lmenu:ClearAllPoints() + if y / sc - h < 0 then + lmenu:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", x / sc, y / sc) + else + lmenu:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / sc, y / sc) + end + lcatcher:Show() + lmenu:Show() + end + + local dlg, dlgTitle, dlgL1, dlgL2, dlgE1, dlgE2, dlgOk + local function ShowDialog(title, label1, label2, preset, onOk) + if not dlg then + dlg = UI.CreatePanel(UIParent, "bgDeep", "gold") + dlg:SetSize(360, 190) + dlg:SetPoint("CENTER", UIParent, "CENTER", 0, 100) + dlg:SetFrameStrata("FULLSCREEN_DIALOG") + dlg:Hide() + dlgTitle = UI.CreateLabel(dlg, "", { font = "heading", size = 14, color = "gold" }) + dlgTitle:SetPoint("TOPLEFT", dlg, "TOPLEFT", 14, -12) + local function MakeEdit(y) + local lab = UI.CreateLabel(dlg, "", { size = 11, color = "textDim" }) + lab:SetPoint("TOPLEFT", dlg, "TOPLEFT", 14, y) + local bx = UI.CreatePanel(dlg, "bg", "bronze") + bx:SetPoint("TOPLEFT", dlg, "TOPLEFT", 12, y - 16) + bx:SetPoint("TOPRIGHT", dlg, "TOPRIGHT", -12, y - 16) + bx:SetHeight(26) + local e = CreateFrame("EditBox", nil, bx) + e:SetAutoFocus(false) + e:SetPoint("TOPLEFT", bx, "TOPLEFT", 6, -3) + e:SetPoint("BOTTOMRIGHT", bx, "BOTTOMRIGHT", -6, 3) + e:SetMaxLetters(40) + if not e:SetFont(UI.GetFontPath("body"), 12, "") then e:SetFont("Fonts\\FRIZQT__.TTF", 12, "") end + e:SetTextColor(UI.Color("text")) + e:SetScript("OnEscapePressed", function() dlg:Hide() end) + return lab, bx, e + end + local bx1, bx2 + dlgL1, bx1, dlgE1 = MakeEdit(-42) + dlgL2, bx2, dlgE2 = MakeEdit(-90) + dlg.bx2 = bx2 + local function Ok() + local fn = dlg.onOk + local v1 = dlgE1:GetText() or "" + local v2 = dlgE2:GetText() or "" + dlg:Hide() + if fn then fn(v1, v2) end + end + dlgE1:SetScript("OnEnterPressed", Ok) + dlgE2:SetScript("OnEnterPressed", Ok) + dlgOk = UI.CreateButton(dlg, L.CHAT_OK, 100, 26, Ok, "primary") + dlgOk:SetPoint("BOTTOMRIGHT", dlg, "BOTTOMRIGHT", -12, 12) + local cancel = UI.CreateButton(dlg, L.CHAT_CANCEL, 100, 26, function() dlg:Hide() end) + cancel:SetPoint("RIGHT", dlgOk, "LEFT", -8, 0) + end + dlg.onOk = onOk + dlgTitle:SetText(title) + dlgL1:SetText(label1 or "") + dlgE1:SetText(preset or "") + if label2 then + dlgL2:SetText(label2); dlgL2:Show(); dlg.bx2:Show(); dlgE2:Show() + else + dlgL2:Hide(); dlg.bx2:Hide(); dlgE2:Hide() + end + dlgE2:SetText("") + dlg:Show() + dlgE1:SetFocus() + end + ------------------------------------------------------------------ -- Linke Spalte: Kategorien und Chats ------------------------------------------------------------------ + local leftW = WIDE_W + local narrow = false + local visCats = {} + local ApplyLayout + local left = UI.CreateScrollFrame(f) - left:SetPoint("TOPLEFT", f, "TOPLEFT", 0, 0) - left:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 0, 0) - left:SetWidth(LEFT_W) + left:SetPoint("TOPLEFT", f, "TOPLEFT", 0, -26) + left:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 0, 40) + left:SetWidth(leftW) local lrows = {} + local tog = UI.CreateButton(f, "<", 24, 20, function() + narrow = not narrow + Layout().narrow = narrow or nil + ApplyLayout() + Redraw() + end) + local addBtn + + local function CatName(id) + local l = Layout() + if l.cats[id] then return l.cats[id].name end + if id == "std" then return L.CHAT_CAT_STD end + if id == "other" then return L.CHAT_CAT_OTHER end + if id == "guild" then return L.CHAT_CAT_GUILD end + if id == "group" then return L.CHAT_CAT_GROUP end + if id == "whisper" then return L.CHAT_CAT_WHISPER end + if id == "service" then return L.CHAT_CAT_SERVICE end + return tostring(id) + end + + local function CatValid(id) + local l = Layout() + return id and (CHANNEL_CATS[id] or l.cats[id]) and true or false + end + + local function MoveCat(id, dir) + local l = Layout() + local at + local k = 1 + while k <= table.getn(visCats) do + if visCats[k] == id then at = k end + k = k + 1 + end + local other = at and visCats[at + dir] + if not other then return end + local ia, ib + k = 1 + while k <= table.getn(l.order) do + if l.order[k] == id then ia = k end + if l.order[k] == other then ib = k end + k = k + 1 + end + if ia and ib then l.order[ia], l.order[ib] = l.order[ib], l.order[ia] end + Redraw() + end + + local function DeleteCat(id) + local l = Layout() + l.cats[id] = nil + local new, k = {}, 1 + while k <= table.getn(l.order) do + if l.order[k] ~= id then new[table.getn(new) + 1] = l.order[k] end + k = k + 1 + end + l.order = new + Redraw() + end + + local function NewCat(name) + name = string.gsub(name or "", "^%s+", "") + name = string.gsub(name, "%s+$", "") + if name == "" then return end + local l = Layout() + l.seq = (l.seq or 0) + 1 + local id = "u" .. l.seq + l.cats[id] = { name = name } + l.order[table.getn(l.order) + 1] = id + Redraw() + end + + local function JoinDialog(catId) + local title = L.CHAT_DLG_JOIN .. " (" .. L.CHAT_SLOTS .. Ch.FreeSlots() .. ")" + ShowDialog(title, L.CHAT_DLG_NAME, L.CHAT_DLG_PW, "", function(name, pw) + name = string.gsub(name or "", "^%s+", "") + name = string.gsub(name, "%s+$", "") + if name == "" then return end + local ok, why = Ch.Join(name, pw, catId) + if not ok and why == "full" then FW:Print(L.CHAT_FULL) end + end) + end + + local function AddMenu() + ShowList(L.CHAT_SLOTS .. Ch.FreeSlots(), { + { L.CHAT_MENU_JOIN, function() JoinDialog(nil) end }, + { L.CHAT_MENU_NEWCAT, function() + ShowDialog(L.CHAT_DLG_NEWCAT, L.CHAT_DLG_CATNAME, nil, L.CHAT_NEWCAT_DEFAULT, NewCat) + end }, + }) + end + + addBtn = UI.CreateButton(f, L.CHAT_ADD, WIDE_W - 6, 30, AddMenu, "primary") + + local function ChannelMenu(en) + local l = Layout() + local items = {} + local k = 1 + while k <= table.getn(l.order) do + local id = l.order[k] + if CatValid(id) and id ~= en.cat then + local target = id + items[table.getn(items) + 1] = { L.CHAT_MOVE_TO .. CatName(id), function() + l.assign[en.key] = target + Redraw() + end } + end + k = k + 1 + end + if l.assign[en.key] then + items[table.getn(items) + 1] = { L.CHAT_MOVE_RESET, function() + l.assign[en.key] = nil + Redraw() + end } + end + if en.e and not string.find(en.e.name, "^Freewood") then + local nm = en.e.name + items[table.getn(items) + 1] = { L.CHAT_LEAVE, function() Ch.Leave(nm) end } + end + if table.getn(items) > 0 then ShowList(en.label, items) end + end + local function LeftRow(i) local r = lrows[i] if not r then @@ -193,19 +486,19 @@ local function Build(content) r:SetHeight(ROW) r:SetPoint("TOPLEFT", left.child, "TOPLEFT", 0, -(i - 1) * ROW) r:SetPoint("TOPRIGHT", left.child, "TOPRIGHT", 0, -(i - 1) * ROW) - r:RegisterForClicks("LeftButtonUp") + r:RegisterForClicks("LeftButtonUp", "RightButtonUp") r.sel = r:CreateTexture(nil, "BACKGROUND") r.sel:SetAllPoints() r.sel:SetColorTexture(UI.Color("bronze", 0.35)) r.hl = r:CreateTexture(nil, "HIGHLIGHT") r.hl:SetAllPoints() r.hl:SetColorTexture(UI.Color("gold", 0.15)) + r.line = r:CreateTexture(nil, "ARTWORK") + r.line:SetColorTexture(UI.Color("bronze", 0.6)) + r.line:SetHeight(1) r.icon = r:CreateTexture(nil, "ARTWORK") - r.icon:SetSize(18, 18) - r.icon:SetPoint("LEFT", r, "LEFT", 8, 0) + r.icon:SetSize(20, 20) r.text = UI.CreateLabel(r, "", { size = 12, justify = "LEFT" }) - r.text:SetPoint("LEFT", r.icon, "RIGHT", 6, 0) - r.text:SetPoint("RIGHT", r, "RIGHT", -26, 0) r.x = CreateFrame("Button", nil, r) r.x:SetSize(18, 18) r.x:SetPoint("RIGHT", r, "RIGHT", -4, 0) @@ -213,62 +506,100 @@ local function Build(content) r.xt:SetPoint("CENTER", r.x, "CENTER", 0, 0) r.x:SetScript("OnEnter", function() r.xt:SetTextColor(UI.Color("danger")) end) r.x:SetScript("OnLeave", function() r.xt:SetTextColor(UI.Color("textDim")) end) + r.hb = {} + local labels = { "+", "^", "v", "x" } + local j = 1 + while j <= 4 do + local b = CreateFrame("Button", nil, r) + b:SetSize(18, 18) + b.t = UI.CreateLabel(b, labels[j], { size = 13, color = "textDim" }) + b.t:SetPoint("CENTER", b, "CENTER", 0, 0) + b:SetScript("OnEnter", function() b.t:SetTextColor(UI.Color("gold")) end) + b:SetScript("OnLeave", function() b.t:SetTextColor(UI.Color("textDim")) end) + b:Hide() + r.hb[j] = b + j = j + 1 + end lrows[i] = r end return r end local function BuildEntries() - local std, other, service = {}, {}, {} + local l = Layout() + -- neu beigetretene Kanaele ihrer gewuenschten Kategorie zuordnen local k = 1 + while k <= table.getn(Ch.list) do + local lowName = string.lower(Ch.list[k].name) + local pc = Ch.pendingCat[lowName] + if pc then + l.assign["c:" .. Ch.list[k].name] = pc + Ch.pendingCat[lowName] = nil + end + k = k + 1 + end + local items = {} + local function Put(cat, en) + if not items[cat] then items[cat] = {} end + en.cat = cat + items[cat][table.getn(items[cat]) + 1] = en + end + k = 1 while k <= table.getn(Ch.list) do local e = Ch.list[k] local low = string.lower(e.name) local en = { key = "c:" .. e.name, kind = "channel", num = e.num, label = e.num .. ". " .. e.name, - count = e.stamp and e.count or nil, e = e } + count = e.stamp and e.count or nil, e = e, mode = "bubble", icon = ICONS[low] or ICONS.general } + local def if string.find(e.name, "^Freewood") then - en.mode = "data"; en.logo = true - service[table.getn(service) + 1] = en + en.mode = "data"; en.logo = true; def = "service" elseif low == "lft" then - en.mode = "log"; en.icon = ICONS.lft - service[table.getn(service) + 1] = en + en.mode = "log"; en.icon = ICONS.lft; def = "service" elseif STD[low] then - en.mode = "bubble"; en.icon = ICONS[low] or ICONS.general - std[table.getn(std) + 1] = en + def = "std" else - en.mode = "bubble"; en.icon = ICONS[low] or ICONS.general - other[table.getn(other) + 1] = en + def = "other" end + local cat = l.assign[en.key] + if not CatValid(cat) then cat = def end + Put(cat, en) k = k + 1 end - local out = {} - local function Head(t) out[table.getn(out) + 1] = { head = t } end - local function Add(en) out[table.getn(out) + 1] = en end - if table.getn(std) > 0 then - Head(L.CHAT_CAT_STD) - k = 1; while k <= table.getn(std) do Add(std[k]); k = k + 1 end - end - if table.getn(other) > 0 then - Head(L.CHAT_CAT_OTHER) - k = 1; while k <= table.getn(other) do Add(other[k]); k = k + 1 end - end - Head(L.CHAT_CAT_GUILD) - Add({ key = "guild", kind = "guild", mode = "bubble", label = L.CHAT_GUILD, icon = ICON_GUILD, + Put("guild", { key = "guild", kind = "guild", mode = "bubble", label = L.CHAT_GUILD, icon = ICON_GUILD, count = table.getn(CL.guild) }) - Head(L.CHAT_CAT_GROUP) - Add({ key = "group", kind = "group", mode = "bubble", label = L.CHAT_GROUP, icon = ICON_GROUP }) - Head(L.CHAT_CAT_WHISPER) + Put("group", { key = "group", kind = "group", mode = "bubble", label = L.CHAT_GROUP, icon = ICON_GROUP }) local ws = CL.OpenWhispers() k = 1 while k <= table.getn(ws) do - Add({ key = "w:" .. ws[k].name, kind = "whisper", mode = "bubble", label = ws[k].name, + Put("whisper", { key = "w:" .. ws[k].name, kind = "whisper", mode = "bubble", label = ws[k].name, icon = ICON_WHISPER, closable = true, name = ws[k].name }) k = k + 1 end - if table.getn(ws) == 0 then out[table.getn(out) + 1] = { note = L.CHAT_NOWHISPER } end - if table.getn(service) > 0 then - Head(L.CHAT_CAT_SERVICE) - k = 1; while k <= table.getn(service) do Add(service[k]); k = k + 1 end + + local out = {} + visCats = {} + k = 1 + while k <= table.getn(l.order) do + local id = l.order[k] + local custom = l.cats[id] ~= nil + local known = custom or id == "std" or id == "other" or id == "guild" or id == "group" + or id == "whisper" or id == "service" + local list = items[id] + local cnt = list and table.getn(list) or 0 + if known and (custom or cnt > 0 or id == "whisper") then + visCats[table.getn(visCats) + 1] = id + out[table.getn(out) + 1] = { head = CatName(id), cat = id, custom = custom } + local m = 1 + while m <= cnt do out[table.getn(out) + 1] = list[m]; m = m + 1 end + if cnt == 0 then + if id == "whisper" then + out[table.getn(out) + 1] = { note = L.CHAT_NOWHISPER } + else + out[table.getn(out) + 1] = { note = L.CHAT_EMPTY_CAT } + end + end + end + k = k + 1 end return out end @@ -291,6 +622,19 @@ local function Build(content) return nil end + local function Tip(r, text) + r:SetScript("OnEnter", function() + if GameTooltip then + pcall(GameTooltip.SetOwner, GameTooltip, r, "ANCHOR_RIGHT") + pcall(GameTooltip.SetText, GameTooltip, text) + pcall(GameTooltip.Show, GameTooltip) + end + end) + r:SetScript("OnLeave", function() + if GameTooltip then pcall(GameTooltip.Hide, GameTooltip) end + end) + end + local function DrawLeft() entries = BuildEntries() if not selKey or not EntryByKey(selKey) then @@ -308,44 +652,105 @@ local function Build(content) r:SetPoint("TOPLEFT", left.child, "TOPLEFT", 0, -y) r:SetPoint("TOPRIGHT", left.child, "TOPRIGHT", 0, -y) r.x:Hide() + r.line:Hide() + r:SetScript("OnEnter", nil) + r:SetScript("OnLeave", nil) + local j = 1 + while j <= 4 do r.hb[j]:Hide(); j = j + 1 end + r.text:ClearAllPoints() + r.icon:ClearAllPoints() if en.head then - r:SetHeight(26) r.sel:Hide(); r.hl:Hide(); r.icon:Hide() - r.text:ClearAllPoints() - r.text:SetPoint("LEFT", r, "LEFT", 8, -4) - r.text:SetPoint("RIGHT", r, "RIGHT", -4, -4) - r.text:SetText(Hex(UI.Color("gold")) .. string.upper(en.head) .. "|r") r:SetScript("OnClick", nil) - y = y + 26 + if narrow then + r:SetHeight(14) + r.text:SetText("") + r.line:ClearAllPoints() + r.line:SetPoint("LEFT", r, "LEFT", 8, -2) + r.line:SetPoint("RIGHT", r, "RIGHT", -8, -2) + r.line:Show() + Tip(r, en.head) + y = y + 14 + else + r:SetHeight(26) + r.text:SetText(Hex(UI.Color("gold")) .. string.upper(en.head) .. "|r") + local cat = en.cat + local btns = en.custom and { r.hb[4], r.hb[3], r.hb[2], r.hb[1] } or { r.hb[3], r.hb[2] } + local x = -4 + j = 1 + while j <= table.getn(btns) do + btns[j]:ClearAllPoints() + btns[j]:SetPoint("RIGHT", r, "RIGHT", x, -4) + btns[j]:Show() + x = x - 18 + j = j + 1 + end + r.text:SetPoint("LEFT", r, "LEFT", 8, -4) + r.text:SetPoint("RIGHT", r, "RIGHT", x - 2, -4) + r.hb[3]:SetScript("OnClick", function() MoveCat(cat, 1) end) + r.hb[2]:SetScript("OnClick", function() MoveCat(cat, -1) end) + if en.custom then + r.hb[1]:SetScript("OnClick", function() JoinDialog(cat) end) + r.hb[4]:SetScript("OnClick", function() DeleteCat(cat) end) + r:SetScript("OnClick", function() + ShowDialog(L.CHAT_DLG_RENAME, L.CHAT_DLG_CATNAME, nil, CatName(cat), function(v) + v = string.gsub(v or "", "^%s+", "") + v = string.gsub(v, "%s+$", "") + if v ~= "" then Layout().cats[cat].name = v; Redraw() end + end) + end) + end + y = y + 26 + end elseif en.note then - r:SetHeight(44) r.sel:Hide(); r.hl:Hide(); r.icon:Hide() - r.text:ClearAllPoints() - r.text:SetPoint("LEFT", r, "LEFT", 8, 0) - r.text:SetPoint("RIGHT", r, "RIGHT", -8, 0) - r.text:SetText("|cff9a978c" .. en.note .. "|r") r:SetScript("OnClick", nil) - y = y + 44 + if narrow then + r:SetHeight(4) + r.text:SetText("") + y = y + 4 + else + r:SetHeight(46) + r.text:SetPoint("LEFT", r, "LEFT", 10, 0) + r.text:SetPoint("RIGHT", r, "RIGHT", -8, 0) + r.text:SetText("|cff9a978c" .. en.note .. "|r") + y = y + 46 + end else r:SetHeight(ROW) r.hl:Show(); r.icon:Show() - r.text:ClearAllPoints() - r.text:SetPoint("LEFT", r.icon, "RIGHT", 6, 0) - r.text:SetPoint("RIGHT", r, "RIGHT", en.closable and -26 or -6, 0) if en.logo then r.icon:SetTexture(LOGO); r.icon:SetTexCoord(0, 1, 0, 1) else SetIcon(r.icon, en.icon) end - local txt = en.label - if en.count then txt = txt .. " |cff9a978c(" .. en.count .. ")|r" end - local un = CL.unread[en.key] - if un and un > 0 and en.key ~= selKey then txt = txt .. " |cffc89b3c+" .. un .. "|r" end - r.text:SetText(txt) + if narrow then + r.icon:SetPoint("CENTER", r, "CENTER", 0, 0) + r.text:SetText("") + local txt = en.label + Tip(r, txt) + else + r.icon:SetPoint("LEFT", r, "LEFT", 10, 0) + r.text:SetPoint("LEFT", r.icon, "RIGHT", 8, 0) + r.text:SetPoint("RIGHT", r, "RIGHT", en.closable and -26 or -6, 0) + local txt = en.label + if en.count then txt = txt .. " |cff9a978c(" .. en.count .. ")|r" end + local un = CL.unread[en.key] + if un and un > 0 and en.key ~= selKey then txt = txt .. " |cffc89b3c+" .. un .. "|r" end + r.text:SetText(txt) + end if en.key == selKey then r.sel:Show() else r.sel:Hide() end local key = en.key - r:SetScript("OnClick", function() selectKey(key) end) - if en.closable then + local ent = en + r:SetScript("OnClick", function(_, btn) + btn = btn or arg1 + if btn == "RightButton" and ent.kind == "channel" then + ChannelMenu(ent) + else + selectKey(key) + end + end) + if en.closable and not narrow then local nm = en.name r.x:Show() r.x:SetScript("OnClick", function() @@ -367,23 +772,23 @@ local function Build(content) -- Mitte: Kopf, Chat (Blasen / Log), Eingabe ------------------------------------------------------------------ local head = UI.CreateLabel(f, "", { font = "heading", size = 15, color = "gold", justify = "LEFT" }) - head:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -2) + head:SetPoint("TOPLEFT", f, "TOPLEFT", leftW + 14, -2) head:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -2) local sub = UI.CreateLabel(f, "", { size = 11, color = "textDim", justify = "LEFT" }) - sub:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -24) + sub:SetPoint("TOPLEFT", f, "TOPLEFT", leftW + 14, -24) sub:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -24) local chat = UI.CreateScrollFrame(f) - chat:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -44) + chat:SetPoint("TOPLEFT", f, "TOPLEFT", leftW + 14, -44) chat:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40) local logArea = UI.CreateTextArea(f) - logArea:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 14, -44) + logArea:SetPoint("TOPLEFT", f, "TOPLEFT", leftW + 14, -44) logArea:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40) logArea:Hide() local empty = UI.CreateLabel(f, "", { size = 12, color = "textDim", justify = "LEFT", justifyV = "TOP" }) - empty:SetPoint("TOPLEFT", f, "TOPLEFT", LEFT_W + 24, -56) + empty:SetPoint("TOPLEFT", f, "TOPLEFT", leftW + 24, -56) empty:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 24), -56) empty:SetHeight(60) @@ -393,7 +798,7 @@ local function Build(content) local ibox = UI.CreatePanel(f, "bgDeep", "bronze") ibox:SetHeight(28) - ibox:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", LEFT_W + 14, 6) + ibox:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", leftW + 14, 6) ibox:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14 + 96), 6) input = CreateFrame("EditBox", nil, ibox) input:SetAutoFocus(false) @@ -424,6 +829,7 @@ local function Build(content) -- Blasen-Pool local bubbles = {} + local seps = {} local function Bubble(i) local b = bubbles[i] if b then return b end @@ -465,9 +871,30 @@ local function Build(content) local atBottom = stick or (chat.scroll:GetVerticalScroll() >= lastH - lastViewH - 8) local y = 6 local idx = 0 + local sidx = 0 + local prevDay = nil local i = first while i <= n do local ln = lines[i] + local dk = DayKey(ln.t) + if dk ~= prevDay then + prevDay = dk + sidx = sidx + 1 + local sp = seps[sidx] + if not sp then + sp = CreateFrame("Frame", nil, chat.child) + sp:SetHeight(20) + sp.t = UI.CreateLabel(sp, "", { size = 11, color = "textDim", justify = "CENTER" }) + sp.t:SetPoint("CENTER", sp, "CENTER", 0, 0) + seps[sidx] = sp + end + sp:SetWidth(areaW - 8) + sp:ClearAllPoints() + sp:SetPoint("TOPLEFT", chat.child, "TOPLEFT", 0, -y) + sp.t:SetText("- " .. DayText(ln.t, L) .. " -") + sp:Show() + y = y + 24 + end idx = idx + 1 local b = Bubble(idx) local own = ln.o ~= nil @@ -478,12 +905,12 @@ local function Build(content) local r, g, bl = CL.ClassColor(cls) b.name:SetTextColor(r, g, bl) b.name:SetText(who) - b.time:SetText(TimeText(ln.t)) + b.time:SetText(Stamp(ln.t, L)) local txt = ln.m or "" measure:SetText(Plain(txt)) local tw = measure:GetStringWidth() or 0 if tw <= 0 then tw = maxBub end - local nameW = (b.name:GetStringWidth() or 60) + 50 + local nameW = (b.name:GetStringWidth() or 60) + (b.time:GetStringWidth() or 80) + 40 local pw = math.floor(math.max(math.min(math.max(tw, nameW) + 22, maxBub), 110)) b.text:SetWidth(pw - 20) b.text:SetText(txt) @@ -519,6 +946,8 @@ local function Build(content) end idx = idx + 1 while bubbles[idx] do bubbles[idx]:Hide(); idx = idx + 1 end + sidx = sidx + 1 + while seps[sidx] do seps[sidx]:Hide(); sidx = sidx + 1 end local viewH = chat.scroll:GetHeight() or 300 chat:SetContentHeight(math.max(1, y)) lastH, lastViewH = y, viewH @@ -538,7 +967,7 @@ local function Build(content) local i = first while i <= n do local ln = lines[i] - parts[table.getn(parts) + 1] = "[" .. TimeText(ln.t) .. "] " .. (ln.s or "?") .. ": " .. Plain(ln.m or "") + parts[table.getn(parts) + 1] = "[" .. Stamp(ln.t, L) .. "] " .. (ln.s or "?") .. ": " .. Plain(ln.m or "") i = i + 1 end if not logArea.edit:HasFocus() then @@ -722,6 +1151,41 @@ local function Build(content) DrawRight() end) + ApplyLayout = function() + leftW = narrow and NARROW_W or WIDE_W + left:SetWidth(leftW) + tog:ClearAllPoints() + tog:SetPoint("TOPLEFT", f, "TOPLEFT", leftW - 28, 0) + tog:SetLabel(narrow and ">" or "<") + Tip(tog, narrow and L.CHAT_EXPAND or L.CHAT_COLLAPSE) + addBtn:ClearAllPoints() + addBtn:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 0, 4) + addBtn:SetWidth(leftW - 6) + addBtn:SetLabel(narrow and "+" or L.CHAT_ADD) + local lx = leftW + 14 + head:ClearAllPoints() + head:SetPoint("TOPLEFT", f, "TOPLEFT", lx, -2) + head:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -2) + sub:ClearAllPoints() + sub:SetPoint("TOPLEFT", f, "TOPLEFT", lx, -24) + sub:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 14), -24) + chat:ClearAllPoints() + chat:SetPoint("TOPLEFT", f, "TOPLEFT", lx, -44) + chat:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40) + logArea:ClearAllPoints() + logArea:SetPoint("TOPLEFT", f, "TOPLEFT", lx, -44) + logArea:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14), 40) + empty:ClearAllPoints() + empty:SetPoint("TOPLEFT", f, "TOPLEFT", lx + 10, -56) + empty:SetPoint("TOPRIGHT", f, "TOPRIGHT", -(RIGHT_W + 24), -56) + ibox:ClearAllPoints() + ibox:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", lx, 6) + ibox:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -(RIGHT_W + 14 + 96), 6) + stick = true + end + narrow = Layout().narrow and true or false + ApplyLayout() + ------------------------------------------------------------------ -- Zusammenspiel ------------------------------------------------------------------ @@ -758,6 +1222,7 @@ local function Build(content) CL.viewVisible = false CL.ClearMemberQueue() HideMenu() + HideList() copy:Hide() end) @@ -767,6 +1232,7 @@ local function Build(content) if acc < 0.4 then return end acc = 0 if not f:IsShown() then return end + if Ch.dirty then Ch.dirty = nil; Ch.RefreshAll() end local w = chat.child:GetWidth() if seen.cl ~= CL.version or seen.ch ~= Ch.version or (w and math.abs(w - seen.w) > 4) then Redraw()