forked from aly/qstbak
muffin v7.4
This commit is contained in:
parent
8a7847ff37
commit
a4175abacd
54 changed files with 95984 additions and 123967 deletions
|
|
@ -110,7 +110,7 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
ImGui.SameLine();
|
||||
int currentItem = (int)base.Configuration.Duties.DefaultDutyMode;
|
||||
ImGui.SetNextItemWidth(200f);
|
||||
if (ImGui.Combo("##DefaultDutyMode", ref currentItem, in DutyModeLabels, DutyModeLabels.Length))
|
||||
if (ImGui.Combo((ImU8String)"##DefaultDutyMode", ref currentItem, (ReadOnlySpan<string>)DutyModeLabels, DutyModeLabels.Length))
|
||||
{
|
||||
base.Configuration.Duties.DefaultDutyMode = (EDutyMode)currentItem;
|
||||
Save();
|
||||
|
|
@ -201,11 +201,11 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
DutyOptions dutyOptions;
|
||||
bool flag = _questRegistry.TryGetDutyByContentFinderConditionId(num, out dutyOptions);
|
||||
ImGui.TableNextRow();
|
||||
string[] items;
|
||||
string[] array;
|
||||
int currentItem;
|
||||
if (flag)
|
||||
{
|
||||
items = (dutyOptions.Enabled ? SupportedCfcOptions : UnsupportedCfcOptions);
|
||||
array = (dutyOptions.Enabled ? SupportedCfcOptions : UnsupportedCfcOptions);
|
||||
currentItem = 0;
|
||||
if (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(num))
|
||||
{
|
||||
|
|
@ -218,7 +218,7 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
}
|
||||
else
|
||||
{
|
||||
items = new string[2] { "Disabled", "Enabled" };
|
||||
array = new string[2] { "Disabled", "Enabled" };
|
||||
currentItem = (base.Configuration.Duties.WhitelistedDutyCfcIds.Contains(num) ? 1 : 0);
|
||||
}
|
||||
if (ImGui.TableNextColumn())
|
||||
|
|
@ -232,14 +232,14 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
{
|
||||
ImGui.TextUnformatted(text);
|
||||
ImGui.Separator();
|
||||
table = new ImU8String(13, 1);
|
||||
table.AppendLiteral("TerritoryId: ");
|
||||
table.AppendFormatted(value2);
|
||||
ImGui.BulletText(table);
|
||||
table = new ImU8String(26, 1);
|
||||
table.AppendLiteral("ContentFinderConditionId: ");
|
||||
table.AppendFormatted(num);
|
||||
ImGui.BulletText(table);
|
||||
ImU8String text2 = new ImU8String(13, 1);
|
||||
text2.AppendLiteral("TerritoryId: ");
|
||||
text2.AppendFormatted(value2);
|
||||
ImGui.BulletText(text2);
|
||||
ImU8String text3 = new ImU8String(26, 1);
|
||||
text3.AppendLiteral("ContentFinderConditionId: ");
|
||||
text3.AppendFormatted(num);
|
||||
ImGui.BulletText(text3);
|
||||
if (flag)
|
||||
{
|
||||
ImGui.BulletText("Duty Support: Available");
|
||||
|
|
@ -257,13 +257,13 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
}
|
||||
if (ImGui.TableNextColumn())
|
||||
{
|
||||
table = new ImU8String(16, 1);
|
||||
table.AppendLiteral("##DungeonEnabled");
|
||||
table.AppendFormatted(num);
|
||||
using (ImRaii.PushId(table))
|
||||
ImU8String id = new ImU8String(16, 1);
|
||||
id.AppendLiteral("##DungeonEnabled");
|
||||
id.AppendFormatted(num);
|
||||
using (ImRaii.PushId(id))
|
||||
{
|
||||
ImGui.SetNextItemWidth(200f);
|
||||
if (ImGui.Combo(string.Empty, ref currentItem, in items, items.Length))
|
||||
if (ImGui.Combo((ImU8String)string.Empty, ref currentItem, (ReadOnlySpan<string>)array, array.Length))
|
||||
{
|
||||
base.Configuration.Duties.WhitelistedDutyCfcIds.Remove(num);
|
||||
base.Configuration.Duties.BlacklistedDutyCfcIds.Remove(num);
|
||||
|
|
@ -291,10 +291,10 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
{
|
||||
continue;
|
||||
}
|
||||
table = new ImU8String(13, 1);
|
||||
table.AppendLiteral("##DungeonMode");
|
||||
table.AppendFormatted(num);
|
||||
using (ImRaii.PushId(table))
|
||||
ImU8String id2 = new ImU8String(13, 1);
|
||||
id2.AppendLiteral("##DungeonMode");
|
||||
id2.AppendFormatted(num);
|
||||
using (ImRaii.PushId(id2))
|
||||
{
|
||||
EDutyMode value3;
|
||||
bool flag2 = base.Configuration.Duties.DutyModeOverrides.TryGetValue(num, out value3);
|
||||
|
|
@ -302,16 +302,16 @@ internal sealed class DutyConfigComponent : ConfigComponent
|
|||
Name = "Use Default";
|
||||
string[] dutyModeLabels = DutyModeLabels;
|
||||
int num3 = 0;
|
||||
string[] array = new string[1 + dutyModeLabels.Length];
|
||||
array[num3] = Name;
|
||||
string[] array2 = new string[1 + dutyModeLabels.Length];
|
||||
array2[num3] = Name;
|
||||
num3++;
|
||||
ReadOnlySpan<string> readOnlySpan = new ReadOnlySpan<string>(dutyModeLabels);
|
||||
readOnlySpan.CopyTo(new Span<string>(array).Slice(num3, readOnlySpan.Length));
|
||||
readOnlySpan.CopyTo(new Span<string>(array2).Slice(num3, readOnlySpan.Length));
|
||||
num3 += readOnlySpan.Length;
|
||||
string[] items2 = array;
|
||||
string[] array3 = array2;
|
||||
int currentItem2 = (int)(num2 + 1);
|
||||
ImGui.SetNextItemWidth(150f);
|
||||
if (ImGui.Combo(string.Empty, ref currentItem2, in items2, items2.Length))
|
||||
if (ImGui.Combo((ImU8String)string.Empty, ref currentItem2, (ReadOnlySpan<string>)array3, array3.Length))
|
||||
{
|
||||
if (currentItem2 == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
return;
|
||||
}
|
||||
int currentItem = (int)base.Configuration.General.CombatModule;
|
||||
if (ImGui.Combo("Preferred Combat Module", ref currentItem, in _combatModuleNames, _combatModuleNames.Length))
|
||||
if (ImGui.Combo((ImU8String)"Preferred Combat Module", ref currentItem, (ReadOnlySpan<string>)_combatModuleNames, _combatModuleNames.Length))
|
||||
{
|
||||
base.Configuration.General.CombatModule = (Configuration.ECombatModule)currentItem;
|
||||
Save();
|
||||
|
|
@ -150,7 +150,7 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
_mountComboJustOpened = false;
|
||||
}
|
||||
int currentItem2 = (int)base.Configuration.General.GrandCompany;
|
||||
if (ImGui.Combo("Preferred Grand Company", ref currentItem2, in _grandCompanyNames, _grandCompanyNames.Length))
|
||||
if (ImGui.Combo((ImU8String)"Preferred Grand Company", ref currentItem2, (ReadOnlySpan<string>)_grandCompanyNames, _grandCompanyNames.Length))
|
||||
{
|
||||
base.Configuration.General.GrandCompany = (FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany)currentItem2;
|
||||
Save();
|
||||
|
|
@ -162,7 +162,7 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
Save();
|
||||
currentItem3 = 0;
|
||||
}
|
||||
if (ImGui.Combo("Preferred Combat Job", ref currentItem3, in _classJobNames, _classJobNames.Length))
|
||||
if (ImGui.Combo((ImU8String)"Preferred Combat Job", ref currentItem3, (ReadOnlySpan<string>)_classJobNames, _classJobNames.Length))
|
||||
{
|
||||
base.Configuration.General.CombatJob = _classJobIds[currentItem3];
|
||||
Save();
|
||||
|
|
@ -279,11 +279,11 @@ internal sealed class GeneralConfigComponent : ConfigComponent
|
|||
Save();
|
||||
}
|
||||
Vector4 col = new Vector4(0.7f, 0.7f, 0.7f, 1f);
|
||||
ImU8String text2 = new ImU8String(77, 1);
|
||||
text2.AppendLiteral("Quest steps will refresh automatically after ");
|
||||
text2.AppendFormatted(v10);
|
||||
text2.AppendLiteral(" seconds if no progress is made.");
|
||||
ImGui.TextColored(in col, text2);
|
||||
ImU8String text3 = new ImU8String(77, 1);
|
||||
text3.AppendLiteral("Quest steps will refresh automatically after ");
|
||||
text3.AppendFormatted(v10);
|
||||
text3.AppendLiteral(" seconds if no progress is made.");
|
||||
ImGui.TextColored(in col, text3);
|
||||
ImGui.Unindent();
|
||||
}
|
||||
ImGui.Spacing();
|
||||
|
|
|
|||
|
|
@ -2,22 +2,17 @@ using System;
|
|||
using System.Linq;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Utility;
|
||||
using Questionable.External;
|
||||
|
||||
namespace Questionable.Windows.ConfigComponents;
|
||||
|
||||
internal sealed class NotificationConfigComponent : ConfigComponent
|
||||
{
|
||||
private readonly NotificationMasterIpc _notificationMasterIpc;
|
||||
|
||||
public NotificationConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration, NotificationMasterIpc notificationMasterIpc)
|
||||
public NotificationConfigComponent(IDalamudPluginInterface pluginInterface, Configuration configuration)
|
||||
: base(pluginInterface, configuration)
|
||||
{
|
||||
_notificationMasterIpc = notificationMasterIpc;
|
||||
}
|
||||
|
||||
public override void DrawTab()
|
||||
|
|
@ -41,31 +36,12 @@ internal sealed class NotificationConfigComponent : ConfigComponent
|
|||
where x != XivChatType.StandardEmote
|
||||
select x).ToArray();
|
||||
int currentItem = Array.IndexOf(array, base.Configuration.Notifications.ChatType);
|
||||
string[] items = array.Select((XivChatType t) => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString()).ToArray();
|
||||
if (ImGui.Combo("Chat channel", ref currentItem, in items, items.Length))
|
||||
string[] array2 = array.Select((XivChatType t) => t.GetAttribute<XivChatTypeInfoAttribute>()?.FancyName ?? t.ToString()).ToArray();
|
||||
if (ImGui.Combo((ImU8String)"Chat channel", ref currentItem, (ReadOnlySpan<string>)array2, array2.Length))
|
||||
{
|
||||
base.Configuration.Notifications.ChatType = array[currentItem];
|
||||
Save();
|
||||
}
|
||||
ImGui.Separator();
|
||||
ImGui.Text("NotificationMaster settings");
|
||||
ImGui.SameLine();
|
||||
ImGuiComponents.HelpMarker("Requires the plugin 'NotificationMaster' to be installed.");
|
||||
using (ImRaii.Disabled(!_notificationMasterIpc.Enabled))
|
||||
{
|
||||
bool v2 = base.Configuration.Notifications.ShowTrayMessage;
|
||||
if (ImGui.Checkbox("Show tray notification", ref v2))
|
||||
{
|
||||
base.Configuration.Notifications.ShowTrayMessage = v2;
|
||||
Save();
|
||||
}
|
||||
bool v3 = base.Configuration.Notifications.FlashTaskbar;
|
||||
if (ImGui.Checkbox("Flash taskbar icon", ref v3))
|
||||
{
|
||||
base.Configuration.Notifications.FlashTaskbar = v3;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,13 +66,12 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
_pluginInterface = pluginInterface;
|
||||
_uiUtils = uiUtils;
|
||||
_commandManager = commandManager;
|
||||
PluginInfo[] obj = new PluginInfo[6]
|
||||
PluginInfo[] obj = new PluginInfo[5]
|
||||
{
|
||||
new PluginInfo("Artisan", "Artisan", "Handles automatic crafting for quests that require\ncrafted items.", new Uri("https://github.com/PunishXIV/Artisan"), new Uri("https://puni.sh/api/plugins")),
|
||||
new PluginInfo("AutoDuty", "AutoDuty", "Automatically handles dungeon and trial completion during\nMain Scenario Quest progression.", new Uri("https://github.com/erdelf/AutoDuty"), new Uri("https://puni.sh/api/repository/erdelf")),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
};
|
||||
Uri websiteUri = new Uri("https://github.com/Jaksuhn/Automaton");
|
||||
|
|
@ -84,17 +83,16 @@ internal sealed class PluginConfigComponent : ConfigComponent
|
|||
int index = 0;
|
||||
span[index] = new PluginDetailInfo("'Sniper no sniping' enabled", "Automatically completes sniping tasks introduced in Stormblood", () => automatonIpc.IsAutoSnipeEnabled);
|
||||
obj[2] = new PluginInfo("CBT (formerly known as Automaton)", "Automaton", "Automaton is a collection of automation-related tweaks.", websiteUri, dalamudRepositoryUri, "/cbt", list);
|
||||
obj[3] = new PluginInfo("NotificationMaster", "NotificationMaster", "Sends a configurable out-of-game notification if a quest\nrequires manual actions.", new Uri("https://github.com/NightmareXIV/NotificationMaster"), null);
|
||||
Uri websiteUri2 = new Uri("https://github.com/PunishXIV/PandorasBox");
|
||||
Uri dalamudRepositoryUri2 = new Uri("https://puni.sh/api/plugins");
|
||||
index = 1;
|
||||
List<PluginDetailInfo> list2 = new List<PluginDetailInfo>(index);
|
||||
CollectionsMarshal.SetCount(list2, index);
|
||||
span = CollectionsMarshal.AsSpan(list2);
|
||||
Span<PluginDetailInfo> span2 = CollectionsMarshal.AsSpan(list2);
|
||||
num = 0;
|
||||
span[num] = new PluginDetailInfo("'Auto Active Time Maneuver' enabled", "Automatically completes active time maneuvers in\nsingle player instances, trials and raids\"", () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled);
|
||||
obj[4] = new PluginInfo("Pandora's Box", "PandorasBox", "Pandora's Box is a collection of tweaks.", websiteUri2, dalamudRepositoryUri2, "/pandora", list2);
|
||||
obj[5] = new PluginInfo("QuestMap", "QuestMap", "Displays quest objectives and markers on the map for\nbetter navigation and tracking.", new Uri("https://github.com/rreminy/QuestMap"), null);
|
||||
span2[num] = new PluginDetailInfo("'Auto Active Time Maneuver' enabled", "Automatically completes active time maneuvers in\nsingle player instances, trials and raids\"", () => pandorasBoxIpc.IsAutoActiveTimeManeuverEnabled);
|
||||
obj[3] = new PluginInfo("Pandora's Box", "PandorasBox", "Pandora's Box is a collection of tweaks.", websiteUri2, dalamudRepositoryUri2, "/pandora", list2);
|
||||
obj[4] = new PluginInfo("QuestMap", "QuestMap", "Displays quest objectives and markers on the map for\nbetter navigation and tracking.", new Uri("https://github.com/rreminy/QuestMap"), null);
|
||||
_recommendedPlugins = new global::_003C_003Ez__ReadOnlyArray<PluginInfo>(obj);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -641,7 +641,7 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
|
|||
foreach (SinglePlayerDutyInfo dutyInfo in dutyInfos)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
string[] items = (dutyInfo.EnabledByDefault ? SupportedCfcOptions : UnsupportedCfcOptions);
|
||||
string[] array = (dutyInfo.EnabledByDefault ? SupportedCfcOptions : UnsupportedCfcOptions);
|
||||
int currentItem = 0;
|
||||
if (base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Contains(dutyInfo.ContentFinderConditionId))
|
||||
{
|
||||
|
|
@ -651,7 +651,6 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
|
|||
{
|
||||
currentItem = 2;
|
||||
}
|
||||
ImU8String text;
|
||||
if (ImGui.TableNextColumn())
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
|
|
@ -663,14 +662,14 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
|
|||
{
|
||||
ImGui.TextUnformatted(dutyInfo.Name);
|
||||
ImGui.Separator();
|
||||
text = new ImU8String(13, 1);
|
||||
ImU8String text = new ImU8String(13, 1);
|
||||
text.AppendLiteral("TerritoryId: ");
|
||||
text.AppendFormatted(dutyInfo.TerritoryId);
|
||||
ImGui.BulletText(text);
|
||||
text = new ImU8String(26, 1);
|
||||
text.AppendLiteral("ContentFinderConditionId: ");
|
||||
text.AppendFormatted(dutyInfo.ContentFinderConditionId);
|
||||
ImGui.BulletText(text);
|
||||
ImU8String text2 = new ImU8String(26, 1);
|
||||
text2.AppendLiteral("ContentFinderConditionId: ");
|
||||
text2.AppendFormatted(dutyInfo.ContentFinderConditionId);
|
||||
ImGui.BulletText(text2);
|
||||
}
|
||||
}
|
||||
if (!dutyInfo.Enabled)
|
||||
|
|
@ -686,15 +685,15 @@ internal sealed class SinglePlayerDutyConfigComponent : ConfigComponent
|
|||
{
|
||||
continue;
|
||||
}
|
||||
text = new ImU8String(6, 1);
|
||||
text.AppendLiteral("##Duty");
|
||||
text.AppendFormatted(dutyInfo.ContentFinderConditionId);
|
||||
using (ImRaii.PushId(text))
|
||||
ImU8String id = new ImU8String(6, 1);
|
||||
id.AppendLiteral("##Duty");
|
||||
id.AppendFormatted(dutyInfo.ContentFinderConditionId);
|
||||
using (ImRaii.PushId(id))
|
||||
{
|
||||
using (ImRaii.Disabled(!dutyInfo.Enabled))
|
||||
{
|
||||
ImGui.SetNextItemWidth(200f);
|
||||
if (ImGui.Combo(string.Empty, ref currentItem, in items, items.Length))
|
||||
if (ImGui.Combo((ImU8String)string.Empty, ref currentItem, (ReadOnlySpan<string>)array, array.Length))
|
||||
{
|
||||
base.Configuration.SinglePlayerDuties.WhitelistedSinglePlayerDutyCfcIds.Remove(dutyInfo.ContentFinderConditionId);
|
||||
base.Configuration.SinglePlayerDuties.BlacklistedSinglePlayerDutyCfcIds.Remove(dutyInfo.ContentFinderConditionId);
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@ internal sealed class StopConditionComponent : ConfigComponent
|
|||
{
|
||||
int sequence = currentQuest.Sequence;
|
||||
ImGui.SameLine();
|
||||
ImU8String text = new ImU8String(11, 1);
|
||||
text.AppendLiteral("(Current: ");
|
||||
text.AppendFormatted(sequence);
|
||||
text.AppendLiteral(")");
|
||||
ImGui.TextDisabled(text);
|
||||
ImU8String text2 = new ImU8String(11, 1);
|
||||
text2.AppendLiteral("(Current: ");
|
||||
text2.AppendFormatted(sequence);
|
||||
text2.AppendLiteral(")");
|
||||
ImGui.TextDisabled(text2);
|
||||
}
|
||||
ImGui.TextWrapped("Note: Individual quest sequences below override this global setting.");
|
||||
}
|
||||
|
|
@ -165,10 +165,10 @@ internal sealed class StopConditionComponent : ConfigComponent
|
|||
{
|
||||
continue;
|
||||
}
|
||||
ImU8String text = new ImU8String(5, 1);
|
||||
text.AppendLiteral("Quest");
|
||||
text.AppendFormatted(elementId);
|
||||
using (ImRaii.PushId(text))
|
||||
ImU8String id = new ImU8String(5, 1);
|
||||
id.AppendLiteral("Quest");
|
||||
id.AppendFormatted(elementId);
|
||||
using (ImRaii.PushId(id))
|
||||
{
|
||||
(Vector4, FontAwesomeIcon, string) questStyle = _uiUtils.GetQuestStyle(elementId);
|
||||
bool flag;
|
||||
|
|
@ -187,21 +187,21 @@ internal sealed class StopConditionComponent : ConfigComponent
|
|||
_questTooltipComponent.Draw(quest2.Info);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
string text2 = elementId.ToString();
|
||||
base.Configuration.Stop.QuestSequences.TryGetValue(text2, out var value);
|
||||
string text3 = elementId.ToString();
|
||||
base.Configuration.Stop.QuestSequences.TryGetValue(text3, out var value);
|
||||
bool v4 = value.HasValue;
|
||||
text = new ImU8String(8, 1);
|
||||
text.AppendLiteral("##UseSeq");
|
||||
text.AppendFormatted(text2);
|
||||
if (ImGui.Checkbox(text, ref v4))
|
||||
ImU8String label = new ImU8String(8, 1);
|
||||
label.AppendLiteral("##UseSeq");
|
||||
label.AppendFormatted(text3);
|
||||
if (ImGui.Checkbox(label, ref v4))
|
||||
{
|
||||
if (v4)
|
||||
{
|
||||
base.Configuration.Stop.QuestSequences[text2] = 1;
|
||||
base.Configuration.Stop.QuestSequences[text3] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Configuration.Stop.QuestSequences.Remove(text2);
|
||||
base.Configuration.Stop.QuestSequences.Remove(text3);
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
|
@ -216,12 +216,12 @@ internal sealed class StopConditionComponent : ConfigComponent
|
|||
ImGui.SameLine();
|
||||
ImGui.SetNextItemWidth(85f);
|
||||
int data3 = value ?? 1;
|
||||
text = new ImU8String(10, 1);
|
||||
text.AppendLiteral("##SeqValue");
|
||||
text.AppendFormatted(text2);
|
||||
if (ImGui.InputInt(text, ref data3, 1, 1) && v4)
|
||||
ImU8String label2 = new ImU8String(10, 1);
|
||||
label2.AppendLiteral("##SeqValue");
|
||||
label2.AppendFormatted(text3);
|
||||
if (ImGui.InputInt(label2, ref data3, 1, 1) && v4)
|
||||
{
|
||||
base.Configuration.Stop.QuestSequences[text2] = Math.Max(0, Math.Min(255, data3));
|
||||
base.Configuration.Stop.QuestSequences[text3] = Math.Max(0, Math.Min(255, data3));
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue