2154 lines
98 KiB
C#
2154 lines
98 KiB
C#
/*
|
|
Copyright (C) 2014-2019 de4dot@gmail.com
|
|
|
|
This file is part of dnSpy
|
|
|
|
dnSpy is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
dnSpy is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.Composition;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using dnlib.DotNet;
|
|
using dnlib.DotNet.Resources;
|
|
using dnSpy.AsmEditor.Commands;
|
|
using dnSpy.AsmEditor.Properties;
|
|
using dnSpy.AsmEditor.UndoRedo;
|
|
using dnSpy.Contracts.App;
|
|
using dnSpy.Contracts.Controls;
|
|
using dnSpy.Contracts.Documents.Tabs;
|
|
using dnSpy.Contracts.Documents.Tabs.DocViewer;
|
|
using dnSpy.Contracts.Documents.TreeView;
|
|
using dnSpy.Contracts.Documents.TreeView.Resources;
|
|
using dnSpy.Contracts.Extension;
|
|
using dnSpy.Contracts.Images;
|
|
using dnSpy.Contracts.Menus;
|
|
using dnSpy.Contracts.MVVM;
|
|
using dnSpy.Contracts.TreeView;
|
|
using dnSpy.Contracts.Utilities;
|
|
using WF = System.Windows.Forms;
|
|
|
|
namespace dnSpy.AsmEditor.Resources {
|
|
[ExportAutoLoaded]
|
|
sealed class CommandLoader : IAutoLoaded {
|
|
[ImportingConstructor]
|
|
CommandLoader(IWpfCommandService wpfCommandService, IDocumentTabService documentTabService,
|
|
DeleteResourceCommand.EditMenuCommand removeCmd1,
|
|
DeleteResourceElementCommand.EditMenuCommand removeCmd2,
|
|
DeleteResourceCommand.CodeCommand removeCmd3,
|
|
DeleteResourceElementCommand.CodeCommand removeCmd4,
|
|
ResourceSettingsCommand.EditMenuCommand settingsCmd10,
|
|
ResourceSettingsCommand.CodeCommand settingsCmd11,
|
|
ResourceElementSettingsCommand.EditMenuCommand settingsCmd20,
|
|
ResourceElementSettingsCommand.CodeCommand settingsCmd21,
|
|
ImageResourceElementSettingsCommand.EditMenuCommand settingsCmd30,
|
|
ImageResourceElementSettingsCommand.CodeCommand settingsCmd31,
|
|
SerializedImageResourceElementSettingsCommand.EditMenuCommand settingsCmd40,
|
|
SerializedImageResourceElementSettingsCommand.CodeCommand settingsCmd41,
|
|
SerializedImageListStreamerResourceElementSettingsCommand.EditMenuCommand settingsCmd50,
|
|
SerializedImageListStreamerResourceElementSettingsCommand.CodeCommand settingsCmd51) {
|
|
wpfCommandService.AddRemoveCommand(removeCmd1);
|
|
wpfCommandService.AddRemoveCommand(removeCmd2);
|
|
wpfCommandService.AddRemoveCommand(removeCmd3, documentTabService);
|
|
wpfCommandService.AddRemoveCommand(removeCmd4, documentTabService);
|
|
wpfCommandService.AddSettingsCommand(documentTabService, settingsCmd10, settingsCmd11);
|
|
wpfCommandService.AddSettingsCommand(documentTabService, settingsCmd20, settingsCmd21);
|
|
wpfCommandService.AddSettingsCommand(documentTabService, settingsCmd30, settingsCmd31);
|
|
wpfCommandService.AddSettingsCommand(documentTabService, settingsCmd40, settingsCmd41);
|
|
wpfCommandService.AddSettingsCommand(documentTabService, settingsCmd50, settingsCmd51);
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
sealed class DeleteResourceCommand : IUndoCommand {
|
|
[ExportMenuItem(Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_DELETE, Order = 80)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => DeleteResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => DeleteResourceCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(AsmEditorContext context) => DeleteResourceCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_DELETE, Order = 80)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IDocumentTreeView documentTreeView)
|
|
: base(documentTreeView) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => DeleteResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => DeleteResourceCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(AsmEditorContext context) => DeleteResourceCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_DELETE, Order = 80)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IDocumentTreeView documentTreeView)
|
|
: base(documentTreeView) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && DeleteResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => DeleteResourceCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(CodeContext context) => DeleteResourceCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
static string GetHeader(DocumentTreeNodeData[] nodes) {
|
|
if (nodes.Length == 1)
|
|
return string.Format(dnSpy_AsmEditor_Resources.DeleteX, UIUtilities.EscapeMenuItemHeader(nodes[0].ToString()));
|
|
return string.Format(dnSpy_AsmEditor_Resources.DeleteResourcesCommand, nodes.Length);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => nodes.Length > 0 && nodes.All(n => ResourceNode.GetResource(n) is not null);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
undoCommandService.Value.Add(new DeleteResourceCommand(nodes));
|
|
}
|
|
|
|
struct DeleteModelNodes {
|
|
ModelInfo[]? infos;
|
|
|
|
readonly struct ModelInfo {
|
|
public readonly ModuleDef OwnerModule;
|
|
public readonly int Index;
|
|
|
|
public ModelInfo(ModuleDef module, Resource rsrc) {
|
|
OwnerModule = module;
|
|
Index = module.Resources.IndexOf(rsrc);
|
|
Debug.Assert(Index >= 0);
|
|
if (Index < 0)
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
public void Delete(DocumentTreeNodeData[] nodes, DocumentTreeNodeData[] parents) {
|
|
Debug2.Assert(infos is null);
|
|
if (infos is not null)
|
|
throw new InvalidOperationException();
|
|
|
|
infos = new ModelInfo[nodes.Length];
|
|
|
|
for (int i = 0; i < infos.Length; i++) {
|
|
var node = nodes[i];
|
|
|
|
var module = parents[i].GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
var resource = ResourceNode.GetResource(node) ?? throw new InvalidOperationException();
|
|
var info = new ModelInfo(module, resource);
|
|
infos[i] = info;
|
|
info.OwnerModule.Resources.RemoveAt(info.Index);
|
|
}
|
|
}
|
|
|
|
public void Restore(DocumentTreeNodeData[] nodes) {
|
|
Debug2.Assert(infos is not null);
|
|
if (infos is null)
|
|
throw new InvalidOperationException();
|
|
Debug.Assert(infos.Length == nodes.Length);
|
|
if (infos.Length != nodes.Length)
|
|
throw new InvalidOperationException();
|
|
|
|
for (int i = infos.Length - 1; i >= 0; i--) {
|
|
var node = nodes[i];
|
|
ref readonly var info = ref infos[i];
|
|
var resource = ResourceNode.GetResource(node) ?? throw new InvalidOperationException();
|
|
info.OwnerModule.Resources.Insert(info.Index, resource);
|
|
}
|
|
|
|
infos = null;
|
|
}
|
|
}
|
|
|
|
DeletableNodes<DocumentTreeNodeData> nodes;
|
|
DeleteModelNodes modelNodes;
|
|
|
|
DeleteResourceCommand(DocumentTreeNodeData[] rsrcNodes) => nodes = new DeletableNodes<DocumentTreeNodeData>(rsrcNodes);
|
|
|
|
public string Description => dnSpy_AsmEditor_Resources.DeleteResourceCommand;
|
|
|
|
public void Execute() {
|
|
nodes.Delete();
|
|
modelNodes.Delete(nodes.Nodes, nodes.Parents!);
|
|
}
|
|
|
|
public void Undo() {
|
|
modelNodes.Restore(nodes.Nodes);
|
|
nodes.Restore();
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects => nodes.Nodes;
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
sealed class DeleteResourceElementCommand : IUndoCommand {
|
|
[ExportMenuItem(Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_DELETE, Order = 90)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => DeleteResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => DeleteResourceElementCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(AsmEditorContext context) => DeleteResourceElementCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_DELETE, Order = 90)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IDocumentTreeView documentTreeView)
|
|
: base(documentTreeView) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => DeleteResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => DeleteResourceElementCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(AsmEditorContext context) => DeleteResourceElementCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:DeleteResourceCommand", Icon = DsImagesAttribute.Cancel, InputGestureText = "res:DeleteCommandKey", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_DELETE, Order = 90)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IDocumentTreeView documentTreeView)
|
|
: base(documentTreeView) => this.undoCommandService = undoCommandService;
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && DeleteResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => DeleteResourceElementCommand.Execute(undoCommandService, context.Nodes);
|
|
public override string? GetHeader(CodeContext context) => DeleteResourceElementCommand.GetHeader(context.Nodes);
|
|
}
|
|
|
|
static string GetHeader(DocumentTreeNodeData[] nodes) {
|
|
if (nodes.Length == 1)
|
|
return string.Format(dnSpy_AsmEditor_Resources.DeleteX, UIUtilities.EscapeMenuItemHeader(nodes[0].ToString()));
|
|
return string.Format(dnSpy_AsmEditor_Resources.DeleteResourcesCommand, nodes.Length);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => nodes.Length > 0 && nodes.All(n => ResourceElementNode.GetResourceElement(n) is not null);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
undoCommandService.Value.Add(new DeleteResourceElementCommand(nodes));
|
|
}
|
|
|
|
sealed class ModuleInfo {
|
|
public readonly ResourceElementSetNode Node;
|
|
readonly ModuleDef Module;
|
|
readonly int Index;
|
|
readonly Resource Resource;
|
|
Resource? NewResource;
|
|
|
|
public ModuleInfo(ResourceElementSetNode node) {
|
|
Node = node ?? throw new InvalidOperationException();
|
|
var module = node.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
Module = module ?? throw new InvalidOperationException();
|
|
Index = module.Resources.IndexOf(node.Resource);
|
|
Debug.Assert(Index >= 0);
|
|
if (Index < 0)
|
|
throw new InvalidOperationException();
|
|
Resource = node.Resource;
|
|
}
|
|
|
|
public void Replace() {
|
|
Debug.Assert(Index < Module.Resources.Count && Module.Resources[Index] == Node.Resource);
|
|
if (NewResource is null) {
|
|
Node.RegenerateEmbeddedResource();
|
|
NewResource = Node.Resource;
|
|
}
|
|
else
|
|
Node.Resource = NewResource;
|
|
Module.Resources[Index] = NewResource;
|
|
}
|
|
|
|
public void Restore() {
|
|
Debug.Assert(Index < Module.Resources.Count && Module.Resources[Index] == Node.Resource);
|
|
Node.Resource = Resource;
|
|
Module.Resources[Index] = Resource;
|
|
}
|
|
}
|
|
|
|
DeletableNodes<DocumentTreeNodeData> nodes;
|
|
readonly List<ModuleInfo> savedResources = new List<ModuleInfo>();
|
|
|
|
DeleteResourceElementCommand(DocumentTreeNodeData[] rsrcNodes) => nodes = new DeletableNodes<DocumentTreeNodeData>(rsrcNodes);
|
|
|
|
public string Description => dnSpy_AsmEditor_Resources.DeleteResourceCommand;
|
|
|
|
public void Execute() {
|
|
Debug.Assert(savedResources.Count == 0);
|
|
savedResources.AddRange(nodes.Nodes.Select(a => a.GetAncestorOrSelf<ResourceElementSetNode>()!).Distinct().Select(a => new ModuleInfo(a)));
|
|
|
|
nodes.Delete();
|
|
|
|
for (int i = 0; i < savedResources.Count; i++)
|
|
savedResources[i].Replace();
|
|
}
|
|
|
|
public void Undo() {
|
|
Debug.Assert(savedResources.Count > 0);
|
|
for (int i = savedResources.Count - 1; i >= 0; i--)
|
|
savedResources[i].Restore();
|
|
savedResources.Clear();
|
|
|
|
nodes.Restore();
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects => nodes.Nodes;
|
|
}
|
|
|
|
abstract class SaveResourcesCommandBase : MenuItemBase {
|
|
readonly bool useSubDirs;
|
|
readonly ResourceDataType resourceDataType;
|
|
|
|
protected SaveResourcesCommandBase(bool useSubDirs, ResourceDataType resourceDataType) {
|
|
this.useSubDirs = useSubDirs;
|
|
this.resourceDataType = resourceDataType;
|
|
}
|
|
|
|
protected abstract IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context);
|
|
|
|
IResourceDataProvider[]? Filter(IResourceDataProvider[] nodes) {
|
|
nodes = nodes.Where(a => a.GetResourceData(resourceDataType).Any()).ToArray();
|
|
return nodes.Length == 0 ? null : nodes;
|
|
}
|
|
|
|
protected IResourceDataProvider[]? CodeGetResourceNodes(IMenuItemContext context) {
|
|
if (context.CreatorObject.Guid != new Guid(MenuConstants.GUIDOBJ_DOCUMENTVIEWERCONTROL_GUID))
|
|
return null;
|
|
|
|
var @ref = context.Find<TextReference>();
|
|
if (@ref is null)
|
|
return null;
|
|
if (!(@ref.Reference is DocumentTreeNodeData node && ResourceDataProviderUtils.GetResourceDataProvider(node) is IResourceDataProvider rsrcNode))
|
|
return null;
|
|
|
|
return Filter(new[] { rsrcNode });
|
|
}
|
|
|
|
protected IResourceDataProvider[]? FilesGetResourceNodes(IMenuItemContext context) {
|
|
if (context.CreatorObject.Guid != new Guid(MenuConstants.GUIDOBJ_DOCUMENTS_TREEVIEW_GUID))
|
|
return null;
|
|
|
|
var selNodes = context.Find<TreeNodeData[]>();
|
|
if (selNodes is null)
|
|
return null;
|
|
|
|
if (selNodes.Length == 1 && selNodes[0] is ResourcesFolderNode) {
|
|
var rlist = (ResourcesFolderNode)selNodes[0];
|
|
rlist.TreeNode.EnsureChildrenLoaded();
|
|
selNodes = rlist.TreeNode.DataChildren.Cast<DocumentTreeNodeData>().ToArray();
|
|
}
|
|
return Filter(selNodes.Select(a => ResourceDataProviderUtils.GetResourceDataProvider((DocumentTreeNodeData)a)).OfType<IResourceDataProvider>().ToArray());
|
|
}
|
|
|
|
protected ResourceData[] GetResourceData(IResourceDataProvider[]? nodes) => SaveResources.GetResourceData(nodes, resourceDataType);
|
|
public override bool IsVisible(IMenuItemContext context) => GetResourceNodes(context) is not null;
|
|
public override void Execute(IMenuItemContext context) => SaveResources.Save(GetResourceNodes(context), useSubDirs, resourceDataType);
|
|
}
|
|
|
|
static class SaveResourcesCommand {
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SAVE, Order = 0)]
|
|
sealed class CodeCommand : SaveResourcesCommandBase {
|
|
public CodeCommand()
|
|
: base(false, ResourceDataType.Deserialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => CodeGetResourceNodes(context);
|
|
}
|
|
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SAVE, Order = 0)]
|
|
sealed class DocumentsCommand : SaveResourcesCommandBase {
|
|
public DocumentsCommand()
|
|
: base(false, ResourceDataType.Deserialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => FilesGetResourceNodes(context);
|
|
}
|
|
|
|
static string GetHeaderInternal(ResourceData[] infos) {
|
|
if (infos.Length == 1)
|
|
return string.Format(dnSpy_AsmEditor_Resources.SaveResourceCommand, UIUtilities.EscapeMenuItemHeader(infos[0].Name));
|
|
return string.Format(dnSpy_AsmEditor_Resources.SaveResourcesCommand, infos.Length);
|
|
}
|
|
}
|
|
|
|
static class SaveWithPathResourcesCommand {
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SAVE, Order = 10)]
|
|
sealed class CodeCommand : SaveResourcesCommandBase {
|
|
public CodeCommand()
|
|
: base(true, ResourceDataType.Deserialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => CodeGetResourceNodes(context);
|
|
public override bool IsVisible(IMenuItemContext context) => IsVisibleInternal(GetResourceData(GetResourceNodes(context)));
|
|
}
|
|
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SAVE, Order = 10)]
|
|
sealed class DocumentsCommand : SaveResourcesCommandBase {
|
|
public DocumentsCommand()
|
|
: base(true, ResourceDataType.Deserialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => FilesGetResourceNodes(context);
|
|
public override bool IsVisible(IMenuItemContext context) => IsVisibleInternal(GetResourceData(GetResourceNodes(context)));
|
|
}
|
|
|
|
static string GetHeaderInternal(ResourceData[] infos) => string.Format(dnSpy_AsmEditor_Resources.SaveResourcesSubDirectoriesCommand, infos.Length);
|
|
internal static bool IsVisibleInternal(ResourceData[] infos) => infos.Length > 1 && infos.Any(a => a.Name.Contains('/') || a.Name.Contains('\\'));
|
|
}
|
|
|
|
static class SaveRawResourcesCommand {
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SAVE, Order = 20)]
|
|
sealed class CodeCommand : SaveResourcesCommandBase {
|
|
public CodeCommand()
|
|
: base(false, ResourceDataType.Serialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => CodeGetResourceNodes(context);
|
|
}
|
|
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SAVE, Order = 20)]
|
|
sealed class DocumentsCommand : SaveResourcesCommandBase {
|
|
public DocumentsCommand()
|
|
: base(false, ResourceDataType.Serialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => FilesGetResourceNodes(context);
|
|
}
|
|
|
|
static string GetHeaderInternal(ResourceData[] infos) {
|
|
if (infos.Length == 1)
|
|
return string.Format(dnSpy_AsmEditor_Resources.RawSaveResourceCommand, UIUtilities.EscapeMenuItemHeader(infos[0].Name));
|
|
return string.Format(dnSpy_AsmEditor_Resources.RawSaveResourcesCommand, infos.Length);
|
|
}
|
|
}
|
|
|
|
static class SaveRawWithPathResourcesCommand {
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SAVE, Order = 30)]
|
|
sealed class CodeCommand : SaveResourcesCommandBase {
|
|
public CodeCommand()
|
|
: base(true, ResourceDataType.Serialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => CodeGetResourceNodes(context);
|
|
public override bool IsVisible(IMenuItemContext context) => IsVisibleInternal(GetResourceData(GetResourceNodes(context)));
|
|
}
|
|
|
|
[ExportMenuItem(Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SAVE, Order = 30)]
|
|
sealed class DocumentsCommand : SaveResourcesCommandBase {
|
|
public DocumentsCommand()
|
|
: base(true, ResourceDataType.Serialized) {
|
|
}
|
|
|
|
public override string? GetHeader(IMenuItemContext context) => GetHeaderInternal(GetResourceData(GetResourceNodes(context)));
|
|
protected override IResourceDataProvider[]? GetResourceNodes(IMenuItemContext context) => FilesGetResourceNodes(context);
|
|
public override bool IsVisible(IMenuItemContext context) => IsVisibleInternal(GetResourceData(GetResourceNodes(context)));
|
|
}
|
|
|
|
static string GetHeaderInternal(ResourceData[] infos) => string.Format(dnSpy_AsmEditor_Resources.RawSaveResourcesSubDirectoriesCommand, infos.Length);
|
|
static bool IsVisibleInternal(ResourceData[] infos) => SaveWithPathResourcesCommand.IsVisibleInternal(infos);
|
|
}
|
|
|
|
static class ResUtils {
|
|
public static bool CanExecuteResourceListCommand(DocumentTreeNodeData[] nodes) => GetResourceListTreeNode(nodes) is not null;
|
|
|
|
public static ResourcesFolderNode? GetResourceListTreeNode(DocumentTreeNodeData[] nodes) {
|
|
if (nodes.Length != 1)
|
|
return null;
|
|
ResourcesFolderNode? rsrcListNode;
|
|
if ((rsrcListNode = nodes[0] as ResourcesFolderNode) is not null)
|
|
return rsrcListNode;
|
|
rsrcListNode = nodes[0].TreeNode.Parent?.Data as ResourcesFolderNode;
|
|
if (rsrcListNode is not null)
|
|
return rsrcListNode;
|
|
|
|
var modNode = nodes[0] as ModuleDocumentNode;
|
|
if (modNode is null)
|
|
return null;
|
|
modNode.TreeNode.EnsureChildrenLoaded();
|
|
rsrcListNode = (ResourcesFolderNode?)modNode.TreeNode.DataChildren.FirstOrDefault(a => a is ResourcesFolderNode);
|
|
if (rsrcListNode is null) // If not a module node
|
|
return null;
|
|
rsrcListNode.TreeNode.EnsureChildrenLoaded();
|
|
if (rsrcListNode.TreeNode.Children.Count == 0)
|
|
return rsrcListNode;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
sealed class CreateFileResourceCommand : IUndoCommand {
|
|
[ExportMenuItem(Header = "res:CreateFileResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 100)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateFileResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 100)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateFileResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 100)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => ResUtils.CanExecuteResourceListCommand(nodes);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcListNode = ResUtils.GetResourceListTreeNode(nodes)!;
|
|
|
|
var module = rsrcListNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var dlg = new WF.OpenFileDialog {
|
|
RestoreDirectory = true,
|
|
Multiselect = true,
|
|
};
|
|
if (dlg.ShowDialog() != WF.DialogResult.OK)
|
|
return;
|
|
var fnames = dlg.FileNames;
|
|
if (fnames.Length == 0)
|
|
return;
|
|
|
|
var newNodes = new NodeAndResource[fnames.Length];
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup);
|
|
for (int i = 0; i < fnames.Length; i++) {
|
|
var fn = fnames[i];
|
|
try {
|
|
var rsrc = new EmbeddedResource(Path.GetFileName(fn), File.ReadAllBytes(fn), ManifestResourceAttributes.Public);
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, rsrc, treeNodeGroup)).Data;
|
|
newNodes[i] = new NodeAndResource(node);
|
|
}
|
|
catch (Exception ex) {
|
|
MsgBox.Instance.Show(string.Format(dnSpy_AsmEditor_Resources.Error_ReadingFiles, ex.Message));
|
|
return;
|
|
}
|
|
}
|
|
|
|
undoCommandService.Value.Add(new CreateFileResourceCommand(rsrcListNode, newNodes));
|
|
appService.DocumentTabService.FollowReference(newNodes[0].Node);
|
|
}
|
|
|
|
readonly ModuleDef module;
|
|
readonly ResourcesFolderNode rsrcListNode;
|
|
readonly NodeAndResource[] nodes;
|
|
|
|
CreateFileResourceCommand(ResourcesFolderNode rsrcListNode, NodeAndResource[] nodes) {
|
|
module = rsrcListNode.GetModule()!;
|
|
Debug2.Assert(module is not null);
|
|
this.rsrcListNode = rsrcListNode;
|
|
this.nodes = nodes;
|
|
}
|
|
|
|
public string Description => dnSpy_AsmEditor_Resources.CreateFileResourceCommand2;
|
|
|
|
public void Execute() {
|
|
foreach (var info in nodes) {
|
|
module.Resources.Add(info.Resource);
|
|
rsrcListNode.TreeNode.AddChild(info.Node.TreeNode);
|
|
}
|
|
}
|
|
|
|
public void Undo() {
|
|
for (int i = nodes.Length - 1; i >= 0; i--) {
|
|
var info = nodes[i];
|
|
bool b = rsrcListNode.TreeNode.Children.Remove(info.Node.TreeNode);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
b = module.Resources.Remove(info.Resource);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
}
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects {
|
|
get { yield return rsrcListNode; }
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
abstract class CreateResourceTreeNodeCommand : IUndoCommand {
|
|
readonly ModuleDef module;
|
|
readonly ResourcesFolderNode rsrcListNode;
|
|
readonly NodeAndResource resTreeNode;
|
|
|
|
protected CreateResourceTreeNodeCommand(ResourcesFolderNode rsrcListNode, NodeAndResource resTreeNode) {
|
|
module = rsrcListNode.GetModule()!;
|
|
Debug2.Assert(module is not null);
|
|
this.rsrcListNode = rsrcListNode;
|
|
this.resTreeNode = resTreeNode;
|
|
}
|
|
|
|
public abstract string Description { get; }
|
|
|
|
public void Execute() {
|
|
module.Resources.Add(resTreeNode.Resource);
|
|
rsrcListNode.TreeNode.AddChild(resTreeNode.Node.TreeNode);
|
|
}
|
|
|
|
public void Undo() {
|
|
bool b = rsrcListNode.TreeNode.Children.Remove(resTreeNode.Node.TreeNode);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
b = module.Resources.Remove(resTreeNode.Resource);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects {
|
|
get { yield return rsrcListNode; }
|
|
}
|
|
}
|
|
|
|
sealed class CreateMultiFileResourceCommand : CreateResourceTreeNodeCommand {
|
|
[ExportMenuItem(Header = "res:CreateMultiFileResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 110)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateMultiFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateMultiFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateMultiFileResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 110)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateMultiFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateMultiFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateMultiFileResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 110)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateMultiFileResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateMultiFileResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => ResUtils.CanExecuteResourceListCommand(nodes);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcListNode = ResUtils.GetResourceListTreeNode(nodes)!;
|
|
|
|
var module = rsrcListNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var options = new ResourceOptions {
|
|
ResourceType = ResourceType.Embedded,
|
|
Name = "my.resources",
|
|
Attributes = ManifestResourceAttributes.Public,
|
|
};
|
|
var data = new ResourceVM(options, module);
|
|
var win = new ResourceDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.CreateMultiFileResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var outStream = new MemoryStream();
|
|
ResourceWriter.Write(module, outStream, new ResourceElementSet());
|
|
var er = new EmbeddedResource(data.Name, outStream.ToArray(), data.Attributes);
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup);
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, er, treeNodeGroup)).Data;
|
|
|
|
undoCommandService.Value.Add(new CreateMultiFileResourceCommand(rsrcListNode, new NodeAndResource(node)));
|
|
appService.DocumentTabService.FollowReference(node);
|
|
}
|
|
|
|
CreateMultiFileResourceCommand(ResourcesFolderNode rsrcListNode, NodeAndResource resTreeNode)
|
|
: base(rsrcListNode, resTreeNode) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateMultiFileResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateAssemblyLinkedResourceCommand : CreateResourceTreeNodeCommand {
|
|
[ExportMenuItem(Header = "res:CreateAssemblyLinkedResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 120)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateAssemblyLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateAssemblyLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateAssemblyLinkedResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 120)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateAssemblyLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateAssemblyLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateAssemblyLinkedResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 120)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateAssemblyLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateAssemblyLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => ResUtils.CanExecuteResourceListCommand(nodes);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcListNode = ResUtils.GetResourceListTreeNode(nodes)!;
|
|
|
|
var module = rsrcListNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var options = new ResourceOptions {
|
|
ResourceType = ResourceType.AssemblyLinked,
|
|
Name = "asmlinked",
|
|
Attributes = ManifestResourceAttributes.Public,
|
|
Assembly = module.CorLibTypes.AssemblyRef,
|
|
};
|
|
var data = new ResourceVM(options, module);
|
|
var win = new ResourceDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.CreateAssemblyLinkedResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup);
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, new AssemblyLinkedResource(data.Name, data.Assembly, data.Attributes), treeNodeGroup)).Data;
|
|
undoCommandService.Value.Add(new CreateAssemblyLinkedResourceCommand(rsrcListNode, new NodeAndResource(node)));
|
|
appService.DocumentTabService.FollowReference(node);
|
|
}
|
|
|
|
CreateAssemblyLinkedResourceCommand(ResourcesFolderNode rsrcListNode, NodeAndResource resTreeNode)
|
|
: base(rsrcListNode, resTreeNode) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateAssemblyLinkedResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateFileLinkedResourceCommand : CreateResourceTreeNodeCommand {
|
|
[ExportMenuItem(Header = "res:CreateFileLinkedResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 130)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateFileLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateFileLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateFileLinkedResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 130)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateFileLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateFileLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateFileLinkedResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 130)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateFileLinkedResourceCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateFileLinkedResourceCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => ResUtils.CanExecuteResourceListCommand(nodes);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcListNode = ResUtils.GetResourceListTreeNode(nodes)!;
|
|
|
|
var module = rsrcListNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var options = new ResourceOptions {
|
|
ResourceType = ResourceType.Linked,
|
|
Name = "filelinked",
|
|
Attributes = ManifestResourceAttributes.Public,
|
|
File = new FileDefUser("somefile", dnlib.DotNet.FileAttributes.ContainsNoMetadata, Array.Empty<byte>()),
|
|
};
|
|
var data = new ResourceVM(options, module);
|
|
var win = new ResourceDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.CreateFileLinkedResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var opts = data.CreateResourceOptions();
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup);
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, new LinkedResource(opts.Name, opts.File, opts.Attributes), treeNodeGroup)).Data;
|
|
undoCommandService.Value.Add(new CreateFileLinkedResourceCommand(rsrcListNode, new NodeAndResource(node)));
|
|
appService.DocumentTabService.FollowReference(node);
|
|
}
|
|
|
|
CreateFileLinkedResourceCommand(ResourcesFolderNode rsrcListNode, NodeAndResource resTreeNode)
|
|
: base(rsrcListNode, resTreeNode) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateFileLinkedResourceCommand2;
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
sealed class ResourceSettingsCommand : IUndoCommand {
|
|
[ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SETTINGS, Order = 80)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ResourceSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ResourceSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_SETTINGS, Order = 90)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ResourceSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ResourceSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SETTINGS, Order = 90)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => ResourceSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => ResourceSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => nodes.Length == 1 && ResourceNode.GetResource(nodes[0]) is not null;
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcNode = new NodeAndResource(nodes[0]);
|
|
|
|
var module = nodes[0].GetParentModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var data = new ResourceVM(new ResourceOptions(rsrcNode.Resource), module);
|
|
var win = new ResourceDlg();
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
undoCommandService.Value.Add(new ResourceSettingsCommand(rsrcNode, data.CreateResourceOptions()));
|
|
}
|
|
|
|
readonly NodeAndResource rsrcNode;
|
|
readonly ResourceOptions newOptions;
|
|
readonly ResourceOptions origOptions;
|
|
readonly DocumentTreeNodeData origParentNode;
|
|
readonly int origParentChildIndex;
|
|
readonly bool nameChanged;
|
|
|
|
ResourceSettingsCommand(NodeAndResource rsrcNode, ResourceOptions options) {
|
|
this.rsrcNode = rsrcNode;
|
|
newOptions = options;
|
|
origOptions = new ResourceOptions(rsrcNode.Resource);
|
|
|
|
origParentNode = (DocumentTreeNodeData)rsrcNode.Node.TreeNode.Parent!.Data;
|
|
origParentChildIndex = origParentNode.TreeNode.Children.IndexOf(rsrcNode.Node.TreeNode);
|
|
Debug.Assert(origParentChildIndex >= 0);
|
|
if (origParentChildIndex < 0)
|
|
throw new InvalidOperationException();
|
|
|
|
nameChanged = origOptions.Name != newOptions.Name;
|
|
}
|
|
|
|
public string Description => dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
|
|
public void Execute() {
|
|
if (nameChanged) {
|
|
bool b = origParentChildIndex < origParentNode.TreeNode.Children.Count && origParentNode.TreeNode.Children[origParentChildIndex] == rsrcNode.Node.TreeNode;
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
|
|
var isNodeSelected = rsrcNode.Node.TreeNode.TreeView.SelectedItem == rsrcNode.Node;
|
|
|
|
origParentNode.TreeNode.Children.RemoveAt(origParentChildIndex);
|
|
newOptions.CopyTo(rsrcNode.Resource);
|
|
origParentNode.TreeNode.AddChild(rsrcNode.Node.TreeNode);
|
|
|
|
if (isNodeSelected)
|
|
origParentNode.TreeNode.TreeView.SelectItems(new[] { rsrcNode.Node });
|
|
}
|
|
else
|
|
newOptions.CopyTo(rsrcNode.Resource);
|
|
rsrcNode.Node.TreeNode.RefreshUI();
|
|
}
|
|
|
|
public void Undo() {
|
|
if (nameChanged) {
|
|
var isNodeSelected = rsrcNode.Node.TreeNode.TreeView.SelectedItem == rsrcNode.Node;
|
|
|
|
bool b = origParentNode.TreeNode.Children.Remove(rsrcNode.Node.TreeNode);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
|
|
origOptions.CopyTo(rsrcNode.Resource);
|
|
origParentNode.TreeNode.Children.Insert(origParentChildIndex, rsrcNode.Node.TreeNode);
|
|
|
|
if (isNodeSelected)
|
|
origParentNode.TreeNode.TreeView.SelectItems(new[] { rsrcNode.Node });
|
|
}
|
|
else
|
|
origOptions.CopyTo(rsrcNode.Resource);
|
|
rsrcNode.Node.TreeNode.RefreshUI();
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects {
|
|
get { yield return rsrcNode.Node; }
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
abstract class CreateResourceElementCommandBase : IUndoCommand {
|
|
readonly ModuleDef module;
|
|
readonly ResourceElementSetNode rsrcSetNode;
|
|
readonly NodeAndResourceElement[] nodes;
|
|
readonly Resource resource;
|
|
readonly int resourceIndex;
|
|
Resource? newResource;
|
|
|
|
protected CreateResourceElementCommandBase(ResourceElementSetNode rsrcSetNode, NodeAndResourceElement[] nodes) {
|
|
module = rsrcSetNode.GetModule()!;
|
|
Debug2.Assert(module is not null);
|
|
this.rsrcSetNode = rsrcSetNode;
|
|
this.nodes = nodes;
|
|
resource = rsrcSetNode.Resource;
|
|
resourceIndex = module.Resources.IndexOf(resource);
|
|
Debug.Assert(resourceIndex >= 0);
|
|
if (resourceIndex < 0)
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
public abstract string Description { get; }
|
|
|
|
public void Execute() {
|
|
Debug.Assert(resource == rsrcSetNode.Resource);
|
|
Debug.Assert(module.Resources[resourceIndex] == resource);
|
|
foreach (var node in nodes)
|
|
rsrcSetNode.TreeNode.AddChild(node.Node.TreeNode);
|
|
if (newResource is null) {
|
|
rsrcSetNode.RegenerateEmbeddedResource();
|
|
newResource = rsrcSetNode.Resource;
|
|
}
|
|
else
|
|
rsrcSetNode.Resource = newResource;
|
|
module.Resources[resourceIndex] = newResource;
|
|
}
|
|
|
|
public void Undo() {
|
|
for (int i = nodes.Length - 1; i >= 0; i--) {
|
|
var node = nodes[i];
|
|
bool b = rsrcSetNode.TreeNode.Children.Remove(node.Node.TreeNode);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
}
|
|
rsrcSetNode.Resource = resource;
|
|
module.Resources[resourceIndex] = resource;
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects {
|
|
get { yield return rsrcSetNode; }
|
|
}
|
|
}
|
|
|
|
sealed class CreateImageResourceElementCommand : CreateResourceElementCommandBase {
|
|
[ExportMenuItem(Header = "res:CreateBitMapIconResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 140)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateImageResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateImageResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateBitMapIconResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 140)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateImageResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateImageResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateBitMapIconResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 140)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateImageResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateImageResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is ResourceElementSetNode || nodes[0].TreeNode.Parent?.Data is ResourceElementSetNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcSetNode = nodes[0] as ResourceElementSetNode;
|
|
if (rsrcSetNode is null)
|
|
rsrcSetNode = nodes[0].TreeNode.Parent!.Data as ResourceElementSetNode;
|
|
Debug2.Assert(rsrcSetNode is not null);
|
|
|
|
var module = rsrcSetNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var dlg = new WF.OpenFileDialog {
|
|
RestoreDirectory = true,
|
|
Multiselect = true,
|
|
Filter = PickFilenameConstants.ImagesFilter,
|
|
};
|
|
if (dlg.ShowDialog() != WF.DialogResult.OK)
|
|
return;
|
|
var fnames = dlg.FileNames;
|
|
if (fnames.Length == 0)
|
|
return;
|
|
|
|
var newNodes = new List<NodeAndResourceElement>(fnames.Length);
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceElementTreeNodeGroup);
|
|
string? error = null;
|
|
for (int i = 0; i < fnames.Length; i++) {
|
|
var fn = fnames[i];
|
|
try {
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, SerializationUtilities.CreateSerializedImage(fn), treeNodeGroup)).Data;
|
|
newNodes.Add(new NodeAndResourceElement(node));
|
|
}
|
|
catch (Exception ex) {
|
|
if (error is null)
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_ReadingFiles, ex.Message);
|
|
}
|
|
}
|
|
if (error is not null)
|
|
MsgBox.Instance.Show(error);
|
|
if (newNodes.Count == 0)
|
|
return;
|
|
|
|
undoCommandService.Value.Add(new CreateImageResourceElementCommand(rsrcSetNode, newNodes.ToArray()));
|
|
appService.DocumentTabService.FollowReference(newNodes[0].Node);
|
|
}
|
|
|
|
CreateImageResourceElementCommand(ResourceElementSetNode rsrcSetNode, NodeAndResourceElement[] nodes)
|
|
: base(rsrcSetNode, nodes) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateBitMapIconResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateImageListResourceElementCommand : CreateResourceElementCommandBase {
|
|
[ExportMenuItem(Header = "res:CreateImageListStreamerResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 150)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateImageListResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateImageListResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateImageListStreamerResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 150)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateImageListResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateImageListResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateImageListStreamerResourceCommand", Icon = DsImagesAttribute.NewImage, Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 150)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateImageListResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateImageListResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is ResourceElementSetNode || nodes[0].TreeNode.Parent?.Data is ResourceElementSetNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcSetNode = nodes[0] as ResourceElementSetNode;
|
|
if (rsrcSetNode is null)
|
|
rsrcSetNode = nodes[0].TreeNode.Parent!.Data as ResourceElementSetNode;
|
|
Debug2.Assert(rsrcSetNode is not null);
|
|
|
|
var module = rsrcSetNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var data = new ImageListVM(new ImageListOptions() { Name = "my.ImageStream" });
|
|
var win = new ImageListDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.CreateImageListStreamerResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
if (data.ImageListStreamerVM.Collection.Count == 0) {
|
|
MsgBox.Instance.Show(dnSpy_AsmEditor_Resources.Error_EmptyImageList);
|
|
return;
|
|
}
|
|
|
|
var listOpts = data.CreateImageListOptions();
|
|
ResourceElementOptions? opts = null;
|
|
string error;
|
|
try {
|
|
opts = new ResourceElementOptions(SerializedImageListStreamerUtilities.Serialize(listOpts));
|
|
error = SerializedImageListStreamerUtilities.CheckCanUpdateData(module, opts.Create());
|
|
}
|
|
catch (Exception ex) {
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_CouldNotSerializeImages, ex.Message);
|
|
}
|
|
if (!string.IsNullOrEmpty(error)) {
|
|
MsgBox.Instance.Show(error);
|
|
return;
|
|
}
|
|
Debug2.Assert(opts is not null);
|
|
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceElementTreeNodeGroup);
|
|
var newNode = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, opts.Create(), treeNodeGroup)).Data;
|
|
undoCommandService.Value.Add(new CreateImageListResourceElementCommand(rsrcSetNode, newNode));
|
|
appService.DocumentTabService.FollowReference(newNode);
|
|
}
|
|
|
|
CreateImageListResourceElementCommand(ResourceElementSetNode rsrcSetNode, DocumentTreeNodeData node)
|
|
: base(rsrcSetNode, new[] { new NodeAndResourceElement(node) }) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateImageListStreamerResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateByteArrayResourceElementCommand : CreateResourceElementCommandBase {
|
|
[ExportMenuItem(Header = "res:CreateByteArrayResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 160)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateByteArrayResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateByteArrayResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateByteArrayResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 170)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateByteArrayResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateByteArrayResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateByteArrayResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 170)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateByteArrayResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateByteArrayResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is ResourceElementSetNode || nodes[0].TreeNode.Parent?.Data is ResourceElementSetNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
Execute(undoCommandService, appService, resourceNodeFactory, nodes, ResourceTypeCode.ByteArray, (a, b) => new CreateByteArrayResourceElementCommand(a, b));
|
|
}
|
|
|
|
internal static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes, ResourceTypeCode typeCode, Func<ResourceElementSetNode, NodeAndResourceElement[], IUndoCommand> createCommand) {
|
|
var rsrcSetNode = nodes[0] as ResourceElementSetNode;
|
|
if (rsrcSetNode is null)
|
|
rsrcSetNode = nodes[0].TreeNode.Parent!.Data as ResourceElementSetNode;
|
|
Debug2.Assert(rsrcSetNode is not null);
|
|
|
|
var module = rsrcSetNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var dlg = new WF.OpenFileDialog {
|
|
RestoreDirectory = true,
|
|
Multiselect = true,
|
|
Filter = PickFilenameConstants.AnyFilenameFilter,
|
|
};
|
|
if (dlg.ShowDialog() != WF.DialogResult.OK)
|
|
return;
|
|
var fnames = dlg.FileNames;
|
|
if (fnames.Length == 0)
|
|
return;
|
|
|
|
var newNodes = new NodeAndResourceElement[fnames.Length];
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceElementTreeNodeGroup);
|
|
for (int i = 0; i < fnames.Length; i++) {
|
|
var fn = fnames[i];
|
|
try {
|
|
var rsrcElem = new ResourceElement {
|
|
Name = Path.GetFileName(fn),
|
|
ResourceData = new BuiltInResourceData(typeCode, File.ReadAllBytes(fn)),
|
|
};
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, rsrcElem, treeNodeGroup)).Data;
|
|
newNodes[i] = new NodeAndResourceElement(node);
|
|
}
|
|
catch (Exception ex) {
|
|
MsgBox.Instance.Show(string.Format(dnSpy_AsmEditor_Resources.Error_ReadingFiles, ex.Message));
|
|
return;
|
|
}
|
|
}
|
|
|
|
undoCommandService.Value.Add(createCommand(rsrcSetNode, newNodes.ToArray()));
|
|
appService.DocumentTabService.FollowReference(newNodes[0].Node);
|
|
}
|
|
|
|
CreateByteArrayResourceElementCommand(ResourceElementSetNode rsrcSetNode, NodeAndResourceElement[] nodes)
|
|
: base(rsrcSetNode, nodes) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateByteArrayResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateStreamResourceElementCommand : CreateResourceElementCommandBase {
|
|
[ExportMenuItem(Header = "res:CreateStreamResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 170)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateStreamResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateStreamResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateStreamResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 180)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateStreamResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateStreamResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateStreamResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 180)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateStreamResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateStreamResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is ResourceElementSetNode || nodes[0].TreeNode.Parent?.Data is ResourceElementSetNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
CreateByteArrayResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, nodes, ResourceTypeCode.Stream, (a, b) => new CreateStreamResourceElementCommand(a, b));
|
|
}
|
|
|
|
CreateStreamResourceElementCommand(ResourceElementSetNode rsrcSetNode, NodeAndResourceElement[] nodes)
|
|
: base(rsrcSetNode, nodes) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateStreamResourceCommand2;
|
|
}
|
|
|
|
sealed class CreateResourceElementCommand : CreateResourceElementCommandBase {
|
|
[ExportMenuItem(Header = "res:CreateResourceCommand", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_NEW, Order = 190)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, IDocumentTreeViewSettings documentTreeViewSettings) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:CreateResourceCommand", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_NEW, Order = 190)]
|
|
sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, IDocumentTreeViewSettings documentTreeViewSettings)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => CreateResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => CreateResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
[ExportMenuItem(Header = "res:CreateResourceCommand", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_NEW, Order = 190)]
|
|
sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IResourceNodeFactory resourceNodeFactory;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, IDocumentTreeViewSettings documentTreeViewSettings)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.resourceNodeFactory = resourceNodeFactory;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => context.IsDefinition && CreateResourceElementCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => CreateResourceElementCommand.Execute(undoCommandService, appService, resourceNodeFactory, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is ResourceElementSetNode || nodes[0].TreeNode.Parent?.Data is ResourceElementSetNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IResourceNodeFactory resourceNodeFactory, IDocumentTreeViewSettings documentTreeViewSettings, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcSetNode = nodes[0] as ResourceElementSetNode;
|
|
if (rsrcSetNode is null)
|
|
rsrcSetNode = nodes[0].TreeNode.Parent!.Data as ResourceElementSetNode;
|
|
Debug2.Assert(rsrcSetNode is not null);
|
|
|
|
var module = rsrcSetNode.GetModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var options = new ResourceElementOptions(new ResourceElement {
|
|
Name = string.Empty,
|
|
ResourceData = new BuiltInResourceData(ResourceTypeCode.String, string.Empty),
|
|
});
|
|
var data = new ResourceElementVM(options, module, documentTreeViewSettings.DeserializeResources);
|
|
var win = new ResourceElementDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.CreateResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var opts = data.CreateResourceElementOptions();
|
|
var treeView = appService.DocumentTreeView.TreeView;
|
|
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceElementTreeNodeGroup);
|
|
var node = (DocumentTreeNodeData)treeView.Create(resourceNodeFactory.Create(module, opts.Create(), treeNodeGroup)).Data;
|
|
undoCommandService.Value.Add(new CreateResourceElementCommand(rsrcSetNode, node));
|
|
appService.DocumentTabService.FollowReference(node);
|
|
}
|
|
|
|
CreateResourceElementCommand(ResourceElementSetNode rsrcSetNode, DocumentTreeNodeData node)
|
|
: base(rsrcSetNode, new[] { new NodeAndResourceElement(node) }) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.CreateResourceCommand2;
|
|
}
|
|
|
|
[DebuggerDisplay("{Description}")]
|
|
abstract class ResourceElementSettingsBaseCommand : IUndoCommand {
|
|
readonly ModuleDef module;
|
|
readonly Resource resource;
|
|
readonly int resourceIndex;
|
|
Resource? newResource;
|
|
readonly ResourceElementSetNode rsrcSetNode;
|
|
readonly ResourceElementNode rsrcElNode;
|
|
readonly ResourceElement newOptions;
|
|
readonly ResourceElement origOptions;
|
|
readonly int origParentChildIndex;
|
|
readonly bool nameChanged;
|
|
|
|
protected ResourceElementSettingsBaseCommand(ResourceElementNode rsrcElNode, ResourceElementOptions options) {
|
|
rsrcSetNode = (ResourceElementSetNode)rsrcElNode.TreeNode.Parent!.Data;
|
|
this.rsrcElNode = rsrcElNode;
|
|
newOptions = options.Create();
|
|
origOptions = rsrcElNode.ResourceElement;
|
|
|
|
module = rsrcSetNode.GetModule()!;
|
|
Debug2.Assert(module is not null);
|
|
resource = rsrcSetNode.Resource;
|
|
resourceIndex = module.Resources.IndexOf(resource);
|
|
Debug.Assert(resourceIndex >= 0);
|
|
if (resourceIndex < 0)
|
|
throw new InvalidOperationException();
|
|
|
|
origParentChildIndex = rsrcSetNode.TreeNode.Children.IndexOf(rsrcElNode.TreeNode);
|
|
Debug.Assert(origParentChildIndex >= 0);
|
|
if (origParentChildIndex < 0)
|
|
throw new InvalidOperationException();
|
|
|
|
nameChanged = origOptions.Name != newOptions.Name;
|
|
}
|
|
|
|
public abstract string Description { get; }
|
|
|
|
public void Execute() {
|
|
Debug.Assert(resource == rsrcSetNode.Resource);
|
|
Debug.Assert(module.Resources[resourceIndex] == resource);
|
|
if (nameChanged) {
|
|
bool b = origParentChildIndex < rsrcSetNode.TreeNode.Children.Count && rsrcSetNode.TreeNode.Children[origParentChildIndex] == rsrcElNode.TreeNode;
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
rsrcSetNode.TreeNode.Children.RemoveAt(origParentChildIndex);
|
|
rsrcElNode.UpdateData(newOptions);
|
|
|
|
rsrcSetNode.TreeNode.AddChild(rsrcElNode.TreeNode);
|
|
}
|
|
else
|
|
rsrcElNode.UpdateData(newOptions);
|
|
|
|
if (newResource is null) {
|
|
rsrcSetNode.RegenerateEmbeddedResource();
|
|
newResource = rsrcSetNode.Resource;
|
|
}
|
|
else
|
|
rsrcSetNode.Resource = newResource;
|
|
module.Resources[resourceIndex] = newResource;
|
|
rsrcElNode.TreeNode.RefreshUI();
|
|
}
|
|
|
|
public void Undo() {
|
|
if (nameChanged) {
|
|
bool b = rsrcSetNode.TreeNode.Children.Remove(rsrcElNode.TreeNode);
|
|
Debug.Assert(b);
|
|
if (!b)
|
|
throw new InvalidOperationException();
|
|
|
|
rsrcElNode.UpdateData(origOptions);
|
|
rsrcSetNode.TreeNode.Children.Insert(origParentChildIndex, rsrcElNode.TreeNode);
|
|
}
|
|
else
|
|
rsrcElNode.UpdateData(origOptions);
|
|
|
|
rsrcSetNode.Resource = resource;
|
|
module.Resources[resourceIndex] = resource;
|
|
rsrcElNode.TreeNode.RefreshUI();
|
|
}
|
|
|
|
public IEnumerable<object> ModifiedObjects {
|
|
get { yield return rsrcElNode; }
|
|
}
|
|
}
|
|
|
|
sealed class ResourceElementSettingsCommand : ResourceElementSettingsBaseCommand {
|
|
[ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SETTINGS, Order = 90)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IDocumentTreeViewSettings documentTreeViewSettings) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ResourceElementSettingsCommand.Execute(undoCommandService, appService, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_SETTINGS, Order = 100)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IDocumentTreeViewSettings documentTreeViewSettings)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ResourceElementSettingsCommand.Execute(undoCommandService, appService, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SETTINGS, Order = 100)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
readonly IDocumentTreeViewSettings documentTreeViewSettings;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IDocumentTreeViewSettings documentTreeViewSettings)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
this.documentTreeViewSettings = documentTreeViewSettings;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => ResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => ResourceElementSettingsCommand.Execute(undoCommandService, appService, documentTreeViewSettings, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 &&
|
|
(nodes[0] is BuiltInResourceElementNode ||
|
|
nodes[0] is UnknownSerializedResourceElementNode);
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, IDocumentTreeViewSettings documentTreeViewSettings, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var rsrcElNode = (ResourceElementNode)nodes[0];
|
|
var module = nodes[0].GetParentModule();
|
|
Debug2.Assert(module is not null);
|
|
if (module is null)
|
|
throw new InvalidOperationException();
|
|
|
|
var options = new ResourceElementOptions(rsrcElNode.ResourceElement);
|
|
var data = new ResourceElementVM(options, module, documentTreeViewSettings.DeserializeResources);
|
|
data.CanChangeType = false;
|
|
var win = new ResourceElementDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var opts = data.CreateResourceElementOptions();
|
|
string? error;
|
|
try {
|
|
error = rsrcElNode.CheckCanUpdateData(opts.Create());
|
|
}
|
|
catch (Exception ex) {
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_InvalidResourceData, ex.Message);
|
|
}
|
|
if (!string2.IsNullOrEmpty(error)) {
|
|
MsgBox.Instance.Show(error);
|
|
return;
|
|
}
|
|
|
|
undoCommandService.Value.Add(new ResourceElementSettingsCommand(rsrcElNode, opts));
|
|
}
|
|
|
|
ResourceElementSettingsCommand(ResourceElementNode rsrcElNode, ResourceElementOptions options)
|
|
: base(rsrcElNode, options) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
}
|
|
|
|
sealed class ImageResourceElementSettingsCommand : ResourceElementSettingsBaseCommand {
|
|
[ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SETTINGS, Order = 100)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_SETTINGS, Order = 110)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => ImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => ImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SETTINGS, Order = 110)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => ImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => ImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 && nodes[0] is ImageResourceElementNode;
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var imgRsrcElNode = (ImageResourceElementNode)nodes[0];
|
|
|
|
var options = new ResourceElementOptions(imgRsrcElNode.ResourceElement);
|
|
var data = new ImageResourceElementVM(options);
|
|
var win = new ImageResourceElementDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var opts = data.CreateResourceElementOptions();
|
|
string? error;
|
|
try {
|
|
error = imgRsrcElNode.CheckCanUpdateData(opts.Create());
|
|
}
|
|
catch (Exception ex) {
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_NewResourceDataMustBeImage, ex.Message);
|
|
}
|
|
if (!string2.IsNullOrEmpty(error)) {
|
|
MsgBox.Instance.Show(error);
|
|
return;
|
|
}
|
|
|
|
undoCommandService.Value.Add(new ImageResourceElementSettingsCommand(imgRsrcElNode, opts));
|
|
}
|
|
|
|
ImageResourceElementSettingsCommand(ResourceElementNode rsrcElNode, ResourceElementOptions options)
|
|
: base(rsrcElNode, options) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
}
|
|
|
|
sealed class SerializedImageResourceElementSettingsCommand : ResourceElementSettingsBaseCommand {
|
|
[ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SETTINGS, Order = 110)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => SerializedImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => SerializedImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_SETTINGS, Order = 120)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => SerializedImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => SerializedImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SETTINGS, Order = 120)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => SerializedImageResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => SerializedImageResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) => nodes.Length == 1 && nodes[0] is SerializedImageResourceElementNode;
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var imgRsrcElNode = (SerializedImageResourceElementNode)nodes[0];
|
|
var options = new ResourceElementOptions(imgRsrcElNode.GetAsRawImage());
|
|
var data = new ImageResourceElementVM(options);
|
|
var win = new ImageResourceElementDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var opts = data.CreateResourceElementOptions();
|
|
string? error;
|
|
try {
|
|
opts = new ResourceElementOptions(SerializedImageUtilities.Serialize(opts.Create()));
|
|
error = imgRsrcElNode.CheckCanUpdateData(opts.Create());
|
|
}
|
|
catch (Exception ex) {
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_NewResourceDataMustBeImage, ex.Message);
|
|
}
|
|
if (!string2.IsNullOrEmpty(error)) {
|
|
MsgBox.Instance.Show(error);
|
|
return;
|
|
}
|
|
|
|
undoCommandService.Value.Add(new SerializedImageResourceElementSettingsCommand(imgRsrcElNode, opts));
|
|
}
|
|
|
|
SerializedImageResourceElementSettingsCommand(ResourceElementNode rsrcElNode, ResourceElementOptions options)
|
|
: base(rsrcElNode, options) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
}
|
|
|
|
sealed class SerializedImageListStreamerResourceElementSettingsCommand : ResourceElementSettingsBaseCommand {
|
|
[ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCUMENTS_ASMED_SETTINGS, Order = 120)]
|
|
sealed class DocumentsCommand : DocumentsContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
DocumentsCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => SerializedImageListStreamerResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => SerializedImageListStreamerResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(OwnerGuid = MenuConstants.APP_MENU_EDIT_GUID, Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_APP_MENU_EDIT_ASMED_SETTINGS, Order = 130)]
|
|
internal sealed class EditMenuCommand : EditMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
EditMenuCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsVisible(AsmEditorContext context) => SerializedImageListStreamerResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(AsmEditorContext context) => SerializedImageListStreamerResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
[Export, ExportMenuItem(Header = "res:EditResourceCommand", Icon = DsImagesAttribute.Settings, InputGestureText = "res:ShortcutKeyAltEnter", Group = MenuConstants.GROUP_CTX_DOCVIEWER_ASMED_SETTINGS, Order = 130)]
|
|
internal sealed class CodeCommand : CodeContextMenuHandler {
|
|
readonly Lazy<IUndoCommandService> undoCommandService;
|
|
readonly IAppService appService;
|
|
|
|
[ImportingConstructor]
|
|
CodeCommand(Lazy<IUndoCommandService> undoCommandService, IAppService appService)
|
|
: base(appService.DocumentTreeView) {
|
|
this.undoCommandService = undoCommandService;
|
|
this.appService = appService;
|
|
}
|
|
|
|
public override bool IsEnabled(CodeContext context) => SerializedImageListStreamerResourceElementSettingsCommand.CanExecute(context.Nodes);
|
|
public override void Execute(CodeContext context) => SerializedImageListStreamerResourceElementSettingsCommand.Execute(undoCommandService, appService, context.Nodes);
|
|
}
|
|
|
|
static bool CanExecute(DocumentTreeNodeData[] nodes) =>
|
|
nodes.Length == 1 && nodes[0] is SerializedImageListStreamerResourceElementNode;
|
|
|
|
static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService appService, DocumentTreeNodeData[] nodes) {
|
|
if (!CanExecute(nodes))
|
|
return;
|
|
|
|
var imgNode = (SerializedImageListStreamerResourceElementNode)nodes[0];
|
|
var options = new ImageListOptions(imgNode.ImageListOptions);
|
|
var data = new ImageListVM(options);
|
|
var win = new ImageListDlg();
|
|
win.Title = dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
win.DataContext = data;
|
|
win.Owner = appService.MainWindow;
|
|
if (win.ShowDialog() != true)
|
|
return;
|
|
|
|
var listOpts = data.CreateImageListOptions();
|
|
|
|
if (listOpts.ImageSources.Count == 0) {
|
|
MsgBox.Instance.Show(dnSpy_AsmEditor_Resources.Error_EmptyImageList);
|
|
return;
|
|
}
|
|
|
|
ResourceElementOptions? opts = null;
|
|
string? error;
|
|
try {
|
|
opts = new ResourceElementOptions(SerializedImageListStreamerUtilities.Serialize(listOpts));
|
|
error = imgNode.CheckCanUpdateData(opts.Create());
|
|
}
|
|
catch (Exception ex) {
|
|
error = string.Format(dnSpy_AsmEditor_Resources.Error_CouldNotSerializeImages, ex.Message);
|
|
}
|
|
if (!string2.IsNullOrEmpty(error)) {
|
|
MsgBox.Instance.Show(error);
|
|
return;
|
|
}
|
|
|
|
undoCommandService.Value.Add(new SerializedImageListStreamerResourceElementSettingsCommand(imgNode, opts!));
|
|
}
|
|
|
|
SerializedImageListStreamerResourceElementSettingsCommand(ResourceElementNode rsrcElNode, ResourceElementOptions options)
|
|
: base(rsrcElNode, options) {
|
|
}
|
|
|
|
public override string Description => dnSpy_AsmEditor_Resources.EditResourceCommand2;
|
|
}
|
|
}
|