1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
5
using System.Collections.Generic;
6
using System.ComponentModel;
8
using System.Drawing.Design;
11
using System.Resources;
12
using System.Resources.Tools;
13
using System.Windows.Forms;
15
using ICSharpCode.Core;
16
using ICSharpCode.Core.WinForms;
17
using ICSharpCode.FormsDesigner.Services;
18
using ICSharpCode.SharpDevelop;
19
using ICSharpCode.SharpDevelop.Dom;
20
using ICSharpCode.SharpDevelop.Gui;
21
using ICSharpCode.SharpDevelop.Project;
23
namespace ICSharpCode.FormsDesigner.Gui
26
/// Allows the user to select a resource for an image or icon property.
28
public sealed partial class ImageResourceEditorDialog : Form
30
readonly IProject project;
31
readonly Type requiredResourceType;
33
bool selectedImageIsProjectResource;
38
ImageResourceEditorDialog(IProject project, Type requiredResourceType, bool designerSupportsProjectResources)
41
if (requiredResourceType == null)
42
throw new ArgumentNullException("requiredResourceType");
43
this.requiredResourceType = requiredResourceType;
44
this.project = project;
47
// The InitializeComponent() call is required for Windows Forms designer support.
49
InitializeComponent();
52
this.projectResourcesTreeView.Nodes.Add(ResourceService.GetString("Global.PleaseWait"));
54
this.importLocalResourceButton.DataBindings.Add("Enabled", this.localResourceRadioButton, "Checked");
55
this.projectResourcesTreeView.DataBindings.Add("Enabled", this.projectResourceRadioButton, "Checked");
57
this.projectResourceRadioButton.Visible = designerSupportsProjectResources;
58
this.projectResourcesTreeView.Visible = designerSupportsProjectResources;
61
public ImageResourceEditorDialog(IProject project, Type requiredResourceType, ProjectResourceInfo projectResource)
62
: this(project, requiredResourceType, true)
64
if (projectResource == null)
65
throw new ArgumentNullException("projectResource");
67
this.projectResourceRadioButton.Checked = true;
68
this.originalImage = this.selectedImage = projectResource.OriginalValue;
70
Image image = this.selectedImage as Image;
72
this.selectedImageIsProjectResource = true;
73
this.SetPreviewImage(image);
75
Icon icon = this.selectedImage as Icon;
77
this.selectedImageIsProjectResource = true;
78
this.SetPreviewImage(icon.ToBitmap());
81
this.projectTreeScanningBackgroundWorker.RunWorkerAsync(projectResource);
84
public ImageResourceEditorDialog(IProject project, Image localResource, bool designerSupportsProjectResources)
85
: this(project, typeof(Image), designerSupportsProjectResources)
87
if (localResource != null) {
88
this.localResourceRadioButton.Checked = true;
89
this.originalImage = this.selectedImage = localResource;
90
this.SetPreviewImage(localResource);
92
this.noResourceRadioButton.Checked = true;
94
this.projectTreeScanningBackgroundWorker.RunWorkerAsync();
97
public ImageResourceEditorDialog(IProject project, Icon localResource, bool designerSupportsProjectResources)
98
: this(project, typeof(Icon), designerSupportsProjectResources)
100
if (localResource != null) {
101
this.localResourceRadioButton.Checked = true;
102
this.originalImage = this.selectedImage = localResource;
103
this.SetPreviewImage(localResource.ToBitmap());
105
this.noResourceRadioButton.Checked = true;
107
this.projectTreeScanningBackgroundWorker.RunWorkerAsync();
110
static void Translate(Control c)
112
c.Text = StringParser.Parse(c.Text);
113
foreach (Control child in c.Controls) {
123
/// Gets the <see cref="ProjectResourceInfo"/> for the selected project resource,
124
/// or <c>null</c> if the selected resource is not a project resource.
126
public ProjectResourceInfo SelectedProjectResource {
128
if (this.selectedImageIsProjectResource && this.projectResourceRadioButton.Checked) {
130
TreeNode node = this.projectResourcesTreeView.SelectedNode;
131
if (node == null) return null;
133
return new ProjectResourceInfo(((FileProjectItem)node.Parent.Tag).FileName, node.Text);
142
/// Gets the selected image.
143
/// This can be an Image or an Icon (matching the type that was passed to the constructor) or null.
145
public object SelectedResourceValue {
147
return this.selectedImage;
153
void SetPreviewImage(Image image)
155
this.previewPictureBox.Image = image;
157
if (image.Width > this.previewPictureBox.ClientSize.Width ||
158
image.Height > this.previewPictureBox.ClientSize.Height) {
159
this.previewPictureBox.SizeMode = PictureBoxSizeMode.Zoom;
161
this.previewPictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
166
void DisposeImageIfNotOriginal(object image)
168
if (!Object.ReferenceEquals(image, this.originalImage)) {
169
IDisposable d = image as IDisposable;
176
void SetSelectedImage(object image, bool isProjectResource)
178
if (!Object.ReferenceEquals(this.selectedImage, this.previewPictureBox.Image)) {
179
Image temp = this.previewPictureBox.Image;
180
this.previewPictureBox.Image = null;
181
this.DisposeImageIfNotOriginal(temp);
183
this.previewPictureBox.Image = null;
186
if (!this.selectedImageIsProjectResource) {
187
this.DisposeImageIfNotOriginal(this.selectedImage);
190
Image img = image as Image;
192
this.selectedImage = img;
193
this.selectedImageIsProjectResource = isProjectResource;
194
this.SetPreviewImage(img);
196
Icon icon = image as Icon;
198
this.selectedImage = icon;
199
this.selectedImageIsProjectResource = isProjectResource;
200
this.SetPreviewImage(icon.ToBitmap());
202
this.selectedImageIsProjectResource = false;
203
this.selectedImage = null;
208
#region Project tree filling
210
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
211
void ProjectTreeScanningBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
213
if (this.project == null) {
217
ProjectResourceInfo selectedProjectResource = e.Argument as ProjectResourceInfo;
219
IProjectContent projectContent = ParserService.GetProjectContent(this.project);
221
TreeNode root = new TreeNode(this.project.Name, 0, 0);
222
TreeNode preSelection = null;
223
TreeNode lastFileNode = null;
224
int fileNodesCount = 0;
226
foreach (FileProjectItem item in this.project.GetItemsOfType(ItemType.EmbeddedResource).OfType<FileProjectItem>().OrderBy(fpi => Path.GetFileName(fpi.VirtualName))) {
228
if (this.projectTreeScanningBackgroundWorker.CancellationPending) {
233
// Skip files where the generated class name
234
// would conflict with an existing class.
235
string namespaceName = item.GetEvaluatedMetadata("CustomToolNamespace");
236
if (string.IsNullOrEmpty(namespaceName)) {
237
namespaceName = CustomToolsService.GetDefaultNamespace(item.Project, item.FileName);
239
IClass existingClass = projectContent.GetClass(namespaceName + "." + StronglyTypedResourceBuilder.VerifyResourceName(Path.GetFileNameWithoutExtension(item.FileName), projectContent.Language.CodeDomProvider), 0);
240
if (existingClass != null) {
241
if (!ProjectResourceService.IsGeneratedResourceClass(existingClass)) {
246
bool selectedFile = (selectedProjectResource != null) && FileUtility.IsEqualFileName(selectedProjectResource.ResourceFile, item.FileName);
247
TreeNode file = null;
251
foreach (KeyValuePair<string, object> r in this.GetResources(item.FileName).OrderBy(pair => pair.Key)) {
252
if (this.projectTreeScanningBackgroundWorker.CancellationPending) {
258
file = CreateAndAddFileNode(root, item);
261
TreeNode resNode = new TreeNode(r.Key, 3, 3);
262
resNode.Tag = r.Value;
263
file.Nodes.Add(resNode);
266
if (String.Equals(r.Key, selectedProjectResource.ResourceKey, StringComparison.Ordinal)) {
267
preSelection = resNode;
277
} catch (Exception ex) {
279
file = CreateAndAddFileNode(root, item);
281
TreeNode error = new TreeNode(ex.Message, 4, 4);
282
file.Nodes.Add(error);
287
DisposeNodeImages(root);
289
// Preselect the file node if there is only one
290
if (preSelection == null && fileNodesCount == 1) {
291
preSelection = lastFileNode;
293
e.Result = new TreeScanResult(root, preSelection);
297
sealed class TreeScanResult {
298
readonly TreeNode root;
299
readonly TreeNode preSelection;
301
public TreeNode Root {
305
public TreeNode PreSelection {
306
get { return preSelection; }
309
public TreeScanResult(TreeNode root, TreeNode preSelection)
312
this.preSelection = preSelection;
316
static TreeNode CreateAndAddFileNode(TreeNode root, FileProjectItem item)
318
string directory = Path.GetDirectoryName(item.VirtualName);
321
if (String.IsNullOrEmpty(directory)) {
324
dir = GetOrCreateDirectoryNode(root, directory);
327
TreeNode file = new TreeNode(Path.GetFileName(item.VirtualName), 2, 2);
333
static TreeNode GetOrCreateDirectoryNode(TreeNode root, string directory)
335
int index = directory.IndexOfAny(new [] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar});
339
searchDir = directory;
341
searchDir = directory.Substring(0, index);
344
TreeNode node = null;
345
foreach (TreeNode n in root.Nodes) {
346
if (n.Text == searchDir) {
353
node = new TreeNode(searchDir, 1, 1);
355
for (insertIndex = 0; insertIndex < root.Nodes.Count; insertIndex++) {
356
TreeNode n = root.Nodes[insertIndex];
357
if (n.ImageIndex != 1 || StringComparer.CurrentCulture.Compare(searchDir, n.Text) < 0) {
361
root.Nodes.Insert(insertIndex, node);
367
return GetOrCreateDirectoryNode(node, directory.Substring(index + 1));
371
Dictionary<string, object> GetResources(string fileName)
374
WorkbenchSingleton.SafeThreadCall(
376
OpenedFile file = FileService.GetOpenedFile(fileName);
382
s = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
385
using(IResourceReader reader = ResourceStore.CreateResourceReader(s, ResourceStore.GetResourceType(fileName))) {
386
ResXResourceReader resXReader = reader as ResXResourceReader;
387
if (resXReader != null) {
388
resXReader.BasePath = Path.GetDirectoryName(fileName);
391
var resources = new Dictionary<string, object>();
392
foreach (System.Collections.DictionaryEntry entry in reader) {
393
if (entry.Value == null) continue;
394
if (this.requiredResourceType.IsAssignableFrom(entry.Value.GetType())) {
395
resources.Add((string)entry.Key, entry.Value);
403
void ProjectTreeScanningBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
405
if (this.IsDisposed || this.projectResourcesTreeView.IsDisposed) {
406
// This can happen when the dialog is closed before
407
// the scan has finished
408
if (!e.Cancelled && e.Error == null) {
409
TreeScanResult r = e.Result as TreeScanResult;
411
DisposeNodeImages(r.Root);
417
this.projectResourcesTreeView.Nodes.Clear();
423
if (e.Error != null) {
424
MessageService.ShowException(e.Error, "Error in project tree scanning thread");
427
TreeScanResult result = e.Result as TreeScanResult;
428
if (result == null) {
432
this.projectResourcesTreeView.BeginUpdate();
434
ImageList imageList = new ImageList();
435
imageList.ColorDepth = ColorDepth.Depth32Bit;
436
imageList.Images.Add(IconService.GetBitmap(IconService.GetImageForProjectType(this.project.Language)));
437
imageList.Images.Add(WinFormsResourceService.GetBitmap("ProjectBrowser.Folder.Closed"));
438
imageList.Images.Add(IconService.GetBitmap(IconService.GetImageForFile("a.resx")));
439
imageList.Images.Add(WinFormsResourceService.GetBitmap("Icons.16x16.Field"));
440
imageList.Images.Add(WinFormsResourceService.GetBitmap("Icons.16x16.Error"));
441
this.projectResourcesTreeView.ImageList = imageList;
443
this.projectResourcesTreeView.Nodes.Add(result.Root);
445
if (result.PreSelection != null) {
446
result.PreSelection.EnsureVisible();
447
this.projectResourcesTreeView.SelectedNode = result.PreSelection;
448
result.PreSelection.Expand();
450
result.Root.Expand();
453
this.projectResourcesTreeView.EndUpdate();
455
if (result.PreSelection != null) {
456
this.projectResourcesTreeView.Focus();
462
void NoResourceRadioButtonCheckedChanged(object sender, EventArgs e)
464
if (this.noResourceRadioButton.Checked) {
465
this.SetSelectedImage(null, false);
466
this.okButton.Enabled = true;
470
void LocalResourceRadioButtonCheckedChanged(object sender, EventArgs e)
472
if (this.localResourceRadioButton.Checked) {
473
this.okButton.Enabled = true;
477
void ProjectResourceRadioButtonCheckedChanged(object sender, EventArgs e)
479
if (this.projectResourceRadioButton.Checked) {
480
this.UpdateOnProjectResourceSelection();
481
this.projectResourcesTreeView.Focus();
485
void ProjectResourcesTreeViewAfterSelect(object sender, TreeViewEventArgs e)
487
if (this.projectResourceRadioButton.Checked) {
488
this.UpdateOnProjectResourceSelection();
492
void UpdateOnProjectResourceSelection()
494
TreeNode node = this.projectResourcesTreeView.SelectedNode;
495
if (node != null && node.Tag != null && this.requiredResourceType.IsAssignableFrom(node.Tag.GetType())) {
496
this.SetSelectedImage(node.Tag, true);
497
this.okButton.Enabled = true;
499
this.SetSelectedImage(null, false);
500
this.okButton.Enabled = false;
504
void ImageResourceEditorDialogFormClosed(object sender, FormClosedEventArgs e)
506
this.projectTreeScanningBackgroundWorker.CancelAsync();
507
if (this.projectResourcesTreeView.Nodes.Count > 0) {
508
DisposeNodeImages(this.projectResourcesTreeView.Nodes[0]);
512
static void DisposeNodeImages(TreeNode root)
514
if (root.Nodes.Count == 0) {
515
IDisposable d = root.Tag as IDisposable;
521
foreach (TreeNode node in root.Nodes) {
522
DisposeNodeImages(node);
527
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
528
void ImportLocalResourceButtonClick(object sender, EventArgs e)
530
bool isIcon = typeof(Icon).IsAssignableFrom(this.requiredResourceType);
531
using(OpenFileDialog dialog = new OpenFileDialog()) {
532
dialog.Filter = (isIcon ? DummyIconEditor.FileFilterEntry : DummyImageEditor.FileFilterEntry);
533
dialog.RestoreDirectory = true;
534
dialog.Title = StringParser.Parse("${res:ICSharpCode.SharpDevelop.FormDesigner.Gui.ImageResourceEditor.Title}");
535
if (dialog.ShowDialog(this) == DialogResult.OK && !String.IsNullOrEmpty(dialog.FileName)) {
539
this.SetSelectedImage(new Icon(dialog.FileName), false);
541
this.SetSelectedImage(Image.FromFile(dialog.FileName), false);
544
} catch (Exception ex) {
545
MessageService.ShowError(ex.Message);
551
#region Dummy editors for getting the file filter from the framework
553
sealed class DummyImageEditor : ImageEditor
559
internal static string FileFilterEntry {
560
get { return CreateFilterEntry(new ImageEditor()); }
564
sealed class DummyIconEditor : IconEditor
570
internal static string FileFilterEntry {
571
get { return CreateFilterEntry(new IconEditor()); }