279 lines
9.3 KiB
C#
279 lines
9.3 KiB
C#
using System.Data;
|
|
using System.Diagnostics;
|
|
using System.Numerics;
|
|
using Dalamud.Interface;
|
|
using Dalamud.Interface.Components;
|
|
using Dalamud.Interface.Utility;
|
|
using ImGuiNET;
|
|
using Newtonsoft.Json;
|
|
using Screenie.Util;
|
|
|
|
namespace Screenie.Ui.Tabs;
|
|
|
|
internal class DatabaseTab : ITab {
|
|
public string Label => "Database";
|
|
|
|
private Plugin Plugin { get; }
|
|
private List<SavedMetadata> Screenshots { get; } = [];
|
|
private LinkedList<GroupBy> GroupBys { get; } = new();
|
|
|
|
private GroupBy _selectedGroupBy = GroupBy.Character;
|
|
|
|
private enum GroupBy {
|
|
Character,
|
|
World,
|
|
Year,
|
|
Month,
|
|
Day,
|
|
Location,
|
|
}
|
|
|
|
internal DatabaseTab(Plugin plugin) {
|
|
this.Plugin = plugin;
|
|
this.GroupBys.AddLast(GroupBy.Character);
|
|
|
|
this.Update();
|
|
}
|
|
|
|
public void Dispose() {
|
|
}
|
|
|
|
public void Draw() {
|
|
if (ImGui.Button("update")) {
|
|
this.Update();
|
|
}
|
|
|
|
var first = true;
|
|
GroupBy? remove = null;
|
|
foreach (var groupBy in this.GroupBys) {
|
|
ImGui.TextUnformatted(
|
|
first
|
|
? $"Group by {Enum.GetName(groupBy)}"
|
|
: $"then by {Enum.GetName(groupBy)}"
|
|
);
|
|
|
|
ImGui.SameLine();
|
|
|
|
if (ImGuiComponents.IconButton($"remove-{groupBy}", FontAwesomeIcon.Times)) {
|
|
remove = groupBy;
|
|
}
|
|
|
|
first = false;
|
|
}
|
|
|
|
if (remove != null) {
|
|
this.GroupBys.Remove(remove.Value);
|
|
}
|
|
|
|
Vector2 buttonSize;
|
|
ImGui.PushFont(UiBuilder.IconFont);
|
|
using (new OnDispose(ImGui.PopFont)) {
|
|
buttonSize = ImGuiHelpers.GetButtonSize(FontAwesomeIcon.Plus.ToIconString());
|
|
}
|
|
|
|
var comboSize = ImGui.GetContentRegionAvail().X
|
|
- buttonSize.X
|
|
- ImGui.GetStyle().ItemSpacing.X;
|
|
|
|
ImGui.SetNextItemWidth(comboSize);
|
|
if (ImGui.BeginCombo("##then-by", Enum.GetName(this._selectedGroupBy))) {
|
|
using var endCombo = new OnDispose(ImGui.EndCombo);
|
|
foreach (var groupBy in Enum.GetValues<GroupBy>()) {
|
|
var contained = this.GroupBys.Contains(groupBy);
|
|
using (ImGuiHelper.WithDisabled(contained)) {
|
|
if (ImGui.Selectable(Enum.GetName(groupBy), this._selectedGroupBy == groupBy)) {
|
|
this._selectedGroupBy = groupBy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
|
|
var contains = this.GroupBys.Contains(this._selectedGroupBy);
|
|
using (ImGuiHelper.WithDisabled(contains)) {
|
|
if (ImGuiComponents.IconButton("add-group-by", FontAwesomeIcon.Plus) && !contains) {
|
|
this.GroupBys.AddLast(this._selectedGroupBy);
|
|
}
|
|
}
|
|
|
|
if (this.GroupBys.First == null) {
|
|
this.DrawShots(this.Screenshots);
|
|
} else {
|
|
this.DrawGroup(this.GroupBys.First, this.Screenshots);
|
|
}
|
|
}
|
|
|
|
private void DrawGroup(LinkedListNode<GroupBy> groupBy, IEnumerable<SavedMetadata> shots) {
|
|
var grouped = shots.GroupBy(shot => {
|
|
switch (groupBy.Value) {
|
|
case GroupBy.Character: {
|
|
var chara = shot.Metadata.ActiveCharacter;
|
|
if (chara == null) {
|
|
return null;
|
|
}
|
|
|
|
return $"{chara.Name} ({chara.HomeWorld})";
|
|
}
|
|
case GroupBy.World: {
|
|
return shot.Metadata.World;
|
|
}
|
|
case GroupBy.Year: {
|
|
return shot.Metadata
|
|
.CapturedAtUtc
|
|
.ToLocalTime()
|
|
.ToString("yyyy");
|
|
}
|
|
case GroupBy.Month: {
|
|
return shot.Metadata
|
|
.CapturedAtUtc
|
|
.ToLocalTime()
|
|
.ToString("MMMM");
|
|
}
|
|
case GroupBy.Day: {
|
|
return shot.Metadata
|
|
.CapturedAtUtc
|
|
.ToLocalTime()
|
|
.ToString("dd");
|
|
}
|
|
case GroupBy.Location: {
|
|
return shot.Metadata.Location;
|
|
}
|
|
default: {
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
|
|
foreach (var grouping in grouped) {
|
|
var label = grouping.Key;
|
|
|
|
ImGui.PushID($"{Enum.GetName(groupBy.Value)}-{label}");
|
|
using var popId = new OnDispose(ImGui.PopID);
|
|
|
|
if (!ImGui.TreeNodeEx(label ?? "<unknown>")) {
|
|
continue;
|
|
}
|
|
|
|
using var treePop = new OnDispose(ImGui.TreePop);
|
|
|
|
if (groupBy.Next == null) {
|
|
this.DrawShots(grouping);
|
|
} else {
|
|
this.DrawGroup(groupBy.Next, grouping);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawShots(IEnumerable<SavedMetadata> shots) {
|
|
foreach (var shot in shots) {
|
|
this.DrawShot(shot);
|
|
}
|
|
}
|
|
|
|
private void DrawShot(SavedMetadata shot) {
|
|
var fileName = Path.GetFileName(shot.Path);
|
|
if (!ImGui.TreeNodeEx($"{fileName}##shot-{shot.Path}-{shot.Blake3Hash}")) {
|
|
return;
|
|
}
|
|
|
|
using var treePop = new OnDispose(ImGui.TreePop);
|
|
|
|
var availWidth = ImGui.GetContentRegionAvail().X;
|
|
var buttonWidth = availWidth / 2 - ImGui.GetStyle().ItemSpacing.X / 2;
|
|
|
|
if (ImGui.Button("Open", new Vector2(buttonWidth, 0))) {
|
|
Process.Start(new ProcessStartInfo(shot.Path) {
|
|
UseShellExecute = true,
|
|
});
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
|
|
if (ImGui.Button("View", new Vector2(buttonWidth, 0))) {
|
|
this.Plugin.Ui.Drawables.Add(new Viewer(this.Plugin, shot.Path));
|
|
}
|
|
|
|
if (ImGui.CollapsingHeader("Players visible in shot")) {
|
|
if (shot.Metadata.VisibleCharacters.Length == 0) {
|
|
ImGui.TextUnformatted("None");
|
|
}
|
|
|
|
var viewers = this.Plugin.Ui.Drawables
|
|
.Where(drawable => drawable is Viewer)
|
|
.Cast<Viewer>()
|
|
.Where(viewer => viewer.ImagePath == shot.Path)
|
|
.ToList();
|
|
var anyHovered = false;
|
|
foreach (var chara in shot.Metadata.VisibleCharacters) {
|
|
ImGui.TextUnformatted($"{chara.Name} ({chara.HomeWorld})");
|
|
|
|
if (ImGui.IsItemHovered()) {
|
|
foreach (var viewer in viewers) {
|
|
anyHovered = true;
|
|
viewer.DotPosition = chara.ImagePosition;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!anyHovered) {
|
|
foreach (var viewer in viewers) {
|
|
viewer.DotPosition = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImGui.CollapsingHeader("Mods visible in shot")) {
|
|
if (shot.Metadata.ModsInUse.Length == 0) {
|
|
ImGui.TextUnformatted("None");
|
|
}
|
|
|
|
foreach (var mod in shot.Metadata.ModsInUse) {
|
|
ImGui.TextUnformatted(mod.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Update() {
|
|
using var reader = this.Plugin.Database.Query(
|
|
"""
|
|
select * from screenshots order by captured_at_utc desc
|
|
"""
|
|
);
|
|
|
|
this.Screenshots.Clear();
|
|
while (reader.Read()) {
|
|
var meta = new SavedMetadata {
|
|
Blake3Hash = reader.GetFieldValue<string>("hash"),
|
|
Path = reader.GetFieldValue<string>("path"),
|
|
Metadata = new ScreenshotMetadata {
|
|
ActiveCharacter = JsonConvert.DeserializeObject<Character>(
|
|
reader.GetFieldValue<string>("active_character")
|
|
),
|
|
Location = reader.GetNullableFieldValue<string>("location"),
|
|
LocationSub = reader.GetNullableFieldValue<string>("location_sub"),
|
|
Area = reader.GetNullableFieldValue<string>("area"),
|
|
AreaSub = reader.GetNullableFieldValue<string>("area_sub"),
|
|
TerritoryType = (uint) reader.GetFieldValue<int>("territory_type"),
|
|
World = reader.GetNullableFieldValue<string>("world"),
|
|
WorldId = (uint) reader.GetFieldValue<int>("world_id"),
|
|
CapturedAtLocal = reader.GetFieldValue<DateTime>("captured_at_local"),
|
|
CapturedAtUtc = reader.GetFieldValue<DateTime>("captured_at_utc"),
|
|
EorzeaTime = reader.GetFieldValue<string>("eorzea_time"),
|
|
Weather = reader.GetNullableFieldValue<string>("weather"),
|
|
Ward = (uint?) reader.GetNullableFieldValue<int>("ward"),
|
|
Plot = (uint?) reader.GetNullableFieldValue<int>("plot"),
|
|
VisibleCharacters = JsonConvert.DeserializeObject<Character[]>(
|
|
reader.GetFieldValue<string>("visible_characters")
|
|
) ?? [],
|
|
ModsInUse = JsonConvert.DeserializeObject<PenumbraMod[]>(
|
|
reader.GetFieldValue<string>("mods_in_use")
|
|
) ?? [],
|
|
},
|
|
};
|
|
|
|
this.Screenshots.Add(meta);
|
|
}
|
|
}
|
|
}
|