using Ink_Canvas.Controls;
using Ink_Canvas.Helpers;
using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using SystemEvents = Microsoft.Win32.SystemEvents;
namespace Ink_Canvas.Windows
{
///
/// PPT侧滑快捷面板
///
public partial class PPTQuickPanel : UserControl
{
// Windows Core Audio API
[ComImport]
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IAudioEndpointVolume
{
int NotImpl1();
int NotImpl2();
int GetChannelCount(out int channelCount);
int SetMasterVolumeLevel(float level, Guid eventContext);
int SetMasterVolumeLevelScalar(float level, Guid eventContext);
int GetMasterVolumeLevel(out float level);
int GetMasterVolumeLevelScalar(out float level);
int SetChannelVolumeLevel(uint channelNumber, float level, Guid eventContext);
int SetChannelVolumeLevelScalar(uint channelNumber, float level, Guid eventContext);
int GetChannelVolumeLevel(uint channelNumber, out float level);
int GetChannelVolumeLevelScalar(uint channelNumber, out float level);
int SetMute([MarshalAs(UnmanagedType.Bool)] bool mute, Guid eventContext);
int GetMute(out bool mute);
}
[ComImport]
[Guid("D666063F-1587-4E43-81F1-B948E807363F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMMDevice
{
int Activate(ref Guid iid, int clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
int OpenPropertyStore(int stgmAccess, out IPropertyStore propertyStore);
int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);
int GetState(out int state);
}
[ComImport]
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IMMDeviceEnumerator
{
int NotImpl1();
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice device);
}
[ComImport]
[Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IPropertyStore
{
int GetCount(out int propertyCount);
int GetAt(int propertyIndex, out Guid propertyKey);
int GetValue(ref Guid propertyKey, out object value);
int SetValue(ref Guid propertyKey, ref object value);
int Commit();
}
private static class MMDeviceEnumeratorFactory
{
[ComImport]
[Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
private class MMDeviceEnumerator
{
}
public static IMMDeviceEnumerator CreateInstance()
{
return new MMDeviceEnumerator() as IMMDeviceEnumerator;
}
}
private const int DEVICE_STATE_ACTIVE = 1;
private const int eRender = 0;
private const int eConsole = 0;
private IAudioEndpointVolume _audioEndpointVolume;
private bool _isExpanded = false;
private bool _isDragging = false;
private Point _dragStartPoint;
private double _collapsedOffset = 200; // 折叠时的偏移量(隐藏内容区域)
private MainWindow _mainWindow;
private Dictionary _pptImages = new Dictionary();
private Dictionary> _pptImagePaths = new Dictionary>();
public PPTQuickPanel()
{
InitializeComponent();
InitializeAudio();
ApplyTheme();
// 监听主题变化
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
Loaded += PPTQuickPanel_Loaded;
Unloaded += PPTQuickPanel_Unloaded;
IsVisibleChanged += PPTQuickPanel_IsVisibleChanged;
MagnifierWindow.Closed2 += OnMagnifierClosed;
}
private void OnMagnifierClosed(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(SyncMagnifierButtonState));
}
private void SyncMagnifierButtonState()
{
if (MagnifierToggleButton == null) return;
MagnifierToggleButton.Content = MagnifierWindow.HasInstance ? "关闭放大镜" : "开启放大镜";
}
private void PPTQuickPanel_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (Visibility == Visibility.Visible)
{
ApplyTheme();
SyncMagnifierButtonState();
}
}
private void PPTQuickPanel_Loaded(object sender, RoutedEventArgs e)
{
// 初始状态为折叠
PanelTransform.X = _collapsedOffset;
UpdateArrowRotation();
// 获取MainWindow引用
_mainWindow = Application.Current.MainWindow as MainWindow;
SubscribeToPPTEvents();
SubscribeToInkCanvasChildrenChanges();
Dispatcher.BeginInvoke(new Action(() =>
{
UpdateVolumeDisplay();
}), DispatcherPriority.Loaded);
}
private void SubscribeToInkCanvasChildrenChanges()
{
try
{
if (_mainWindow == null) return;
System.Windows.Controls.InkCanvas inkCanvas = null;
var inkCanvasField = _mainWindow.GetType().GetField("inkCanvas",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
if (inkCanvasField != null)
{
inkCanvas = inkCanvasField.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas;
}
if (inkCanvas == null)
{
var inkCanvasProperty = _mainWindow.GetType().GetProperty("inkCanvas",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
inkCanvas = inkCanvasProperty?.GetValue(_mainWindow) as System.Windows.Controls.InkCanvas;
}
if (inkCanvas != null && inkCanvas.Children is INotifyCollectionChanged notifyCollection)
{
notifyCollection.CollectionChanged += InkCanvasChildren_CollectionChanged;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"订阅inkCanvas.Children变化事件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void InkCanvasChildren_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
try
{
if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (var item in e.OldItems)
{
if (item is FrameworkElement fe && _pptImages.ContainsKey(fe))
{
RemoveImageFromPPT(fe);
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理inkCanvas.Children变化失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void RemoveImageFromPPT(FrameworkElement element)
{
try
{
if (element == null) return;
if (_pptImages.ContainsKey(element))
{
int slideNumber = _pptImages[element];
_pptImages.Remove(element);
if (_pptImagePaths.ContainsKey(slideNumber))
{
string imagePath = element.Tag as string;
if (!string.IsNullOrEmpty(imagePath) && _pptImagePaths[slideNumber].Contains(imagePath))
{
_pptImagePaths[slideNumber].Remove(imagePath);
if (_pptImagePaths[slideNumber].Count == 0)
{
_pptImagePaths.Remove(slideNumber);
DeletePPTImagePathsFile(slideNumber);
}
else
{
SavePPTImagePaths(slideNumber);
}
LogHelper.WriteLogToFile($"已从PPT页面{slideNumber}移除图片: {imagePath}");
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"从PPT关联数据中移除图片失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void DeletePPTImagePathsFile(int slideIndex)
{
try
{
if (slideIndex <= 0) return;
var folderPath = GetPresentationFolderPath();
if (string.IsNullOrEmpty(folderPath) || !Directory.Exists(folderPath))
return;
var jsonFilePath = Path.Combine(folderPath, slideIndex.ToString("0000") + ".images.json");
if (File.Exists(jsonFilePath))
{
File.Delete(jsonFilePath);
LogHelper.WriteLogToFile($"已删除第{slideIndex}页图片路径JSON文件: {jsonFilePath}");
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"删除PPT图片路径JSON文件失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private void SubscribeToPPTEvents()
{
try
{
if (_mainWindow == null) return;
// 获取PPTManager
var pptManagerProperty = _mainWindow.GetType().GetProperty("PPTManager",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
var pptManager = pptManagerProperty?.GetValue(_mainWindow);
if (pptManager != null)
{
// 订阅PPT演示文稿打开事件
var presentationOpenEvent = pptManager.GetType().GetEvent("PresentationOpen");
if (presentationOpenEvent != null)
{
var openHandler = new Action