Notification changes
authorpkanavos <pkanavos@gmail.com>
Fri, 6 Apr 2012 19:48:59 +0000 (22:48 +0300)
committerpkanavos <pkanavos@gmail.com>
Fri, 6 Apr 2012 19:48:59 +0000 (22:48 +0300)
Fixed the way folders appear in Selective Synch
Added form with conflicts

28 files changed:
trunk/Pithos.Client.WPF.Test/NodeTest.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF.Test/Pithos.Client.WPF.Test.csproj [new file with mode: 0644]
trunk/Pithos.Client.WPF.Test/Properties/AssemblyInfo.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF.Test/packages.config [new file with mode: 0644]
trunk/Pithos.Client.WPF/Converters/EnumTypeConverter.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml [new file with mode: 0644]
trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF/FileProperties/ConflictsViewModel.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF/Pithos.Client.WPF.csproj
trunk/Pithos.Client.WPF/Properties/AssemblyInfo.cs
trunk/Pithos.Client.WPF/SelectiveSynch/DirectoryRecord.cs
trunk/Pithos.Client.WPF/SelectiveSynch/SelectiveSynchView.xaml
trunk/Pithos.Client.WPF/SelectiveSynch/SelectiveSynchViewModel.cs
trunk/Pithos.Client.WPF/Shell/AboutViewModel.cs
trunk/Pithos.Client.WPF/Shell/FeedbackView.xaml
trunk/Pithos.Client.WPF/Shell/FeedbackViewModel.cs
trunk/Pithos.Client.WPF/Shell/ShellView.xaml
trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
trunk/Pithos.Client.WPF/Utils/EnumerableExtensions.cs [new file with mode: 0644]
trunk/Pithos.Client.WPF/Utils/Node.cs [new file with mode: 0644]
trunk/Pithos.Core/Agents/FileAgent.cs
trunk/Pithos.Core/Agents/Notifier.cs [new file with mode: 0644]
trunk/Pithos.Core/Agents/Uploader.cs
trunk/Pithos.Core/IStatusNotification.cs
trunk/Pithos.Core/Pithos.Core.csproj
trunk/Pithos.Network/RestClient.cs
trunk/Pithos.sln
trunk/packages/repositories.config

diff --git a/trunk/Pithos.Client.WPF.Test/NodeTest.cs b/trunk/Pithos.Client.WPF.Test/NodeTest.cs
new file mode 100644 (file)
index 0000000..db834fc
--- /dev/null
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using Pithos.Client.WPF.Utils;
+
+namespace Pithos.Client.WPF.Test
+{
+    [TestFixture]
+    class NodeTest
+    {
+        [Test]
+        public void TestIteration()
+        {
+            var root = new Node<int>{Path = "Root",
+                                Children =
+                                    {
+                                        new Node<int> {Path = "Root/Path1",
+                                            Children =
+                                                {
+                                                    new Node<int>{Path="Root/Path1/Path11",
+                                                        Children=
+                                                            {
+                                                                new Node<int>{Path="Root/Path1/Path11/Path111",
+                                                                Children=
+                                                                    {
+                                                                        new Node<int>{Path="Root/Path1/Path11/Path111/File1"}        
+                                                                    }}
+                                                            }
+                                                    },
+                                                    new Node<int>{Path="Root/Path1/File2"}
+                                                }
+                                        },                                        
+                                    }
+                            };
+            Assert.That(root.Count(), Is.EqualTo(6));
+        }
+        
+        [Test]
+        public void TestEquals()
+        {
+            var target = new Node<int>{Path = "Root",
+                                Children =
+                                    {
+                                        new Node<int> {Path = "Root/Path1",
+                                            Children =
+                                                {
+                                                    new Node<int>{Path="Root/Path1/Path11",
+                                                        Children=
+                                                            {
+                                                                new Node<int>{Path="Root/Path1/Path11/Path111",
+                                                                Children=
+                                                                    {
+                                                                        new Node<int>{Path="Root/Path1/Path11/Path111/File1"}        
+                                                                    }}
+                                                            }
+                                                    },
+                                                    new Node<int>{Path="Root/Path1/File2"}
+                                                }
+                                        },                                        
+                                    }
+                            };
+            var source= new Node<int>{Path = "Root",
+                                Children =
+                                    {
+                                        new Node<int>{Path = "Root/Path1",
+                                            Children =
+                                                {
+                                                    new Node<int>{Path="Root/Path1/Path11",
+                                                        Children=
+                                                            {
+                                                                new Node<int>{Path="Root/Path1/Path11/Path111",
+                                                                Children=
+                                                                    {
+                                                                        new Node<int>{Path="Root/Path1/Path11/Path111/File1"}        
+                                                                    }}
+                                                            }
+                                                    },
+                                                    new Node<int>{Path="Root/Path1/File2"}
+                                                }
+                                        },                                        
+                                    }
+                            };
+            Assert.That(source.Equals(target), Is.True);            
+        }
+
+        [Test]
+        public void TestToTree()
+        {
+            var target = new Node<int>{Path = "Root",
+                                Children =
+                                    {
+                                        new Node<int>{Path = "Root/Path1",
+                                            Children =
+                                                {
+                                                    new Node<int>{Path="Root/Path1/File2"},
+                                                    new Node<int>{Path="Root/Path1/Path11",
+                                                        Children=
+                                                            {
+                                                                new Node<int>{Path="Root/Path1/Path11/Path111",
+                                                                Children=
+                                                                    {
+                                                                        new Node<int>{Path="Root/Path1/Path11/Path111/File1"}        
+                                                                    }}
+                                                            }
+                                                    },
+                                                }
+                                        },                                        
+                                    }
+                            };
+            var source= new[]
+                            {
+                                Tuple.Create("Root",0),
+                                Tuple.Create("Root/Path1",0),
+                                Tuple.Create("Root/Path1/Path11",0),
+                                Tuple.Create("Root/Path1/Path11/Path111",0),
+                                Tuple.Create("Root/Path1/Path11/Path111/File1",0),
+                                Tuple.Create("Root/Path1/File2",0)
+                            };
+
+            Assert.That(source.ToTree(s=>s.Item1,s=>s.Item2).First().Equals(target), Is.True);
+        }
+    }
+}
diff --git a/trunk/Pithos.Client.WPF.Test/Pithos.Client.WPF.Test.csproj b/trunk/Pithos.Client.WPF.Test/Pithos.Client.WPF.Test.csproj
new file mode 100644 (file)
index 0000000..1639c2a
--- /dev/null
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{7B5BFE77-FC4D-43B3-84A0-9CB457238951}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Pithos.Client.WPF.Test</RootNamespace>
+    <AssemblyName>Pithos.Client.WPF.Test</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Caliburn.Micro, Version=1.2.0.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL" />
+    <Reference Include="nunit.framework">
+      <HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="nunit.mocks">
+      <HintPath>..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll</HintPath>
+    </Reference>
+    <Reference Include="pnunit.framework">
+      <HintPath>..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="NodeTest.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Pithos.Client.WPF\Pithos.Client.WPF.csproj">
+      <Project>{4D9406A3-50ED-4672-BB97-A0B3EA4946FE}</Project>
+      <Name>Pithos.Client.WPF</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/trunk/Pithos.Client.WPF.Test/Properties/AssemblyInfo.cs b/trunk/Pithos.Client.WPF.Test/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..b47c7c6
--- /dev/null
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Pithos.Client.WPF.Test")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HP")]
+[assembly: AssemblyProduct("Pithos.Client.WPF.Test")]
+[assembly: AssemblyCopyright("Copyright © HP 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("75c60907-2488-4b7b-9b03-2e923a17e92a")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/trunk/Pithos.Client.WPF.Test/packages.config b/trunk/Pithos.Client.WPF.Test/packages.config
new file mode 100644 (file)
index 0000000..0c82178
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NUnit" version="2.5.10.11092" />
+</packages>
\ No newline at end of file
diff --git a/trunk/Pithos.Client.WPF/Converters/EnumTypeConverter.cs b/trunk/Pithos.Client.WPF/Converters/EnumTypeConverter.cs
new file mode 100644 (file)
index 0000000..ee8ca84
--- /dev/null
@@ -0,0 +1,59 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Windows.Markup;
+
+namespace Pithos.Client.WPF.Converters
+{
+    public class EnumTypeConverter : EnumConverter
+    {
+        public EnumTypeConverter(Type enumType) : base(enumType) { }
+
+        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+        {
+            if (destinationType == typeof(string) && value != null)
+            {
+                var enumType = value.GetType();
+                if (enumType.IsEnum)
+                    return GetDisplayName(value);
+            }
+
+            return base.ConvertTo(context, culture, value, destinationType);
+        }
+        private string GetDisplayName(object enumValue)
+        {
+            var displayNameAttribute = EnumType.GetField(enumValue.ToString())
+                                                                 .GetCustomAttributes(typeof(DescriptionAttribute), false)
+                                                                 .FirstOrDefault() as DescriptionAttribute;
+            if (displayNameAttribute != null)
+                return displayNameAttribute.Description;
+
+            return Enum.GetName(EnumType, enumValue);
+        }
+    }
+
+    [MarkupExtensionReturnType(typeof(object[]))]
+    public class EnumValuesExtension : MarkupExtension
+    {
+        public EnumValuesExtension()
+        {
+        }
+
+        public EnumValuesExtension(Type enumType)
+        {
+            EnumType = enumType;
+        }
+
+        [ConstructorArgument("enumType")]
+        public Type EnumType { get; set; }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            if (EnumType == null)
+                throw new ArgumentException("The enum type is not set");
+            return Enum.GetValues(EnumType);
+        }
+    }
+
+}
diff --git a/trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml b/trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml
new file mode 100644 (file)
index 0000000..ee3ddc1
--- /dev/null
@@ -0,0 +1,53 @@
+<Window x:Class="Pithos.Client.WPF.FileProperties.ConflictsView"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www.caliburnproject.org" 
+        xmlns:cnv="clr-namespace:Pithos.Client.WPF.Converters"
+        xmlns:model="clr-namespace:Pithos.Client.WPF.FileProperties"
+        Title="Conflicts" Height="300" Width="500" x:Name="This"
+               >
+    <Window.Resources>
+        <ResourceDictionary>
+            <ContextMenu  x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
+            <MenuItem Header="{Binding FilePath}" cal:Message.Attach="[Event MouseLeftButtonUp]=[Action GoToFile($dataContext)]"
+                      cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=DataGrid}, Path=DataContext}"/>
+        </ContextMenu>
+        <Style x:Key="DefaultRowStyle" TargetType="{x:Type DataGridRow}">
+            <Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
+        </Style>
+                <ResourceDictionary.MergedDictionaries>
+                    <ResourceDictionary Source="..\PithosStyles.xaml" />
+                </ResourceDictionary.MergedDictionaries>
+            </ResourceDictionary>
+    </Window.Resources>
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition />
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        
+        <ListBox x:Name="Conflicts" HorizontalContentAlignment="Stretch" Grid.Row="0">
+            <ListBox.ItemTemplate>
+                <DataTemplate>
+                    <Grid>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition />
+                            <ColumnDefinition Width="Auto"/>
+                        </Grid.ColumnDefinitions>
+                        <TextBlock x:Name="FilePath" Text="{Binding FilePath}" Grid.Row="0" Margin="5"/>
+                        <ComboBox x:Name="Action" ItemsSource="{cnv:EnumValues model:ConflictAction}" 
+                                  SelectedValue="{Binding Action}" Grid.Row="1" 
+                                  HorizontalAlignment="Right" 
+                                  HorizontalContentAlignment="Left"
+                                  Width="110" Margin="5" Padding="5,2"
+                                  />
+                    </Grid>
+                </DataTemplate>
+            </ListBox.ItemTemplate>
+        </ListBox>
+        <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">
+            <Button Name="Apply" Content="OK" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}" IsDefault="False" />
+            <Button Name="Cancel" Content="Cancel" Margin="5,5,10,5" Style="{StaticResource ButtonStyle}" IsCancel="True" />
+        </StackPanel>
+
+    </Grid>
+</Window>
diff --git a/trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml.cs b/trunk/Pithos.Client.WPF/FileProperties/ConflictsView.xaml.cs
new file mode 100644 (file)
index 0000000..073e511
--- /dev/null
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace Pithos.Client.WPF.FileProperties
+{
+    /// <summary>
+    /// Interaction logic for ConflictsView.xaml
+    /// </summary>
+    public partial class ConflictsView : Window
+    {
+        public ConflictsView()
+        {
+            InitializeComponent();
+        }
+    }
+}
diff --git a/trunk/Pithos.Client.WPF/FileProperties/ConflictsViewModel.cs b/trunk/Pithos.Client.WPF/FileProperties/ConflictsViewModel.cs
new file mode 100644 (file)
index 0000000..b523742
--- /dev/null
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.ComponentModel.Composition;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Caliburn.Micro;
+using Pithos.Client.WPF.Converters;
+using Pithos.Core;
+using Pithos.Interfaces;
+
+namespace Pithos.Client.WPF.FileProperties
+{
+    [TypeConverter(typeof(EnumTypeConverter))]
+    public enum ConflictAction
+    {
+        [Description("Defer Decision")]
+        Defer,
+        [Description("Keep Local")]
+        KeepLocal,
+        [Description("Keep Server")]
+        KeepServer,
+        [Description("Keep Both")]
+        KeepBoth,
+        [Description("Clear Record")]
+        ClearLocal
+    }
+
+    public class ConflictFile:PropertyChangedBase
+    {
+        private string _filePath;
+        public string FilePath
+        {
+            get { return _filePath; }
+            set
+            {
+                _filePath = value;
+                NotifyOfPropertyChange(()=>FilePath);
+            }
+        }
+
+        private ConflictAction _action;
+
+        public ConflictAction Action
+        {
+            get { return _action; }
+            set
+            {
+                _action = value;
+                NotifyOfPropertyChange(()=>Action);
+            }
+        }
+    }
+    [Export(typeof(ConflictsViewModel))]
+    class ConflictsViewModel:Screen
+    {
+        [Import]
+        public IStatusKeeper StatusKeeper { get; set; }
+
+        [Import]
+        public IConflictResolver Resolver { get; set; }
+
+        private readonly ObservableCollection<ConflictFile> _conflicts;
+
+        public ObservableCollection<ConflictFile> Conflicts
+        {
+            get { return _conflicts; }
+        }
+
+        public string[]  Actions
+        {
+            get { return new[] {"Keep Local", "Keep Server", "Keep Both"}; }
+        }
+
+        public ConflictsViewModel()
+        {
+                       this.DisplayName="Conflicts";
+            var conflicts = from state in FileState.Queryable
+                            /*where state.FileStatus == FileStatus.Conflict ||
+                                  state.OverlayStatus == FileOverlayStatus.Conflict*/
+                            select new ConflictFile {FilePath = state.FilePath};          
+            _conflicts = new ObservableCollection<ConflictFile>(conflicts.ToList());
+            
+        }
+
+        /// <summary>
+        /// Open an explorer window to the target path's directory
+        /// and select the file
+        /// </summary>
+        /// <param name="entry"></param>
+        public void GoToFile(string fullPath)
+        {
+            if (!File.Exists(fullPath) && !Directory.Exists(fullPath))
+                return;
+            Process.Start("explorer.exe", "/select, " + fullPath);
+        }
+
+        public void Apply()
+        {
+            var conflicts = from conflict in Conflicts
+                            where conflict.Action != ConflictAction.Defer
+                            select conflict;
+            Resolver.Resolve(conflicts);
+            
+            TryClose(true);
+        }
+
+        public void Cancel()
+        {
+            TryClose(false);
+        }
+
+        
+    }
+
+    internal interface IConflictResolver
+    {
+        void Resolve(IEnumerable<ConflictFile> conflicts);
+    }
+}
index 97cfe2a..2968e76 100644 (file)
     <Reference Include="Caliburn.Micro, Version=1.2.0.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
       <HintPath>..\Libraries\Caliburn.Micro.dll</HintPath>
     </Reference>
+    <Reference Include="Castle.ActiveRecord, Version=3.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL" />
     <Reference Include="log4net">
       <HintPath>..\Libraries\log4net.dll</HintPath>
     </Reference>
     </ApplicationDefinition>
     <Compile Include="Converters\DummyConverter.cs" />
     <Compile Include="Converters\EmptyToVisibilityConverter.cs" />
+    <Compile Include="Converters\EnumTypeConverter.cs" />
     <Compile Include="Converters\NullToVisibilityConverter.cs" />
     <Compile Include="Converters\SingleLineConverter.cs" />
+    <Compile Include="FileProperties\ConflictsView.xaml.cs">
+      <DependentUpon>ConflictsView.xaml</DependentUpon>
+    </Compile>
+    <Compile Include="FileProperties\ConflictsViewModel.cs" />
     <Compile Include="FileProperties\ContainerPolicy.cs" />
     <Compile Include="FileProperties\NewContainerView.xaml.cs">
       <DependentUpon>NewContainerView.xaml</DependentUpon>
     </Compile>
     <Compile Include="Shell\ShellViewModel.cs" />
     <Compile Include="Services\StatusService.cs" />
+    <Compile Include="Utils\EnumerableExtensions.cs" />
+    <Compile Include="Utils\Node.cs" />
     <Compile Include="Wpf32Window.cs" />
+    <Page Include="FileProperties\ConflictsView.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
     <Page Include="FileProperties\NewContainerView.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
index 663d11a..bee5acc 100644 (file)
@@ -56,7 +56,7 @@ using System.Windows;
 [assembly: AssemblyCopyright("Copyright © GRNet 2011-2012")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
-[assembly: AssemblyInformationalVersion("2012-04-03")]
+[assembly: AssemblyInformationalVersion("2012-04-06")]
 
 // Setting ComVisible to false makes the types in this assembly not visible 
 // to COM components.  If you need to access a type in this assembly from 
@@ -93,5 +93,5 @@ using System.Windows;
 // You can specify all the values or you can default the Build and Revision Numbers 
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.7.20403.0")]
-[assembly: AssemblyFileVersionAttribute("0.7.20403.0")]
+[assembly: AssemblyVersion("0.7.20406.0")]
+[assembly: AssemblyFileVersionAttribute("0.7.20406.0")]
index 29bbf05..2ef1867 100644 (file)
@@ -47,19 +47,20 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using Caliburn.Micro;
+using Pithos.Client.WPF.Utils;
 using Pithos.Core.Agents;
 using Pithos.Interfaces;
 
 namespace Pithos.Client.WPF.SelectiveSynch
 {
-    public class DirectoryRecord : PropertyChangedBase,IEnumerable<DirectoryRecord>
+    public class DirectoryRecord :PropertyChangedBase, IEnumerable<DirectoryRecord>
     {
         private ObjectInfo _objectInfo;
         public ObjectInfo ObjectInfo
         {
             get { return _objectInfo; }
             set
-            {                
+            {
                 _objectInfo = value;
                 Uri = value.Uri;
             }
@@ -133,21 +134,11 @@ namespace Pithos.Client.WPF.SelectiveSynch
 
         public bool IsInitiallySelected { get; private set; }
 
-        /*readonly Lazy<List<DirectoryRecord>> _directories = new Lazy<List<DirectoryRecord>>();
-
+        private List<DirectoryRecord>  _directories=new List<DirectoryRecord>();
         public List<DirectoryRecord> Directories
         {
-            get
-            {
-                return _directories.Value;
-            }
-        }*/
-
-        private IEnumerable<DirectoryRecord> _directories=new List<DirectoryRecord>();
-        public IEnumerable<DirectoryRecord> Directories
-        {
             get { return _directories; }
-            set { _directories = value; }
+            set { _directories= value; }
         }
 
         public DirectoryRecord()
@@ -166,6 +157,7 @@ namespace Pithos.Client.WPF.SelectiveSynch
         }
 
         private string _displayName;
+
         public string DisplayName
         {
             get
@@ -177,10 +169,8 @@ namespace Pithos.Client.WPF.SelectiveSynch
             set { _displayName = value; }
         }
 
-        public DirectoryRecord(string rootPath,ObjectInfo info)
+        public DirectoryRecord(ObjectInfo info)
         {
-            var relativePath = info.RelativeUrlToFilePath(info.Account);
-            //LocalInfo = new DirectoryInfo(Path.Combine(rootPath, relativePath));
             ObjectInfo = info;
         }
 
@@ -206,6 +196,7 @@ namespace Pithos.Client.WPF.SelectiveSynch
         }
 */
 
+
         public IEnumerator<DirectoryRecord> GetEnumerator()
         {
             yield return this;
@@ -220,5 +211,42 @@ namespace Pithos.Client.WPF.SelectiveSynch
         {
             return GetEnumerator();
         }
+
+        public override int GetHashCode()
+        {
+            return ObjectInfo.GetHashCode();
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is DirectoryRecord))
+                return false;
+            var other = (DirectoryRecord)obj;
+            if (Uri != other.Uri)
+                return false;
+            if (ObjectInfo== null ^ other.ObjectInfo== null)
+                return false;
+
+            if (ObjectInfo!= null && !ObjectInfo.Equals(other.ObjectInfo))
+                return false;
+            var thisEnum = GetEnumerator();
+            var otherEnum = other.GetEnumerator();
+            //Skipt the first item, it is the current node itself
+            thisEnum.MoveNext();
+            otherEnum.MoveNext();
+            while (true)
+            {
+                var thisMove = thisEnum.MoveNext();
+                var otherMove = otherEnum.MoveNext();
+
+                if (thisMove ^ otherMove)
+                    return false;
+                if (!thisMove)
+                    return true;
+
+                if (!thisEnum.Current.Equals(otherEnum.Current))
+                    return false;
+            }
+        }
     }
 }
index d2389ee..f6a6b2b 100644 (file)
@@ -4,7 +4,7 @@
         xmlns:local="clr-namespace:Pithos.Client.WPF.SelectiveSynch"
         xmlns:extToolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"
         xmlns:Converters="clr-namespace:Pithos.Client.WPF.Converters" 
-        Title="Selective Synch" Height="300" Width="300" 
+        Title="Selective Synch" Height="500" Width="500" 
         ShowInTaskbar="true"
         WindowStartupLocation="CenterScreen"
         Icon="/PithosPlus;component/Images/PithosTaskbar.png"
@@ -47,7 +47,7 @@
             <RowDefinition Height="Auto"/>
         </Grid.RowDefinitions>
         <extToolkit:BusyIndicator Name="IsBusy" Grid.Row="0" BusyContent="Retrieving folders ..." DisplayAfter="0">
-            <TreeView  Name="RootNodes" 
+            <TreeView  Name="RootNodes" Margin="5"
                   ItemContainerStyle="{StaticResource TreeItemStyle}" 
                   ItemTemplate="{StaticResource CheckboxStyle}">
             </TreeView>
index 50a19cb..00ce722 100644 (file)
@@ -46,6 +46,7 @@ using System.Linq;
 using System.Threading.Tasks;
 using Caliburn.Micro;
 using Pithos.Client.WPF.Properties;
+using Pithos.Client.WPF.Utils;
 using Pithos.Core;
 using Pithos.Interfaces;
 
@@ -78,7 +79,7 @@ namespace Pithos.Client.WPF.SelectiveSynch
         {
             var root = RootNodes[0];            
             _checks = new ObservableCollection<ObjectInfo>(
-                from record in root
+                from DirectoryRecord record in root
                 where record.IsChecked==true
                 select record.ObjectInfo);
             NotifyOfPropertyChange(() => Checks);
@@ -88,7 +89,7 @@ namespace Pithos.Client.WPF.SelectiveSynch
         {
             Account = account;
             AccountName = account.AccountName;
-            DisplayName = account.AccountName;
+            DisplayName = String.Format("Selective folder synchronization for {0}",account.AccountName);
             _monitor = monitor;
             _events = events;
             TaskEx.Run(LoadRootNode);
@@ -98,14 +99,14 @@ namespace Pithos.Client.WPF.SelectiveSynch
         {
             var client = _monitor.CloudClient;
 
-            var dirs = from container in client.ListContainers(_monitor.UserName)
+            var dirs = from container in client.ListContainers(_monitor.UserName)                       
                        select new DirectoryRecord
                                   {
                                       DisplayName = container.Name,
                                       Uri=new Uri(client.StorageUrl,container.Name),
-                                      Directories = (from dir in client.ListObjects(_monitor.UserName, container.Name, "")
+                                      Directories = (from dir in client.ListObjects(_monitor.UserName, container.Name)                                                     
                                                      where dir.Content_Type == DirectoryType
-                                                     select new DirectoryRecord { DisplayName = dir.Name, ObjectInfo = dir }).ToList()
+                                                     select dir).ToTree()
                                   };
             var ownFolders = dirs.ToList();
 
@@ -119,9 +120,9 @@ namespace Pithos.Client.WPF.SelectiveSynch
                                                         {
                                                             DisplayName=container.Name,
                                                             Uri = new Uri(client.StorageUrl, "../" + account.name + "/" + container.Name),
-                                                            Directories=(from folder in client.ListObjects(account.name,container.Name,"")
+                                                            Directories=(from folder in client.ListObjects(account.name,container.Name)
                                                                         where folder.Content_Type==DirectoryType
-                                                                        select new DirectoryRecord{DisplayName=folder.Name,ObjectInfo=folder}).ToList()
+                                                                        select folder).ToTree()
                                                         }).ToList()
                              };                                                          
 
@@ -169,8 +170,8 @@ namespace Pithos.Client.WPF.SelectiveSynch
             //Initially, all nodes are checked
             //We need to *uncheck* the nodes that are not selected
 
-            var selects = from rootRecord in RootNodes
-                          from record in rootRecord
+            var selects = from DirectoryRecord rootRecord in RootNodes
+                          from DirectoryRecord record in rootRecord
                           where record.Uri !=null &&  !selections.Contains(record.Uri.ToString())
                           select record;
 
@@ -181,21 +182,21 @@ namespace Pithos.Client.WPF.SelectiveSynch
 
         public void SaveChanges()
         {
-            var uris = (from root in RootNodes
-                        from record in root
+            var uris = (from DirectoryRecord root in RootNodes
+                        from DirectoryRecord record in root
                         where record.IsChecked == true && record.Uri != null
                         select record.Uri).ToArray();            
 
             SaveSettings(uris);
             
             //RootNodes is an ObservableCollection, it can't be enumerated iterativelly
-            
-            var added= (from root in RootNodes
-                        from record in root
+
+            var added = (from DirectoryRecord root in RootNodes
+                         from DirectoryRecord record in root
                          where record.Added && record.Uri != null
                          select record.Uri).ToArray();
-            var removed = (from root in RootNodes
-                            from record in root
+            var removed = (from DirectoryRecord root in RootNodes
+                           from DirectoryRecord record in root
                           where record.Removed && record.Uri != null
                          select record.Uri).ToArray();
             //TODO: Include Uris for the containers as well
index ccb57d7..b87871f 100644 (file)
@@ -83,6 +83,8 @@ namespace Pithos.Client.WPF.Shell
             Version = fileVersion.FileVersion;
 
             Bits = Environment.Is64BitProcess ? "64 bit" : "32 bit";
+
+            DisplayName = "About Pithos+";
         }
 
         public void CloseAbout()
index da0374d..d8ce829 100644 (file)
@@ -1,7 +1,7 @@
 <Window x:Class="Pithos.Client.WPF.Shell.FeedbackView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        Title="FeedbackView" Height="389" Width="455" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters" Icon="/PithosPlus;component/Images/PithosTaskbar.png">
+        Height="389" Width="455" xmlns:my="clr-namespace:Pithos.Client.WPF.Converters" Icon="/PithosPlus;component/Images/PithosTaskbar.png">
     <Window.Resources>
         <my:EmptyToVisibilityConverter x:Key="EmptyToVisible" />
     </Window.Resources>
index fcefc09..5171213 100644 (file)
@@ -75,6 +75,7 @@ namespace Pithos.Client.WPF.Shell
         [ImportingConstructor]
         public FeedbackViewModel(IWindowManager windowManager)
         {
+            DisplayName = "Send Feedback";
             _windowManager = windowManager;
             Data = GetBasicData() ;
         }
index 8395cbe..0eeddeb 100644 (file)
@@ -95,6 +95,7 @@
                     <Separator  Visibility="{Binding Path=HasAccounts, Converter={StaticResource BooleanToVisible}}"/>
                     <MenuItem  Header="{Binding PauseSyncCaption}" x:Name="ToggleSynching" cal:Message.Attach="ToggleSynching"  Visibility="{Binding Path=HasAccounts, Converter={StaticResource BooleanToVisible}}"/>
                     <Separator  />
+                    <MenuItem x:Name="ShowConflicts" Header="Show Conflicts" Visibility="{Binding Path=HasConflicts, Converter={StaticResource BooleanToVisible}}" cal:Message.Attach="ShowConflicts" />
                     <MenuItem  Header="Preferences ..." x:Name="ShowPreferences" cal:Message.Attach="ShowPreferences"  />
                     <Separator  />
                     <MenuItem  Header="Send Feedback" x:Name="SendFeedback" cal:Message.Attach="SendFeedback">
index c99d650..8ef5a6c 100644 (file)
@@ -519,6 +519,15 @@ namespace Pithos.Client.WPF {
                NotifyOfPropertyChange(()=>MiniStatusCaption);
         }
 
+           public bool HasConflicts
+           {
+            get { return true; }
+           }
+        public void ShowConflicts()
+        {
+            _windowManager.ShowWindow(new ConflictsViewModel());
+        }
+
            /// <summary>
         /// Open an explorer window to the target path's directory
         /// and select the file
@@ -706,9 +715,20 @@ namespace Pithos.Client.WPF {
             SetPithosStatus(status);
         }
 
+         /*  public Notifier GetNotifier(Notification startNotification, Notification endNotification)
+           {
+               return new Notifier(this, startNotification, endNotification);
+           }*/
 
+           public Notifier GetNotifier(string startMessage, string endMessage, params object[] args)
+           {
+               return new Notifier(this, 
+                new StatusNotification(String.Format(startMessage,args)), 
+                new StatusNotification(String.Format(endMessage,args)));
+           }
 
-               ///<summary>
+
+           ///<summary>
                /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
                ///</summary>
                public void UpdateStatus()
diff --git a/trunk/Pithos.Client.WPF/Utils/EnumerableExtensions.cs b/trunk/Pithos.Client.WPF/Utils/EnumerableExtensions.cs
new file mode 100644 (file)
index 0000000..9de758a
--- /dev/null
@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Text;
+using Pithos.Client.WPF.SelectiveSynch;
+using Pithos.Interfaces;
+
+namespace Pithos.Client.WPF.Utils
+{
+    public static class EnumerableExtensions
+    {
+        public static IEnumerable<T> Slice<T>(this IEnumerable<T> collection, int start, int end)
+        {
+            int index = 0;
+            int count = 0;
+
+            if (collection == null)
+                throw new ArgumentNullException("collection");
+
+            // Optimise item count for ICollection interfaces.
+            if (collection is ICollection<T>)
+                count = ((ICollection<T>)collection).Count;
+            else if (collection is ICollection)
+                count = ((ICollection)collection).Count;
+            else
+            {
+                count = collection.Count();
+            }
+
+            // Get start/end indexes, negative numbers start at the end of the collection.
+            if (start < 0)
+                start += count;
+
+            if (end < 0)
+                end += count;
+
+            foreach (var item in collection)
+            {
+                if (index >= end)
+                    yield break;
+
+                if (index >= start)
+                    yield return item;
+
+                ++index;
+            }
+        }
+
+        public static IEnumerable<Node<T>> ToTree<TSource,T>(this IEnumerable<TSource> enumerable,Func<TSource,string> pathFunc,Func<TSource,T> valueFunc,string delimiter="/")
+        {
+            var orderedItems=enumerable.OrderBy(pathFunc);
+            var lookups = new Dictionary<string,Node<T>>();
+            var nodes = new List<Node<T>>();
+            foreach (var item in orderedItems)
+            {
+                var path = pathFunc(item);
+                var value = valueFunc(item);
+                var newNode = new Node<T> { Path = path,Data=value };
+                lookups[path] = newNode;
+
+                var lastIndex = path.LastIndexOf(delimiter, StringComparison.Ordinal);
+                var upTo = lastIndex < 0 ? path.Length - 1 : lastIndex;
+                var parentPath = path.Substring(0, upTo);              
+  
+                Node<T> parent;
+                if (lookups.TryGetValue(parentPath, out parent))
+                {
+                    parent.Children.Add(newNode);   
+                    parent.Children.Sort((x,y)=>String.CompareOrdinal(x.Path, y.Path));
+                }
+                else
+                    nodes.Add(newNode);
+
+            }
+            return nodes;
+        }
+
+        public static List<DirectoryRecord> ToTree(this IEnumerable<ObjectInfo> enumerable)
+        {
+            var orderedItems=enumerable.OrderBy(o=>o.Uri.ToString());
+            var lookups = new Dictionary<string,DirectoryRecord>();
+            var nodes = new List<DirectoryRecord>();
+            foreach (var item in orderedItems)
+            {
+                var path = item.Uri.ToString();
+                var newNode = new DirectoryRecord{ DisplayName=item.Name,ObjectInfo=item};
+                lookups[path] = newNode;
+
+                var lastIndex = path.LastIndexOf("/", StringComparison.Ordinal);
+                var upTo = lastIndex < 0 ? path.Length - 1 : lastIndex;
+                var parentPath = path.Substring(0, upTo);              
+  
+                DirectoryRecord parent;
+                if (lookups.TryGetValue(parentPath, out parent))
+                {
+                    parent.Directories.Add(newNode);   
+                    parent.Directories.Sort((x,y)=>String.CompareOrdinal(x.Uri.ToString(), y.Uri.ToString()));
+                }
+                else
+                    nodes.Add(newNode);
+
+            }
+            return nodes;
+        }
+    }
+}
diff --git a/trunk/Pithos.Client.WPF/Utils/Node.cs b/trunk/Pithos.Client.WPF/Utils/Node.cs
new file mode 100644 (file)
index 0000000..ddbaac1
--- /dev/null
@@ -0,0 +1,72 @@
+using System.Collections;
+using System.Collections.Generic;
+using Caliburn.Micro;
+
+namespace Pithos.Client.WPF.Utils
+{
+    public class Node<T> : IEnumerable<Node<T>>
+    {
+        public string Path { get; set; }
+
+        public T Data { get; set; }
+
+        private List<Node<T>> _children = new List<Node<T>>();
+        public List<Node<T>> Children
+        {
+            get { return _children; }
+            set { _children = value; }
+        }
+
+        public IEnumerator<Node<T>> GetEnumerator()
+        {
+            yield return this;
+            foreach (var children in _children)
+                foreach (var info in children)
+                {
+                    yield return info;
+                }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return GetEnumerator();
+        }
+
+        public override int GetHashCode()
+        {
+            return Path.GetHashCode();
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (!(obj is Node<T>))
+                return false;
+            var other = (Node<T>) obj;
+            if (Path != other.Path)
+                return false;
+            if (Data==null ^ other.Data==null)
+                return false;
+            
+            if (Data!=null && !Data.Equals(other.Data))
+                return false;
+            var thisEnum=GetEnumerator();
+            var otherEnum = other.GetEnumerator();
+            //Skipt the first item, it is the current node itself
+            thisEnum.MoveNext();
+            otherEnum.MoveNext();
+            while(true)
+            {
+                var thisMove=thisEnum.MoveNext();
+                var otherMove=otherEnum.MoveNext();
+
+                if (thisMove ^ otherMove)
+                    return false;
+                if (!thisMove)
+                    return true;
+                
+                if (!thisEnum.Current.Equals(otherEnum.Current))
+                    return false;
+            }
+        }
+    }
+}
\ No newline at end of file
index a79ccdf..fc8fbe8 100644 (file)
@@ -489,17 +489,19 @@ namespace Pithos.Core.Agents
             if (Directory.Exists(path))
                 return state;
 
-
             var info = new FileInfo(path);
-            StatusNotification.Notify(new StatusNotification(String.Format("Hashing [{0}]",info.Name)));
 
-            var shortHash = info.ComputeShortHash(); 
-            
-            string merkleHash = info.CalculateHash(StatusKeeper.BlockSize,StatusKeeper.BlockHash);
-            StatusKeeper.UpdateFileChecksum(path,shortHash, merkleHash);
+            using (StatusNotification.GetNotifier("Hashing {0}", "Finished Hashing {0}", info.Name))
+            {
 
-            state.Hash = merkleHash;
-            return state;
+                var shortHash = info.ComputeShortHash();
+
+                string merkleHash = info.CalculateHash(StatusKeeper.BlockSize, StatusKeeper.BlockHash);
+                StatusKeeper.UpdateFileChecksum(path, shortHash, merkleHash);
+
+                state.Hash = merkleHash;
+                return state;
+            }
         }
 
         //Does the file exist in the container's local folder?
diff --git a/trunk/Pithos.Core/Agents/Notifier.cs b/trunk/Pithos.Core/Agents/Notifier.cs
new file mode 100644 (file)
index 0000000..fc90cde
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Pithos.Core
+{
+    public class Notifier:IDisposable
+    {
+        private readonly IStatusNotification _statusNotification;
+
+        private readonly Notification _endNotification;
+
+        public Notifier(IStatusNotification statusNotification,string startMessage,string endMessage)
+            :this(statusNotification,new Notification{Message=startMessage},new Notification{Message=endMessage} )
+        {
+            
+        }
+
+        public Notifier(IStatusNotification statusNotification,Notification startNotification,Notification endNotification)
+        {
+            _statusNotification = statusNotification;
+            _endNotification = endNotification;
+            _statusNotification.Notify(startNotification);
+        }
+
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~Notifier()
+        {
+            Dispose(false);
+        }
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _statusNotification.Notify(_endNotification);        
+            }
+        }
+    }
+}
index 6d0f258..87c4dc3 100644 (file)
@@ -101,10 +101,13 @@ namespace Pithos.Core.Agents
 
                                 var cloudHash = cloudInfo.Hash.ToLower();
 
-                                StatusNotification.Notify(new StatusNotification(String.Format("Hashing {0} for Upload", fileInfo.Name)));
-
-                                var treeHash = action.TreeHash.Value;
-                                var topHash = treeHash.TopHash.ToHashString();
+                                string topHash;
+                                TreeHash treeHash;
+                                using(StatusNotification.GetNotifier("Hashing {0} for Upload", "Finished hashing {0}",fileInfo.Name))
+                                {
+                                    treeHash = action.TreeHash.Value;
+                                    topHash = treeHash.TopHash.ToHashString();
+                                }
 
                                 //If the file hashes match, abort the upload
                                 if (cloudInfo != ObjectInfo.Empty && topHash == cloudHash)
@@ -207,49 +210,51 @@ namespace Pithos.Core.Agents
                 throw new ArgumentException("Invalid container", "cloudFile");
             Contract.EndContractBlock();
 
-            StatusNotification.Notify(new StatusNotification(String.Format("Uploading {0}", fileInfo.Name)));
+            using (StatusNotification.GetNotifier("Uploading {0}", "Finished Uploading {0}", fileInfo.Name))
+            {
 
-            var fullFileName = fileInfo.GetProperCapitalization();
+                var fullFileName = fileInfo.GetProperCapitalization();
 
-            var account = cloudFile.Account ?? accountInfo.UserName;
-            var container = cloudFile.Container;
+                var account = cloudFile.Account ?? accountInfo.UserName;
+                var container = cloudFile.Container;
 
-            var client = new CloudFilesClient(accountInfo);
-            //Send the hashmap to the server            
-            var missingHashes = await client.PutHashMap(account, container, url, treeHash);
-            int block = 0;
-            ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);
-            //If the server returns no missing hashes, we are done
-            while (missingHashes.Count > 0)
-            {
-
-                var buffer = new byte[accountInfo.BlockSize];
-                foreach (var missingHash in missingHashes)
+                var client = new CloudFilesClient(accountInfo);
+                //Send the hashmap to the server            
+                var missingHashes = await client.PutHashMap(account, container, url, treeHash);
+                int block = 0;
+                ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);
+                //If the server returns no missing hashes, we are done
+                while (missingHashes.Count > 0)
                 {
-                    //Find the proper block
-                    var blockIndex = treeHash.HashDictionary[missingHash];
-                    long offset = blockIndex * accountInfo.BlockSize;
-
-                    var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);
 
-                    try
-                    {
-                        //And upload the block                
-                        await client.PostBlock(account, container, buffer, 0, read);
-                        Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);
-                    }
-                    catch (Exception exc)
+                    var buffer = new byte[accountInfo.BlockSize];
+                    foreach (var missingHash in missingHashes)
                     {
-                        Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);
+                        //Find the proper block
+                        var blockIndex = treeHash.HashDictionary[missingHash];
+                        long offset = blockIndex*accountInfo.BlockSize;
+
+                        var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);
+
+                        try
+                        {
+                            //And upload the block                
+                            await client.PostBlock(account, container, buffer, 0, read);
+                            Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);
+                        }
+                        catch (Exception exc)
+                        {
+                            Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);
+                        }
+                        ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);
                     }
-                    ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);
+
+                    //Repeat until there are no more missing hashes                
+                    missingHashes = await client.PutHashMap(account, container, url, treeHash);
                 }
 
-                //Repeat until there are no more missing hashes                
-                missingHashes = await client.PutHashMap(account, container, url, treeHash);
+                ReportUploadProgress(fileInfo.Name, missingHashes.Count, missingHashes.Count, fileInfo.Length);
             }
-
-            ReportUploadProgress(fileInfo.Name, missingHashes.Count, missingHashes.Count, fileInfo.Length);
         }
 
         private void ReportUploadProgress(string fileName, int block, int totalBlocks, long fileSize)
index e4ba992..a91173b 100644 (file)
@@ -57,6 +57,8 @@ namespace Pithos.Core
         void Notify(Notification notification);\r
         void SetPithosStatus(PithosStatus status);\r
         void SetPithosStatus(PithosStatus localSyncing, string format);\r
+        //Notifier GetNotifier(Notification startNotification, Notification endNotification);\r
+        Notifier GetNotifier(string startMessage, string endMessage,params object[] args);\r
     }\r
 \r
     public class Notification\r
index c2cf51a..1825295 100644 (file)
     <Compile Include="Agents\IdleBatch.cs" />
     <Compile Include="Agents\MovedEventArgs.cs" />
     <Compile Include="Agents\NetworkAgent.cs" />
+    <Compile Include="Agents\Notifier.cs" />
     <Compile Include="Agents\ObjectInfoComparer.cs" />
     <Compile Include="Agents\PollAgent.cs" />
     <Compile Include="Agents\SnapshotDifferencer.cs" />
index d610a15..de4b4a5 100644 (file)
@@ -287,7 +287,7 @@ namespace Pithos.Network
 
             try
             {
-                var result = task.Result;
+                    var result = task.Result;
                 return result;
 
             }
index 1abc004..2db5942 100644 (file)
@@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pithos.AppCast", "Pithos.Ap
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetSparkle2010", "NetSparkle\NetSparkle2010.csproj", "{74635A21-2BAD-4522-AB95-E3E5703CD301}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Client.WPF.Test", "Pithos.Client.WPF.Test\Pithos.Client.WPF.Test.csproj", "{7B5BFE77-FC4D-43B3-84A0-9CB457238951}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug All|Any CPU = Debug All|Any CPU
@@ -532,6 +534,36 @@ Global
                {74635A21-2BAD-4522-AB95-E3E5703CD301}.Test|Mixed Platforms.Build.0 = Release|Any CPU
                {74635A21-2BAD-4522-AB95-E3E5703CD301}.Test|x64.ActiveCfg = Release|Any CPU
                {74635A21-2BAD-4522-AB95-E3E5703CD301}.Test|x86.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|Any CPU.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|Any CPU.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|Mixed Platforms.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|Mixed Platforms.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|x64.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug All|x86.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|x64.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Debug|x86.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|x64.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Premium Debug|x86.ActiveCfg = Debug|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|Any CPU.Build.0 = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|x64.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Release|x86.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|Any CPU.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|Any CPU.Build.0 = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|Mixed Platforms.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|Mixed Platforms.Build.0 = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|x64.ActiveCfg = Release|Any CPU
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951}.Test|x86.ActiveCfg = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -541,5 +573,6 @@ Global
                {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095}
                {E027200B-C26A-4877-BFD9-1A18CF5DF2F4} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095}
                {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095}
+               {7B5BFE77-FC4D-43B3-84A0-9CB457238951} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095}
        EndGlobalSection
 EndGlobal
index 040c2d7..9abaa04 100644 (file)
@@ -1,10 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <repositories>
+  <repository path="..\Pithos.Client.Test\packages.config" />
+  <repository path="..\Pithos.Client.WPF.Test\packages.config" />
+  <repository path="..\Pithos.Client.WPF\packages.config" />
   <repository path="..\Pithos.Client\packages.config" />
   <repository path="..\Pithos.Core.Test\packages.config" />
+  <repository path="..\Pithos.Core\packages.config" />
   <repository path="..\Pithos.Network.Test\packages.config" />
   <repository path="..\Pithos.ShellExtensions.Test\packages.config" />
-  <repository path="..\Pithos.Client.Test\packages.config" />
-  <repository path="..\Pithos.Core\packages.config" />
-  <repository path="..\Pithos.Client.WPF\packages.config" />
 </repositories>
\ No newline at end of file