2022-10-23 11:02:31 +13:00
|
|
|
|
// <copyright file="RowData.cs" company="PlaceholderCompany">
|
|
|
|
|
// Copyright (c) PlaceholderCompany. All rights reserved.
|
|
|
|
|
// </copyright>
|
|
|
|
|
|
|
|
|
|
namespace SystemTrayMenu.DataClasses
|
|
|
|
|
{
|
|
|
|
|
using System;
|
2023-05-19 09:28:52 +12:00
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
using System.IO;
|
2023-05-19 09:28:52 +12:00
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using SystemTrayMenu.Properties;
|
|
|
|
|
using SystemTrayMenu.UserInterface;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
using SystemTrayMenu.Utilities;
|
2023-05-19 09:28:52 +12:00
|
|
|
|
using Icon = System.Drawing.Icon;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-05-19 09:28:52 +12:00
|
|
|
|
internal class RowData : INotifyPropertyChanged
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
private Brush? backgroundBrush;
|
|
|
|
|
private Brush? borderBrush;
|
|
|
|
|
private ImageSource? columnIcon;
|
|
|
|
|
private string? columnText;
|
|
|
|
|
private bool isSelected;
|
|
|
|
|
|
2022-10-23 11:02:31 +13:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="RowData"/> class.
|
|
|
|
|
/// (Related replace "\x00" see #171.)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="isFolder">Flag if file or folder.</param>
|
2023-04-17 06:17:24 +12:00
|
|
|
|
/// <param name="isAdditionalItem">Flag if additional item, from other folder than root folder.</param>
|
2022-10-23 11:02:31 +13:00
|
|
|
|
/// <param name="level">The number of the menu level.</param>
|
|
|
|
|
/// <param name="path">Path to item.</param>
|
2023-04-29 04:48:39 +12:00
|
|
|
|
internal RowData(bool isFolder, bool isAdditionalItem, int level, string path)
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
|
|
|
|
IsFolder = isFolder;
|
2023-04-17 06:17:24 +12:00
|
|
|
|
IsAdditionalItem = isAdditionalItem;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
Level = level;
|
2023-04-30 04:57:39 +12:00
|
|
|
|
FileInfo = new FileInfo(path.Replace("\x00", string.Empty));
|
|
|
|
|
Path = isFolder ? $@"{FileInfo.FullName}\" : FileInfo.FullName;
|
|
|
|
|
FileExtension = System.IO.Path.GetExtension(Path) ?? string.Empty;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
if (FileExtension.Equals(".lnk", StringComparison.InvariantCultureIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
ResolvedPath = FileLnk.GetResolvedFileName(Path, out bool isLinkToFolder);
|
2023-05-19 09:28:52 +12:00
|
|
|
|
ShowOverlay = Settings.Default.ShowLinkOverlay;
|
2023-04-30 04:57:39 +12:00
|
|
|
|
Text = System.IO.Path.GetFileNameWithoutExtension(Path);
|
2023-05-19 09:28:52 +12:00
|
|
|
|
if (Settings.Default.ResolveLinksToFolders)
|
2023-04-30 04:57:39 +12:00
|
|
|
|
{
|
|
|
|
|
IsPointingToFolder |= isLinkToFolder || FileLnk.IsNetworkRoot(ResolvedPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-04-30 04:57:39 +12:00
|
|
|
|
ResolvedPath = Path;
|
|
|
|
|
if (string.IsNullOrEmpty(FileInfo.Name))
|
|
|
|
|
{
|
|
|
|
|
int nameBegin = FileInfo.FullName.LastIndexOf(@"\", StringComparison.InvariantCulture) + 1;
|
|
|
|
|
Text = FileInfo.FullName[nameBegin..];
|
|
|
|
|
}
|
|
|
|
|
else if (FileExtension.Equals(".url", StringComparison.InvariantCultureIgnoreCase) ||
|
|
|
|
|
FileExtension.Equals(".appref-ms", StringComparison.InvariantCultureIgnoreCase))
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
ShowOverlay = Settings.Default.ShowLinkOverlay;
|
2023-04-30 04:57:39 +12:00
|
|
|
|
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
2023-04-30 04:57:39 +12:00
|
|
|
|
else if (!isFolder && Config.IsHideFileExtension())
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-04-30 04:57:39 +12:00
|
|
|
|
Text = System.IO.Path.GetFileNameWithoutExtension(FileInfo.Name);
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
2023-04-30 04:57:39 +12:00
|
|
|
|
else
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-04-30 04:57:39 +12:00
|
|
|
|
Text = FileInfo.Name;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-30 04:57:39 +12:00
|
|
|
|
|
|
|
|
|
IsPointingToFolder |= isFolder;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 09:28:52 +12:00
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
|
|
|
|
|
|
public Brush? BackgroundBrush
|
|
|
|
|
{
|
|
|
|
|
get => backgroundBrush;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (value != backgroundBrush)
|
|
|
|
|
{
|
|
|
|
|
backgroundBrush = value;
|
|
|
|
|
CallPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Brush? BorderBrush
|
|
|
|
|
{
|
|
|
|
|
get => borderBrush;
|
|
|
|
|
private set
|
|
|
|
|
{
|
|
|
|
|
if (value != borderBrush)
|
|
|
|
|
{
|
|
|
|
|
borderBrush = value;
|
|
|
|
|
CallPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ImageSource? ColumnIcon
|
|
|
|
|
{
|
|
|
|
|
get => columnIcon;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value != columnIcon)
|
|
|
|
|
{
|
|
|
|
|
columnIcon = value;
|
|
|
|
|
CallPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[AllowNull]
|
|
|
|
|
public string ColumnText
|
|
|
|
|
{
|
|
|
|
|
get => columnText ?? "?";
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value != columnText)
|
|
|
|
|
{
|
|
|
|
|
columnText = value;
|
|
|
|
|
CallPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal int SortIndex { get; set; }
|
|
|
|
|
|
|
|
|
|
internal bool IsPendingOpenItem { get; set; }
|
|
|
|
|
|
|
|
|
|
internal bool IsSelected
|
|
|
|
|
{
|
|
|
|
|
get => isSelected;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (value != isSelected)
|
|
|
|
|
{
|
|
|
|
|
isSelected = value;
|
|
|
|
|
CallPropertyChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-05 13:27:57 +13:00
|
|
|
|
internal Icon? Icon { get; private set; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
internal FileInfo FileInfo { get; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the original/local path.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal string Path { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the resulting target path after following shortcuts or CLSIDs.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal string ResolvedPath { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a value indicating whether
|
|
|
|
|
/// the item is actually a folder and not a file.
|
|
|
|
|
/// E.g. a shortcut (.lnk) would return false.
|
|
|
|
|
/// </summary>
|
2022-10-23 11:02:31 +13:00
|
|
|
|
internal bool IsFolder { get; }
|
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets a value indicating whether
|
|
|
|
|
/// the item is actually a folder or at least points to one.
|
|
|
|
|
/// E.g. a shortcut (.lnk) could return either false or true.
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal bool IsPointingToFolder { get; }
|
|
|
|
|
|
2023-04-17 06:17:24 +12:00
|
|
|
|
internal bool IsAdditionalItem { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-05-19 08:28:05 +12:00
|
|
|
|
internal int Level { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
internal string FileExtension { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
|
|
|
|
internal bool ShowOverlay { get; }
|
|
|
|
|
|
2022-12-05 13:27:57 +13:00
|
|
|
|
internal string? Text { get; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-05-04 09:18:03 +12:00
|
|
|
|
internal Menu? Owner { get; set; }
|
|
|
|
|
|
2022-12-05 13:27:57 +13:00
|
|
|
|
internal Menu? SubMenu { get; set; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
|
|
|
|
internal bool HiddenEntry { get; set; }
|
|
|
|
|
|
|
|
|
|
internal int RowIndex { get; set; }
|
|
|
|
|
|
2023-05-19 08:28:05 +12:00
|
|
|
|
internal bool IconLoading { get; private set; }
|
2022-10-23 11:02:31 +13:00
|
|
|
|
|
2023-05-19 09:28:52 +12:00
|
|
|
|
public override string ToString() => nameof(RowData) + ": " + ColumnText + ", Owner: " + (Owner?.ToString() ?? "null");
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Triggers an PropertyChanged event of INotifyPropertyChanged.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="propertyName">Name of the changing property.</param>
|
|
|
|
|
public void CallPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
|
|
|
|
2022-12-05 13:27:57 +13:00
|
|
|
|
internal void ReadIcon(bool updateIconInBackground)
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-04-30 04:57:39 +12:00
|
|
|
|
bool loading;
|
|
|
|
|
if (IsPointingToFolder)
|
2022-10-23 11:02:31 +13:00
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
Icon = IconReader.GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, Level == 0, out loading);
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
Icon = IconReader.GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, updateIconInBackground, Level == 0, out loading);
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-30 04:57:39 +12:00
|
|
|
|
IconLoading = loading;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
if (!IconLoading)
|
|
|
|
|
{
|
|
|
|
|
if (Icon == null)
|
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
Icon = Resources.NotFound;
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
else if (HiddenEntry)
|
|
|
|
|
{
|
2023-05-19 09:28:52 +12:00
|
|
|
|
Icon = IconReader.AddIconOverlay(Icon, Resources.White50Percentage);
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-19 09:28:52 +12:00
|
|
|
|
|
|
|
|
|
internal void OpenItem(int clickCount)
|
|
|
|
|
{
|
|
|
|
|
bool doCloseAfterOpen = false;
|
|
|
|
|
|
|
|
|
|
if (!IsPointingToFolder)
|
|
|
|
|
{
|
|
|
|
|
if (clickCount == -1 ||
|
|
|
|
|
(clickCount == 1 && Settings.Default.OpenItemWithOneClick) ||
|
|
|
|
|
(clickCount == 2 && !Settings.Default.OpenItemWithOneClick))
|
|
|
|
|
{
|
|
|
|
|
string? workingDirectory = System.IO.Path.GetDirectoryName(ResolvedPath);
|
|
|
|
|
Log.ProcessStart(Path, string.Empty, false, workingDirectory, true, ResolvedPath);
|
|
|
|
|
if (!Settings.Default.StaysOpenWhenItemClicked)
|
|
|
|
|
{
|
|
|
|
|
doCloseAfterOpen = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (clickCount == -1 ||
|
|
|
|
|
(clickCount == 1 && Settings.Default.OpenDirectoryWithOneClick) ||
|
|
|
|
|
(clickCount == 2 && !Settings.Default.OpenDirectoryWithOneClick))
|
|
|
|
|
{
|
|
|
|
|
Log.ProcessStart(Path);
|
|
|
|
|
if (!Settings.Default.StaysOpenWhenItemClicked)
|
|
|
|
|
{
|
|
|
|
|
doCloseAfterOpen = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Owner != null)
|
|
|
|
|
{
|
|
|
|
|
if (clickCount == 1)
|
|
|
|
|
{
|
|
|
|
|
Owner.RiseItemOpened(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doCloseAfterOpen)
|
|
|
|
|
{
|
|
|
|
|
Owner.HideAllMenus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void OpenShellContextMenu(Point position)
|
|
|
|
|
{
|
|
|
|
|
if (IsPointingToFolder)
|
|
|
|
|
{
|
|
|
|
|
ShellContextMenu.OpenShellContextMenu(new DirectoryInfo(Path), position);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ShellContextMenu.OpenShellContextMenu(FileInfo, position);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void OpenSubMenu()
|
|
|
|
|
{
|
|
|
|
|
// TODO: always true? maybe only when cached in WaitToLoadMenu or keyboardInput?
|
|
|
|
|
if (Owner?.GetDataGridView().Items.Contains(this) ?? false)
|
|
|
|
|
{
|
|
|
|
|
Menu? openSubMenu = Owner.SubMenu;
|
|
|
|
|
|
|
|
|
|
// only re-open when the menu is not already open
|
|
|
|
|
if (SubMenu != null && SubMenu == openSubMenu)
|
|
|
|
|
{
|
|
|
|
|
// Close second level sub menus when already opened
|
|
|
|
|
openSubMenu.SelectedItem = null;
|
|
|
|
|
if (openSubMenu.SubMenu != null)
|
|
|
|
|
{
|
|
|
|
|
openSubMenu.SubMenu.HideWithFade(true);
|
|
|
|
|
openSubMenu.RefreshSelection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// In case another menu exists, close it
|
|
|
|
|
if (openSubMenu != null)
|
|
|
|
|
{
|
|
|
|
|
// Give the opening window focus
|
|
|
|
|
// if closing window lose focus, no window would have focus any more
|
|
|
|
|
Owner.Activate();
|
|
|
|
|
Owner.FocusTextBox();
|
|
|
|
|
openSubMenu.HideWithFade(true);
|
|
|
|
|
Owner.RefreshSelection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsPointingToFolder)
|
|
|
|
|
{
|
|
|
|
|
Owner.RiseStartLoadSubMenu(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void UpdateColors()
|
|
|
|
|
{
|
|
|
|
|
if (SubMenu != null)
|
|
|
|
|
{
|
|
|
|
|
BorderBrush = MenuDefines.ColorOpenFolderBorder;
|
|
|
|
|
BackgroundBrush = MenuDefines.ColorOpenFolder;
|
|
|
|
|
}
|
|
|
|
|
else if (IsSelected)
|
|
|
|
|
{
|
|
|
|
|
BorderBrush = MenuDefines.ColorSelectedItemBorder;
|
|
|
|
|
BackgroundBrush = MenuDefines.ColorSelectedItem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BorderBrush = Brushes.White;
|
|
|
|
|
BackgroundBrush = Brushes.White;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-23 11:02:31 +13:00
|
|
|
|
}
|
|
|
|
|
}
|