Skip to content

Commit

Permalink
Generalize attachments to allow any file type.
Browse files Browse the repository at this point in the history
Fixes issue #115
  • Loading branch information
clovett committed Jul 7, 2024
1 parent a010c98 commit 35ec196
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 31 deletions.
18 changes: 17 additions & 1 deletion Source/WPF/MyMoney/Dialogs/AttachmentDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ui:ThemeManager.IsThemeAware="True"
Title="Attachments" Height="425" Width="800"
xmlns:help="clr-namespace:Walkabout.Help"
AllowDrop="True"
help:HelpService.HelpKeyword="Basics/Attachments/">

<Window.Resources>
Expand All @@ -17,7 +18,22 @@
<Setter Property="IsCompact" Value="True"/>
<Setter Property="LabelPosition" Value="Collapsed"/>
</Style>
</Window.Resources>

<!-- Drawing: Assembly symbol -->
<DrawingGroup x:Key="FileDrawing" PresentationOptions:Freeze="true">
<DrawingGroup.Children>
<!-- Layer 1/<Path> -->
<GeometryDrawing Brush="White" Geometry="F1 M 15.000,0.000 L 4.000,0.000 L 4.000,2.000 L 2.000,2.000 L 2.000,4.000 L 0.000,4.000 L 0.000,15.000 L 11.000,15.000 L 11.000,12.000 L 13.000,12.000 L 13.000,10.000 L 15.000,10.000 L 15.000,0.000 Z"/>
<!-- Layer 1/<Compound Path> -->
<GeometryDrawing Brush="#ff414141" Geometry="F1 M 8.000,7.000 L 3.000,7.000 L 3.000,12.000 L 8.000,12.000 L 8.000,7.000 Z M 10.000,14.000 L 1.000,14.000 L 1.000,5.000 L 10.000,5.000 L 10.000,14.000 Z M 3.000,3.000 L 3.000,4.000 L 11.000,4.000 L 11.000,11.000 L 12.000,11.000 L 12.000,3.000 L 3.000,3.000 Z M 14.000,1.000 L 14.000,9.000 L 13.000,9.000 L 13.000,2.000 L 5.000,2.000 L 5.000,1.000 L 14.000,1.000 Z"/>
<!-- Layer 1/<Path> -->
<GeometryDrawing Brush="White" Geometry="F1 M 3.000,12.000 L 8.000,12.000 L 8.000,7.000 L 3.000,7.000 L 3.000,12.000 Z"/>
</DrawingGroup.Children>
</DrawingGroup>
<!-- Image: Assembly symbol -->
<DrawingImage x:Key="DefaultFileImage" Drawing="{StaticResource FileDrawing}" PresentationOptions:Freeze="true" />

</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="Cut" Executed="Cut" CanExecute="HasSelectedItem"/>
<CommandBinding Command="Copy" Executed="Copy" CanExecute="HasSelectedItem"/>
Expand Down
156 changes: 154 additions & 2 deletions Source/WPF/MyMoney/Dialogs/AttachmentDialog.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System;
using ModernWpf.Controls;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using Walkabout.Attachments;
using Walkabout.Configuration;
using Walkabout.Controls;
Expand Down Expand Up @@ -55,6 +59,59 @@ public AttachmentDialog()
this.resizerBrush = (Brush)this.Resources["ResizerThumbBrush"];
}

protected override void OnDragEnter(DragEventArgs e)
{
if (this.transaction == null)
{
return;
}
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
}

protected override void OnDrop(DragEventArgs e)
{
if (this.transaction == null)
{
return;
}
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (var file in files)
{
var extension = Path.GetExtension(file);
StringBuilder sb = new StringBuilder();
try
{
string attachmentFullPath = this.Manager.GetUniqueFileName(transaction, extension);
File.Copy(files[0], attachmentFullPath, true);
transaction.HasAttachment = true;
}
catch (Exception ex)
{
sb.AppendLine(ex.Message);
}
if (sb.Length > 0)
{
MessageBoxEx.Show(sb.ToString(), "Add Attachments Failed", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
this.LoadAttachments();
}
}

private void OnDoubleClickItem(AttachmentDialogItem item)
{
if (!string.IsNullOrEmpty(item.FileName))
{
InternetExplorer.OpenUrl(0, item.FileName);
}
}

protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Escape)
Expand All @@ -69,13 +126,17 @@ private void CanvasGrid_MouseDown(object sender, MouseButtonEventArgs e)
Point pos = e.GetPosition(this.CanvasGrid);
HitTestResult result = VisualTreeHelper.HitTest(this.CanvasGrid, pos);

DependencyObject hit = result.VisualHit;
DependencyObject hit = result.VisualHit;
if (hit != null)
{
AttachmentDialogItem item = WpfHelper.FindAncestor<AttachmentDialogItem>(hit);
if (item != null)
{
this.SelectItem(item);
if (e.ClickCount > 1)
{
this.OnDoubleClickItem(item);
}
return;
}
if (hit == this.resizer)
Expand Down Expand Up @@ -140,6 +201,10 @@ private void LoadAttachments()
// load an xamlpackage document.
this.AddItem(new AttachmentDialogDocumentItem(filePath));
}
else
{
this.AddItem(new AttachmentDialogFileItem(filePath));
}
}
catch
{
Expand Down Expand Up @@ -369,6 +434,7 @@ private void MoveResizer(AttachmentDialogItem item, Rect resizerBounds)
{
this.resizer.LimitBounds = this.GetScaledBounds(item.ResizeLimit);
this.resizer.Bounds = this.GetScaledBounds(resizerBounds);
this.resizer.IsEnabled = item.LiveResizable;
this.resizer.InvalidateArrange();
}
}
Expand Down Expand Up @@ -1078,6 +1144,92 @@ internal void RotateImage(double degrees)

}

internal class AttachmentDialogFileItem : AttachmentDialogItem
{
private ImageSource imageSource;

Check warning on line 1149 in Source/WPF/MyMoney/Dialogs/AttachmentDialog.xaml.cs

View workflow job for this annotation

GitHub Actions / build (Debug, Any CPU)

Field 'AttachmentDialogFileItem.imageSource' is never assigned to, and will always have its default value null

Check warning on line 1149 in Source/WPF/MyMoney/Dialogs/AttachmentDialog.xaml.cs

View workflow job for this annotation

GitHub Actions / build (Release, Any CPU)

Field 'AttachmentDialogFileItem.imageSource' is never assigned to, and will always have its default value null
private Image image;

public AttachmentDialogFileItem(string fileName)
{
this.image = new Image();
this.FileName = fileName;
image.Source = FileIcons.Extract(fileName); ;
this.AddVisualChild(this.image);
this.Loaded += this.OnItemLoaded;
}

private void OnItemLoaded(object sender, RoutedEventArgs e)
{
if (image.Source == null)
{
var drawing = (DrawingImage)this.FindResource("DefaultFileImage");
image.Source = drawing;
}
if (image.Source != null)
{
image.Width = this.image.Source.Width;
image.Height = this.image.Source.Height;
}
}

public override FrameworkElement Content => this.image;

public override string FileExtension => System.IO.Path.GetExtension(this.FileName);

public override bool LiveResizable => false;

public override Rect ResizeLimit
{
get { return new Rect(0, 0, this.image.Source.Width, this.image.Source.Height); }
}

public override FrameworkElement CloneContent()
{
return new Image() { Source = imageSource };
}

public override void Copy()
{
}

public override void Resize(Rect newBounds)
{
}

public override void Save(string filePath)
{
}

protected override Visual GetVisualChild(int index)
{
if (index == 0)
{
return this.image;
}
return null;
}

protected override int VisualChildrenCount
{
get
{
return 1;
}
}

protected override Size MeasureOverride(Size availableSize)
{
this.image.Measure(availableSize);
return new Size(this.image.Source.Width, this.image.Source.Height);
}

protected override Size ArrangeOverride(Size finalSize)
{
this.image.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
return finalSize;
}
}

/// <summary>
/// This class implements the abstract AttachmentDialogItem by
/// wrapping a RichTextBox as the content
Expand Down
1 change: 1 addition & 0 deletions Source/WPF/MyMoney/Setup/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<changes>
<change version="2.1.0.21" date="6/22/2024">
- Fix issue #112: add hyperlinks on future bills report.
- Fix issue #115: Attach pdf files to register entries.
</change>
<change version="2.1.0.20" date="6/18/2024">
- Fix issue #119: Add "Accept All" Transaction view context menu.
Expand Down
59 changes: 59 additions & 0 deletions Source/WPF/MyMoney/Utilities/FileIcons.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Walkabout.Utilities;

namespace Walkabout.Utilities
{
internal class FileIcons
{
private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0;
private const uint SHGFI_SMALLICON = 0x1;
private const int FILE_ATTRIBUTE_NORMAL = 0x80;
private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
[StructLayout(LayoutKind.Sequential)]
private struct SHFILEINFO
{
public IntPtr hIcon;
public IntPtr iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};

[DllImport("shell32.dll")]
private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);


public static ImageSource Extract(string fileName)
{
var shinfo = new SHFILEINFO();

SHGetFileInfo(fileName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_LARGEICON);
//The icon is returned in the hIcon member of the shinfo struct
var imageSource = Imaging.CreateBitmapSourceFromHIcon(
shinfo.hIcon,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());

DestroyIcon(shinfo.hIcon);
return imageSource;
}
}

}
10 changes: 10 additions & 0 deletions Source/WPF/MyMoney/Utilities/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ public static class NativeMethods
public const int QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER |
QS_PAINT | QS_HOTKEY;


[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}

[DllImport("user32.dll", EntryPoint = "SetParent",
SetLastError = true, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
Expand Down
23 changes: 3 additions & 20 deletions Source/WPF/MyMoney/Views/TransactionsView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ private void OnDataGridRowDragDrop(object sender, DragEventArgs e)
{
var transaction = row.Item as Transaction;
AttachmentManager mgr = this.ServiceProvider.GetService(typeof(AttachmentManager)) as AttachmentManager;
if (transaction != null && transaction.HasAttachment == false && mgr != null)
if (transaction != null && mgr != null)
{
StringBuilder sb = new StringBuilder();
var files = (string[])e.Data.GetData(DataFormats.FileDrop);
Expand Down Expand Up @@ -1243,31 +1243,14 @@ private void OnDataGridRowDragEnter(object sender, DragEventArgs e)
else if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
var transaction = row.Item as Transaction;
if (transaction != null && transaction.HasAttachment == false)
if (transaction != null)
{
var files = (string[])e.Data.GetData(DataFormats.FileDrop);

if (files.Length == 1 && this.IsValidAttachmentExtension(files[0]))
{
this.SetDragDropStyles(row, DropType.File);
e.Effects = DragDropEffects.Copy;
}
e.Effects = DragDropEffects.Copy;
}
}
}
}

private bool IsValidAttachmentExtension(string filePath)
{
var extension = Path.GetExtension(filePath).ToLower();
if (extension == ".jpg" || extension == ".png" || extension == ".gif" || extension == ".bmp")
{
return true;
}

return false;
}

private void OnDataGridRowDragLeave(object sender, DragEventArgs e)
{
DataGridRow row = (DataGridRow)sender;
Expand Down
2 changes: 1 addition & 1 deletion Source/WPF/Version/VersionMaster.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.1.0.21
2.1.0.22
Loading

0 comments on commit 35ec196

Please sign in to comment.