mirror of
https://github.com/kwsch/PKHeX
synced 2025-02-25 20:07:09 +00:00
This format won't be supported cleanly, so let's just try to allow language detection to work as best as it can without storing metadata in the SK2 bytes.
375 lines
12 KiB
C#
375 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Media;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
using PKHeX.Core;
|
|
using PKHeX.Drawing;
|
|
|
|
namespace PKHeX.WinForms.Controls
|
|
{
|
|
/// <summary>
|
|
/// Orchestrates the movement of slots within the GUI.
|
|
/// </summary>
|
|
public sealed class SlotChangeManager : IDisposable
|
|
{
|
|
public readonly SAVEditor SE;
|
|
public readonly SlotTrackerImage LastSlot = new SlotTrackerImage();
|
|
public readonly DragManager Drag = new DragManager();
|
|
public SaveDataEditor<PictureBox> Env { get; set; }
|
|
|
|
public readonly List<BoxEditor> Boxes = new List<BoxEditor>();
|
|
public readonly SlotHoverHandler Hover = new SlotHoverHandler();
|
|
|
|
public SlotChangeManager(SAVEditor se) => SE = se;
|
|
|
|
public void Reset()
|
|
{
|
|
Drag.Initialize();
|
|
LastSlot.Reset();
|
|
}
|
|
|
|
public void MouseEnter(object sender, EventArgs e)
|
|
{
|
|
var pb = (PictureBox)sender;
|
|
if (pb.Image == null)
|
|
return;
|
|
Hover.Start(pb, LastSlot);
|
|
}
|
|
|
|
public void MouseLeave(object sender, EventArgs e)
|
|
{
|
|
Hover.Stop();
|
|
}
|
|
|
|
public void MouseClick(object sender, MouseEventArgs e)
|
|
{
|
|
if (!Drag.Info.DragDropInProgress)
|
|
SE.ClickSlot(sender, e);
|
|
}
|
|
|
|
public void MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left)
|
|
Drag.Info.LeftMouseIsDown = false;
|
|
Drag.Info.Source = null;
|
|
}
|
|
|
|
public void MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if (e.Button == MouseButtons.Left)
|
|
{
|
|
Drag.Info.LeftMouseIsDown = true;
|
|
Drag.MouseDownPosition = Cursor.Position;
|
|
}
|
|
}
|
|
|
|
public void QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
|
|
{
|
|
if (e.Action != DragAction.Cancel && e.Action != DragAction.Drop)
|
|
return;
|
|
Drag.Info.LeftMouseIsDown = false;
|
|
Drag.Info.DragDropInProgress = false;
|
|
}
|
|
|
|
public void DragEnter(object sender, DragEventArgs e)
|
|
{
|
|
if ((e.AllowedEffect & DragDropEffects.Copy) != 0) // external file
|
|
e.Effect = DragDropEffects.Copy;
|
|
else if (e.Data != null) // within
|
|
e.Effect = DragDropEffects.Move;
|
|
|
|
if (Drag.Info.DragDropInProgress)
|
|
Drag.SetCursor(((Control)sender).FindForm(), Drag.Info.Cursor);
|
|
}
|
|
|
|
private static SlotViewInfo<T> GetSlotInfo<T>(T pb) where T : Control
|
|
{
|
|
var view = WinFormsUtil.FindFirstControlOfType<ISlotViewer<T>>(pb);
|
|
var src = view.GetSlotData(pb);
|
|
return new SlotViewInfo<T>(src, view);
|
|
}
|
|
|
|
public void MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
if (!Drag.CanStartDrag)
|
|
return;
|
|
|
|
// Abort if there is no Pokemon in the given slot.
|
|
PictureBox pb = (PictureBox)sender;
|
|
if (pb.Image == null)
|
|
return;
|
|
var src = GetSlotInfo(pb);
|
|
if (!src.CanWriteTo())
|
|
return;
|
|
bool encrypt = Control.ModifierKeys == Keys.Control;
|
|
HandleMovePKM(pb, encrypt);
|
|
}
|
|
|
|
public void DragDrop(object sender, DragEventArgs e)
|
|
{
|
|
PictureBox pb = (PictureBox)sender;
|
|
var info = GetSlotInfo(pb);
|
|
if (!info.CanWriteTo())
|
|
{
|
|
SystemSounds.Asterisk.Play();
|
|
e.Effect = DragDropEffects.Copy;
|
|
Drag.Reset();
|
|
return;
|
|
}
|
|
|
|
var mod = SlotUtil.GetDropModifier();
|
|
Drag.Info.Destination = info;
|
|
HandleDropPKM(pb, e, mod);
|
|
}
|
|
|
|
private void HandleMovePKM(PictureBox pb, bool encrypt)
|
|
{
|
|
// Create a temporary PKM file to perform a drag drop operation.
|
|
|
|
// Set flag to prevent re-entering.
|
|
Drag.Info.DragDropInProgress = true;
|
|
|
|
// Prepare Data
|
|
Drag.Info.Source = GetSlotInfo(pb);
|
|
Drag.Info.Destination = null;
|
|
|
|
// Make a new file name based off the PID
|
|
string newfile = CreateDragDropPKM(pb, encrypt, out bool external);
|
|
|
|
// drop finished, clean up
|
|
Drag.Info.Source = null;
|
|
Drag.Reset();
|
|
Drag.ResetCursor(pb.FindForm());
|
|
|
|
// Browser apps need time to load data since the file isn't moved to a location on the user's local storage.
|
|
// Tested 10ms -> too quick, 100ms was fine. 500ms should be safe?
|
|
// Keep it to 10 seconds; Discord upload only stores the file path until you click Upload.
|
|
int delay = external ? 10_000 : 0;
|
|
DeleteAsync(newfile, delay);
|
|
if (Drag.Info.DragIsParty)
|
|
SE.SetParty();
|
|
}
|
|
|
|
private async void DeleteAsync(string path, int delay)
|
|
{
|
|
await Task.Delay(delay).ConfigureAwait(true);
|
|
if (File.Exists(path) && Drag.Info.CurrentPath == null)
|
|
File.Delete(path);
|
|
}
|
|
|
|
private string CreateDragDropPKM(PictureBox pb, bool encrypt, out bool external)
|
|
{
|
|
// Make File
|
|
PKM pk = Drag.Info.Source.ReadCurrent();
|
|
string newfile = FileUtil.GetPKMTempFileName(pk, encrypt);
|
|
try
|
|
{
|
|
var data = encrypt ? pk.EncryptedPartyData : pk.DecryptedPartyData;
|
|
external = TryMakeDragDropPKM(pb, data, newfile);
|
|
}
|
|
#pragma warning disable CA1031 // Do not catch general exception types
|
|
// Tons of things can happen with drag & drop; don't try to handle things, just indicate failure.
|
|
catch (Exception x)
|
|
#pragma warning restore CA1031 // Do not catch general exception types
|
|
{
|
|
WinFormsUtil.Error("Drag && Drop Error", x);
|
|
external = false;
|
|
}
|
|
|
|
return newfile;
|
|
}
|
|
|
|
private bool TryMakeDragDropPKM(PictureBox pb, byte[] data, string newfile)
|
|
{
|
|
File.WriteAllBytes(newfile, data);
|
|
var img = (Bitmap)pb.Image;
|
|
Drag.SetCursor(pb.FindForm(), new Cursor(img.GetHicon()));
|
|
Hover.Stop();
|
|
pb.Image = null;
|
|
pb.BackgroundImage = SpriteUtil.Spriter.Drag;
|
|
|
|
// Thread Blocks on DoDragDrop
|
|
Drag.Info.CurrentPath = newfile;
|
|
var result = pb.DoDragDrop(new DataObject(DataFormats.FileDrop, new[] { newfile }), DragDropEffects.Move);
|
|
var external = Drag.Info.Destination == null || result != DragDropEffects.Link;
|
|
if (external || Drag.Info.SameLocation) // not dropped to another box slot, restore img
|
|
{
|
|
pb.Image = img;
|
|
pb.BackgroundImage = LastSlot.OriginalBackground;
|
|
Drag.ResetCursor(pb.FindForm());
|
|
return external;
|
|
}
|
|
|
|
if (result == DragDropEffects.Copy) // viewed in tabs or cloned
|
|
{
|
|
if (Drag.Info.Destination == null) // apply 'view' highlight
|
|
Env.Slots.Get(Drag.Info.Source.Slot);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void HandleDropPKM(PictureBox pb, DragEventArgs e, DropModifier mod)
|
|
{
|
|
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
|
|
if (files == null)
|
|
{
|
|
Drag.Reset();
|
|
return;
|
|
}
|
|
|
|
if (Directory.Exists(files[0])) // folder
|
|
{
|
|
SE.LoadBoxes(out string _, files[0]);
|
|
Drag.Reset();
|
|
return;
|
|
}
|
|
|
|
e.Effect = mod == DropModifier.Clone ? DragDropEffects.Copy : DragDropEffects.Link;
|
|
|
|
// file
|
|
if (Drag.Info.SameLocation)
|
|
{
|
|
e.Effect = DragDropEffects.Link;
|
|
return;
|
|
}
|
|
|
|
var dest = Drag.Info.Destination;
|
|
|
|
if (Drag.Info.Source == null) // external source
|
|
{
|
|
bool badDest = !dest.CanWriteTo();
|
|
if (!TryLoadFiles(files, e, badDest))
|
|
WinFormsUtil.Alert(MessageStrings.MsgSaveSlotBadData);
|
|
}
|
|
else if (!TrySetPKMDestination(pb, mod))
|
|
{
|
|
WinFormsUtil.Alert(MessageStrings.MsgSaveSlotEmpty);
|
|
}
|
|
Drag.Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to load the input <see cref="files"/>
|
|
/// </summary>
|
|
/// <param name="files">Files to load</param>
|
|
/// <param name="e">Args</param>
|
|
/// <param name="badDest">Destination slot disallows eggs/blanks</param>
|
|
/// <returns>True if loaded</returns>
|
|
private bool TryLoadFiles(IReadOnlyList<string> files, DragEventArgs e, bool badDest)
|
|
{
|
|
if (files.Count == 0)
|
|
return false;
|
|
|
|
var sav = Drag.Info.Destination.View.SAV;
|
|
var path = files[0];
|
|
var temp = FileUtil.GetSingleFromPath(path, sav);
|
|
if (temp == null)
|
|
{
|
|
Drag.RequestDD(this, e); // pass thru
|
|
return true; // treat as handled
|
|
}
|
|
|
|
PKM pk = PKMConverter.ConvertToType(temp, sav.PKMType, out string c);
|
|
if (pk == null)
|
|
{
|
|
WinFormsUtil.Error(c);
|
|
Debug.WriteLine(c);
|
|
return false;
|
|
}
|
|
|
|
if (badDest && (pk.Species == 0 || pk.IsEgg))
|
|
return false;
|
|
|
|
if (sav is ILangDeviantSave il && PKMConverter.IsIncompatibleGB(temp, il.Japanese, pk.Japanese))
|
|
{
|
|
c = PKMConverter.GetIncompatibleGBMessage(pk, il.Japanese);
|
|
WinFormsUtil.Error(c);
|
|
Debug.WriteLine(c);
|
|
return false;
|
|
}
|
|
|
|
var errata = sav.IsPKMCompatible(pk);
|
|
if (errata.Count > 0)
|
|
{
|
|
string concat = string.Join(Environment.NewLine, errata);
|
|
if (DialogResult.Yes != WinFormsUtil.Prompt(MessageBoxButtons.YesNo, concat, MessageStrings.MsgContinue))
|
|
{
|
|
Debug.WriteLine(c);
|
|
Debug.WriteLine(concat);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Env.Slots.Set(Drag.Info.Destination.Slot, pk);
|
|
Debug.WriteLine(c);
|
|
return true;
|
|
}
|
|
|
|
private bool TrySetPKMDestination(PictureBox pb, DropModifier mod)
|
|
{
|
|
PKM pk = Drag.Info.Source.ReadCurrent();
|
|
var msg = Drag.Info.Destination.CanWriteTo(pk);
|
|
if (msg != WriteBlockedMessage.None)
|
|
return false;
|
|
|
|
if (Drag.Info.Source != null)
|
|
TrySetPKMSource(pb, mod);
|
|
|
|
// Copy from temp to destination slot.
|
|
Env.Slots.Set(Drag.Info.Destination.Slot, pk);
|
|
Drag.ResetCursor(pb.FindForm());
|
|
return true;
|
|
}
|
|
|
|
private bool TrySetPKMSource(PictureBox sender, DropModifier mod)
|
|
{
|
|
if (Drag.Info.Destination == null || mod == DropModifier.Clone)
|
|
return false;
|
|
|
|
if (sender.Image == null || mod == DropModifier.Overwrite)
|
|
{
|
|
Env.Slots.Delete(Drag.Info.Source.Slot);
|
|
return true;
|
|
}
|
|
|
|
var pk = Drag.Info.Destination.ReadCurrent();
|
|
Env.Slots.Set(Drag.Info.Source.Slot, pk);
|
|
return true;
|
|
}
|
|
|
|
// Utility
|
|
public void SwapBoxes(int index, int other, SaveFile SAV)
|
|
{
|
|
if (index == other)
|
|
return;
|
|
SAV.SwapBox(index, other);
|
|
UpdateBoxViewAtBoxIndexes(index, other);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Hover.Dispose();
|
|
SE?.Dispose();
|
|
LastSlot.OriginalBackground?.Dispose();
|
|
LastSlot.CurrentBackground?.Dispose();
|
|
}
|
|
|
|
private void UpdateBoxViewAtBoxIndexes(params int[] boxIndexes)
|
|
{
|
|
foreach (var box in Boxes)
|
|
{
|
|
var current = box.CurrentBox;
|
|
if (!boxIndexes.Contains(current))
|
|
continue;
|
|
box.ResetSlots();
|
|
box.ResetBoxNames(current);
|
|
}
|
|
}
|
|
}
|
|
}
|