Files
HellionChat/ChatTwo/Configuration.cs
T
2024-07-09 15:18:16 +10:00

523 lines
16 KiB
C#
Executable File

using System.Collections;
using ChatTwo.Code;
using ChatTwo.Resources;
using ChatTwo.Ui;
using Dalamud.Configuration;
using ImGuiNET;
namespace ChatTwo;
[Serializable]
internal class Configuration : IPluginConfiguration
{
private const int LatestVersion = 5;
public int Version { get; set; } = LatestVersion;
public bool HideChat = true;
public bool HideDuringCutscenes = true;
public bool HideWhenNotLoggedIn = true;
public bool HideWhenUiHidden = true;
public bool HideInLoadingScreens;
public bool HideInBattle;
public bool ShowHideButton = true;
public bool NativeItemTooltips = true;
public bool PrettierTimestamps = true;
public bool MoreCompactPretty;
public bool HideSameTimestamps;
public bool ShowNoviceNetwork;
public bool SidebarTabView;
public bool PrintChangelog = true;
public bool OnlyPreviewIf;
public int PreviewMinimum = 1;
public PreviewPosition PreviewPosition = PreviewPosition.Inside;
public CommandHelpSide CommandHelpSide = CommandHelpSide.None;
public KeybindMode KeybindMode = KeybindMode.Strict;
public LanguageOverride LanguageOverride = LanguageOverride.None;
public bool CanMove = true;
public bool CanResize = true;
public bool ShowTitleBar;
public bool ShowPopOutTitleBar = true;
public bool DatabaseBattleMessages;
public bool LoadPreviousSession;
public bool FilterIncludePreviousSessions;
public bool SortAutoTranslate;
public bool CollapseDuplicateMessages;
public bool PlaySounds = true;
public bool KeepInputFocus = true;
public int MaxLinesToRender = 10_000;
public bool ShowEmotes = true;
public HashSet<string> BlockedEmotes = [];
public bool FontsEnabled = true;
public ExtraGlyphRanges ExtraGlyphRanges = 0;
public float FontSize = 17f;
public float JapaneseFontSize = 17f;
public float SymbolsFontSize = 17f;
public string GlobalFont = Fonts.GlobalFonts[0].Name;
public string JapaneseFont = Fonts.JapaneseFonts[0].Item1;
public float TooltipOffset;
public float WindowAlpha = 100f;
public Dictionary<ChatType, uint> ChatColours = new();
public List<Tab> Tabs = new();
public bool OverrideStyle;
public string? ChosenStyle;
internal void UpdateFrom(Configuration other, bool backToOriginal)
{
if (backToOriginal)
foreach (var tab in Tabs.Where(t => t.PopOut))
tab.PopOut = false;
HideChat = other.HideChat;
HideDuringCutscenes = other.HideDuringCutscenes;
HideWhenNotLoggedIn = other.HideWhenNotLoggedIn;
HideWhenUiHidden = other.HideWhenUiHidden;
HideInLoadingScreens = other.HideInLoadingScreens;
HideInBattle = other.HideInBattle;
ShowHideButton = other.ShowHideButton;
NativeItemTooltips = other.NativeItemTooltips;
PrettierTimestamps = other.PrettierTimestamps;
MoreCompactPretty = other.MoreCompactPretty;
HideSameTimestamps = other.HideSameTimestamps;
ShowNoviceNetwork = other.ShowNoviceNetwork;
SidebarTabView = other.SidebarTabView;
PrintChangelog = other.PrintChangelog;
OnlyPreviewIf = other.OnlyPreviewIf;
PreviewMinimum = other.PreviewMinimum;
PreviewPosition = other.PreviewPosition;
CommandHelpSide = other.CommandHelpSide;
KeybindMode = other.KeybindMode;
LanguageOverride = other.LanguageOverride;
CanMove = other.CanMove;
CanResize = other.CanResize;
ShowTitleBar = other.ShowTitleBar;
ShowPopOutTitleBar = other.ShowPopOutTitleBar;
DatabaseBattleMessages = other.DatabaseBattleMessages;
LoadPreviousSession = other.LoadPreviousSession;
FilterIncludePreviousSessions = other.FilterIncludePreviousSessions;
SortAutoTranslate = other.SortAutoTranslate;
CollapseDuplicateMessages = other.CollapseDuplicateMessages;
PlaySounds = other.PlaySounds;
KeepInputFocus = other.KeepInputFocus;
MaxLinesToRender = other.MaxLinesToRender;
ShowEmotes = other.ShowEmotes;
BlockedEmotes = other.BlockedEmotes;
FontsEnabled = other.FontsEnabled;
ExtraGlyphRanges = other.ExtraGlyphRanges;
FontSize = other.FontSize;
JapaneseFontSize = other.JapaneseFontSize;
SymbolsFontSize = other.SymbolsFontSize;
GlobalFont = other.GlobalFont;
JapaneseFont = other.JapaneseFont;
TooltipOffset = other.TooltipOffset;
WindowAlpha = other.WindowAlpha;
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
Tabs = other.Tabs.Select(t => t.Clone()).ToList();
OverrideStyle = other.OverrideStyle;
ChosenStyle = other.ChosenStyle;
}
}
[Serializable]
internal enum UnreadMode
{
All,
Unseen,
None,
}
internal static class UnreadModeExt
{
internal static string Name(this UnreadMode mode) => mode switch
{
UnreadMode.All => Language.UnreadMode_All,
UnreadMode.Unseen => Language.UnreadMode_Unseen,
UnreadMode.None => Language.UnreadMode_None,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
internal static string? Tooltip(this UnreadMode mode) => mode switch
{
UnreadMode.All => Language.UnreadMode_All_Tooltip,
UnreadMode.Unseen => Language.UnreadMode_Unseen_Tooltip,
UnreadMode.None => Language.UnreadMode_None_Tooltip,
_ => null,
};
}
[Serializable]
internal class Tab
{
public string Name = Language.Tab_DefaultName;
public Dictionary<ChatType, ChatSource> ChatCodes = new();
public bool ExtraChatAll;
public HashSet<Guid> ExtraChatChannels = [];
public UnreadMode UnreadMode = UnreadMode.Unseen;
public bool DisplayTimestamp = true;
public InputChannel? Channel;
public bool PopOut;
public bool IndependentOpacity;
public float Opacity = 100f;
public bool InputDisabled;
[NonSerialized]
public uint Unread;
[NonSerialized]
public MessageList Messages = new();
[NonSerialized]
public InputChannel? PreviousChannel;
[NonSerialized]
public Guid Identifier = Guid.NewGuid();
internal bool Matches(Message message)
{
if (message.ExtraChatChannel != Guid.Empty)
return ExtraChatAll || ExtraChatChannels.Contains(message.ExtraChatChannel);
return message.Code.Type.IsGm()
|| ChatCodes.TryGetValue(message.Code.Type, out var sources)
&& (message.Code.Source is 0 or (ChatSource) 1
|| sources.HasFlag(message.Code.Source));
}
internal void AddMessage(Message message, bool unread = true)
{
Messages.AddPrune(message, MessageManager.MessageDisplayLimit);
if (unread)
Unread += 1;
}
internal void Clear()
{
Messages.Clear();
}
internal Tab Clone()
{
return new Tab
{
Name = Name,
ChatCodes = ChatCodes.ToDictionary(entry => entry.Key, entry => entry.Value),
ExtraChatAll = ExtraChatAll,
ExtraChatChannels = ExtraChatChannels.ToHashSet(),
UnreadMode = UnreadMode,
DisplayTimestamp = DisplayTimestamp,
Channel = Channel,
PopOut = PopOut,
IndependentOpacity = IndependentOpacity,
Opacity = Opacity,
Identifier = Identifier,
InputDisabled = InputDisabled,
};
}
/// <summary>
/// MessageList provides an ordered list of messages with duplicate ID
/// tracking, sorting and mutex protection.
/// </summary>
internal class MessageList
{
private ReaderWriterLock rwl = new();
private readonly List<Message> messages;
private readonly HashSet<Guid> trackedMessageIds;
public MessageList()
{
messages = new();
trackedMessageIds = new();
}
public MessageList(int initialCapacity)
{
messages = new(initialCapacity);
trackedMessageIds = new(initialCapacity);
}
public void AddPrune(Message message, int max)
{
rwl.AcquireWriterLock(-1);
try
{
AddLocked(message);
PruneMaxLocked(max);
}
finally
{
rwl.ReleaseWriterLock();
}
}
public void AddSortPrune(IEnumerable<Message> messages, int max)
{
rwl.AcquireWriterLock(-1);
try
{
foreach (var message in messages)
AddLocked(message);
SortLocked();
PruneMaxLocked(max);
}
finally
{
rwl.ReleaseWriterLock();
}
}
private void AddLocked(Message message)
{
if (trackedMessageIds.Contains(message.Id))
return;
messages.Add(message);
trackedMessageIds.Add(message.Id);
}
public void Clear()
{
rwl.AcquireWriterLock(-1);
try
{
messages.Clear();
trackedMessageIds.Clear();
}
finally
{
rwl.ReleaseWriterLock();
}
}
private void SortLocked()
{
messages.Sort((a, b) => a.Date.CompareTo(b.Date));
}
private void PruneMaxLocked(int max)
{
while (messages.Count > max)
{
trackedMessageIds.Remove(messages[0].Id);
messages.RemoveAt(0);
}
}
/// <summary>
/// GetReadOnly returns a read-only list of messages while holding a
/// reader lock. The list should be used with a using statement.
/// </summary>
public RLockedMessageList GetReadOnly(int millisecondsTimeout = -1)
{
rwl.AcquireReaderLock(millisecondsTimeout);
return new RLockedMessageList(rwl, messages);
}
internal class RLockedMessageList(ReaderWriterLock rwl, List<Message> messages) : IReadOnlyList<Message>, IDisposable
{
public IEnumerator<Message> GetEnumerator()
{
return messages.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => messages.Count;
public Message this[int index] => messages[index];
public void Dispose()
{
rwl.ReleaseReaderLock();
}
}
}
}
[Serializable]
internal enum PreviewPosition
{
None,
Inside,
Top,
Bottom,
Tooltip,
}
internal static class PreviewPositionExt
{
internal static string Name(this PreviewPosition position) => position switch
{
PreviewPosition.None => Language.Options_Preview_None,
PreviewPosition.Inside => Language.Options_Preview_Inside,
PreviewPosition.Top => Language.Options_Preview_Top,
PreviewPosition.Bottom => Language.Options_Preview_Bottom,
PreviewPosition.Tooltip => Language.Options_Preview_Tooltip,
_ => throw new ArgumentOutOfRangeException(nameof(position), position, null),
};
}
[Serializable]
internal enum CommandHelpSide
{
None,
Left,
Right,
}
internal static class CommandHelpSideExt
{
internal static string Name(this CommandHelpSide side) => side switch
{
CommandHelpSide.None => Language.CommandHelpSide_None,
CommandHelpSide.Left => Language.CommandHelpSide_Left,
CommandHelpSide.Right => Language.CommandHelpSide_Right,
_ => throw new ArgumentOutOfRangeException(nameof(side), side, null),
};
}
[Serializable]
internal enum KeybindMode
{
Flexible,
Strict,
}
internal static class KeybindModeExt
{
internal static string Name(this KeybindMode mode) => mode switch
{
KeybindMode.Flexible => Language.KeybindMode_Flexible_Name,
KeybindMode.Strict => Language.KeybindMode_Strict_Name,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
internal static string? Tooltip(this KeybindMode mode) => mode switch
{
KeybindMode.Flexible => Language.KeybindMode_Flexible_Tooltip,
KeybindMode.Strict => Language.KeybindMode_Strict_Tooltip,
_ => null,
};
}
[Serializable]
internal enum LanguageOverride
{
None,
ChineseSimplified,
ChineseTraditional,
Dutch,
English,
French,
German,
Greek,
// Italian,
Japanese,
// Korean,
// Norwegian,
PortugueseBrazil,
Romanian,
Russian,
Spanish,
Swedish,
}
internal static class LanguageOverrideExt
{
internal static string Name(this LanguageOverride mode) => mode switch
{
LanguageOverride.None => Language.LanguageOverride_None,
LanguageOverride.ChineseSimplified => "简体中文",
LanguageOverride.ChineseTraditional => "繁體中文",
LanguageOverride.Dutch => "Nederlands",
LanguageOverride.English => "English",
LanguageOverride.French => "Français",
LanguageOverride.German => "Deutsch",
LanguageOverride.Greek => "Ελληνικά",
// LanguageOverride.Italian => "Italiano",
LanguageOverride.Japanese => "日本語",
// LanguageOverride.Korean => "한국어 (Korean)",
// LanguageOverride.Norwegian => "Norsk",
LanguageOverride.PortugueseBrazil => "Português do Brasil",
LanguageOverride.Romanian => "Română",
LanguageOverride.Russian => "Русский",
LanguageOverride.Spanish => "Español",
LanguageOverride.Swedish => "Svenska",
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
internal static string Code(this LanguageOverride mode) => mode switch
{
LanguageOverride.None => "",
LanguageOverride.ChineseSimplified => "zh-hans",
LanguageOverride.ChineseTraditional => "zh-hant",
LanguageOverride.Dutch => "nl",
LanguageOverride.English => "en",
LanguageOverride.French => "fr",
LanguageOverride.German => "de",
LanguageOverride.Greek => "el",
// LanguageOverride.Italian => "it",
LanguageOverride.Japanese => "ja",
// LanguageOverride.Korean => "ko",
// LanguageOverride.Norwegian => "no",
LanguageOverride.PortugueseBrazil => "pt-br",
LanguageOverride.Romanian => "ro",
LanguageOverride.Russian => "ru",
LanguageOverride.Spanish => "es",
LanguageOverride.Swedish => "sv",
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null),
};
}
[Serializable]
[Flags]
internal enum ExtraGlyphRanges
{
ChineseFull = 1 << 0,
ChineseSimplifiedCommon = 1 << 1,
Cyrillic = 1 << 2,
Japanese = 1 << 3,
Korean = 1 << 4,
Thai = 1 << 5,
Vietnamese = 1 << 6,
}
internal static class ExtraGlyphRangesExt
{
internal static string Name(this ExtraGlyphRanges ranges) => ranges switch
{
ExtraGlyphRanges.ChineseFull => Language.ExtraGlyphRanges_ChineseFull_Name,
ExtraGlyphRanges.ChineseSimplifiedCommon => Language.ExtraGlyphRanges_ChineseSimplifiedCommon_Name,
ExtraGlyphRanges.Cyrillic => Language.ExtraGlyphRanges_Cyrillic_Name,
ExtraGlyphRanges.Japanese => Language.ExtraGlyphRanges_Japanese_Name,
ExtraGlyphRanges.Korean => Language.ExtraGlyphRanges_Korean_Name,
ExtraGlyphRanges.Thai => Language.ExtraGlyphRanges_Thai_Name,
ExtraGlyphRanges.Vietnamese => Language.ExtraGlyphRanges_Vietnamese_Name,
_ => throw new ArgumentOutOfRangeException(nameof(ranges), ranges, null),
};
internal static nint Range(this ExtraGlyphRanges ranges) => ranges switch
{
ExtraGlyphRanges.ChineseFull => ImGui.GetIO().Fonts.GetGlyphRangesChineseFull(),
ExtraGlyphRanges.ChineseSimplifiedCommon => ImGui.GetIO().Fonts.GetGlyphRangesChineseSimplifiedCommon(),
ExtraGlyphRanges.Cyrillic => ImGui.GetIO().Fonts.GetGlyphRangesCyrillic(),
ExtraGlyphRanges.Japanese => ImGui.GetIO().Fonts.GetGlyphRangesJapanese(),
ExtraGlyphRanges.Korean => ImGui.GetIO().Fonts.GetGlyphRangesKorean(),
ExtraGlyphRanges.Thai => ImGui.GetIO().Fonts.GetGlyphRangesThai(),
ExtraGlyphRanges.Vietnamese => ImGui.GetIO().Fonts.GetGlyphRangesVietnamese(),
_ => throw new ArgumentOutOfRangeException(nameof(ranges), ranges, null),
};
}