-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support shifting dates of multiple photos.
Also fix a crash when saving empty Author names.
- Loading branch information
1 parent
0076c78
commit 212d9a6
Showing
10 changed files
with
304 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
<UserControl x:Class="PhotoTagger.DateTimeRangeEdit" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" | ||
xmlns:ptwpf="clr-namespace:PhotoTagger.Wpf;assembly=PhotoTagger.Wpf" | ||
Name="Editor" | ||
mc:Ignorable="d" > | ||
<UserControl.Resources> | ||
<ptwpf:DateTimeRangeIsRangeToVisibilityConverter | ||
x:Key="DateTimeRangeIsRangeToVisibilityConverter"/> | ||
<ptwpf:DateTimeRangeToSingleDateConverter | ||
x:Key="DateTimeRangeToSingleDateConverter"/> | ||
</UserControl.Resources> | ||
<Grid> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="Auto"/> | ||
<ColumnDefinition Width="Auto"/> | ||
</Grid.ColumnDefinitions> | ||
<xctk:DateTimePicker Grid.Column="0" | ||
Name="minDatePicker" > | ||
<xctk:DateTimePicker.UpdateValueOnEnterKey> | ||
False | ||
</xctk:DateTimePicker.UpdateValueOnEnterKey> | ||
<xctk:DateTimePicker.Format> | ||
Custom | ||
</xctk:DateTimePicker.Format> | ||
<xctk:DateTimePicker.FormatString> | ||
G | ||
</xctk:DateTimePicker.FormatString> | ||
<xctk:DateTimePicker.Value> | ||
<Binding ElementName="Editor" | ||
Path="DateRange" | ||
Converter="{StaticResource | ||
DateTimeRangeToSingleDateConverter}" | ||
Mode="TwoWay" /> | ||
</xctk:DateTimePicker.Value> | ||
<xctk:DateTimePicker.HorizontalContentAlignment> | ||
Left | ||
</xctk:DateTimePicker.HorizontalContentAlignment> | ||
<xctk:DateTimePicker.HorizontalAlignment> | ||
Left | ||
</xctk:DateTimePicker.HorizontalAlignment> | ||
</xctk:DateTimePicker> | ||
<Grid Grid.Column="1"> | ||
<Grid.Visibility> | ||
<Binding ElementName="Editor" | ||
Path="DateRange" | ||
Converter="{StaticResource | ||
DateTimeRangeIsRangeToVisibilityConverter}" | ||
Mode="OneWay" /> | ||
</Grid.Visibility> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="Auto"/> | ||
<ColumnDefinition Width="*"/> | ||
<ColumnDefinition Width="Auto"/> | ||
</Grid.ColumnDefinitions> | ||
<TextBlock Text="to" Grid.Column="0" /> | ||
<TextBlock Grid.Column="1" | ||
HorizontalAlignment="Center" | ||
VerticalAlignment="Center" > | ||
<TextBlock.Text> | ||
<Binding ElementName="Editor" | ||
Path="DateRange" | ||
Converter="{StaticResource | ||
DateTimeRangeToSingleDateConverter}" | ||
ConverterParameter="true" /> | ||
</TextBlock.Text> | ||
</TextBlock> | ||
<Button Grid.Column="2" Click="setAllEqual"> | ||
<Button.Content> | ||
Set All Equal | ||
</Button.Content> | ||
<Button.Margin> | ||
2 | ||
</Button.Margin> | ||
</Button> | ||
</Grid> | ||
</Grid> | ||
</UserControl> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
using PhotoTagger.Imaging; | ||
using PhotoTagger.Wpf; | ||
using System; | ||
using System.Collections.ObjectModel; | ||
using System.Collections.Specialized; | ||
using System.Linq; | ||
using System.Windows; | ||
using System.Windows.Controls; | ||
|
||
namespace PhotoTagger { | ||
/// <summary> | ||
/// Interaction logic for DateTimeRangeEdit.xaml | ||
/// </summary> | ||
public partial class DateTimeRangeEdit : UserControl { | ||
public DateTimeRangeEdit() { | ||
if (PhotoSet is INotifyCollectionChanged oc) { | ||
oc.CollectionChanged += setChanged; | ||
} | ||
InitializeComponent(); | ||
} | ||
|
||
public ReadOnlyObservableCollection<Photo> PhotoSet { | ||
get { | ||
return (ReadOnlyObservableCollection<Photo>)GetValue( | ||
PhotoSetProperty); | ||
} | ||
set { | ||
SetValue(PhotoSetProperty, value); | ||
} | ||
} | ||
|
||
public static readonly DependencyProperty PhotoSetProperty = | ||
DependencyProperty.Register(nameof(PhotoSet), | ||
typeof(ReadOnlyObservableCollection<Photo>), | ||
typeof(DateTimeRangeEdit), | ||
new PropertyMetadata(setChanged)); | ||
|
||
private static void setChanged(DependencyObject d, | ||
DependencyPropertyChangedEventArgs e) { | ||
var photos = e.NewValue as ReadOnlyObservableCollection<Photo>; | ||
DateTimeRangeEdit self = (d as DateTimeRangeEdit); | ||
self.DateRange = DateTimeRange.FromList( | ||
photos.Select(p => p.DateTaken)); | ||
if (e.OldValue is INotifyCollectionChanged oc) { | ||
oc.CollectionChanged -= self.setChanged; | ||
} | ||
if (photos is INotifyCollectionChanged nc) { | ||
nc.CollectionChanged += self.setChanged; | ||
} | ||
} | ||
|
||
private void setChanged(object sender, NotifyCollectionChangedEventArgs e) { | ||
DateRange = DateTimeRange.FromList( | ||
PhotoSet.Select(p => p.DateTaken)); | ||
} | ||
|
||
|
||
public DateTimeRange? DateRange { | ||
get { | ||
return (DateTimeRange?)GetValue(DateRangeProperty); | ||
} | ||
set { | ||
SetValue(DateRangeProperty, value); | ||
} | ||
} | ||
|
||
public static readonly DependencyProperty DateRangeProperty = | ||
DependencyProperty.Register(nameof(DateRange), typeof(DateTimeRange?), | ||
typeof(DateTimeRangeEdit), | ||
new PropertyMetadata(dateChanged)); | ||
|
||
private static void dateChanged(DependencyObject d, | ||
DependencyPropertyChangedEventArgs e) { | ||
var newTime = e.NewValue as DateTimeRange?; | ||
if (!newTime.HasValue) { | ||
return; | ||
} | ||
if (d is DateTimeRangeEdit self) { | ||
if (self.PhotoSet.Count == 0) { | ||
return; | ||
} | ||
DateTimeRange? oldRange = DateTimeRange.FromList( | ||
self.PhotoSet.Select(p => p.DateTaken)); | ||
if (!oldRange.HasValue || | ||
!oldRange.Value.IsRange) { | ||
self.setAll(newTime.Value.Min); | ||
} else { | ||
var shiftAmount = newTime.Value.Min - oldRange.Value.Min; | ||
self.shiftDates(shiftAmount); | ||
} | ||
} | ||
} | ||
|
||
private async void shiftDates(TimeSpan shiftAmount) { | ||
if (shiftAmount == TimeSpan.Zero) { | ||
return; | ||
} | ||
var part = this.minDatePicker.CurrentDateTimePart; | ||
foreach (Photo p in this.PhotoSet) { | ||
if (p.DateTaken.HasValue) { | ||
p.DateTaken = p.DateTaken.Value + shiftAmount; | ||
} | ||
} | ||
this.DateRange = DateTimeRange.FromList( | ||
this.PhotoSet.Select(p => p.DateTaken)); | ||
// restore the CurrentDateTimePart, but only after all of the data | ||
// binding flow-through has had a chance to propagate. | ||
await this.Dispatcher.InvokeAsync(() => | ||
this.minDatePicker.CurrentDateTimePart = part); | ||
} | ||
|
||
private void setAllEqual(object sender, RoutedEventArgs e) { | ||
if (!this.DateRange.HasValue) { | ||
return; | ||
} | ||
var newTime = this.DateRange.Value.Min; | ||
setAll(newTime); | ||
} | ||
|
||
private async void setAll(DateTime newTime) { | ||
bool anyChanged = false; | ||
var part = this.minDatePicker.CurrentDateTimePart; | ||
foreach (Photo p in this.PhotoSet) { | ||
if (p.DateTaken.HasValue && | ||
p.DateTaken.Value != newTime) { | ||
p.DateTaken = newTime; | ||
anyChanged = true; | ||
} | ||
} | ||
if (anyChanged) { | ||
this.DateRange = DateTimeRange.FromList( | ||
this.PhotoSet.Select(p => p.DateTaken)); | ||
// restore the CurrentDateTimePart, but only after all of the data | ||
// binding flow-through has had a chance to propagate. | ||
await this.Dispatcher.InvokeAsync(() => | ||
this.minDatePicker.CurrentDateTimePart = part); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.