OSDN Git Service

svnから移動
authorgvvp <vvp@icefree.org>
Sun, 26 Jul 2020 05:15:36 +0000 (14:15 +0900)
committergvvp <vvp@icefree.org>
Sun, 26 Jul 2020 05:15:36 +0000 (14:15 +0900)
22 files changed:
src/AssistRB.sln [new file with mode: 0644]
src/AssistRB/App.xaml [new file with mode: 0644]
src/AssistRB/App.xaml.cs [new file with mode: 0644]
src/AssistRB/AssistRB.csproj [new file with mode: 0644]
src/AssistRB/DlnaItemList.cs [new file with mode: 0644]
src/AssistRB/DynamicJson.cs [new file with mode: 0644]
src/AssistRB/FindRecbox.cs [new file with mode: 0644]
src/AssistRB/MainWindow.xaml [new file with mode: 0644]
src/AssistRB/MainWindow.xaml.cs [new file with mode: 0644]
src/AssistRB/Properties/AssemblyInfo.cs [new file with mode: 0644]
src/AssistRB/Properties/Resources.Designer.cs [new file with mode: 0644]
src/AssistRB/Properties/Resources.resx [new file with mode: 0644]
src/AssistRB/Properties/Settings.Designer.cs [new file with mode: 0644]
src/AssistRB/Properties/Settings.settings [new file with mode: 0644]
src/AssistRB/RecboxController.cs [new file with mode: 0644]
src/AssistRB/SortableBindingList.cs [new file with mode: 0644]
src/AssistRB/ToolLog.cs [new file with mode: 0644]
src/AssistRB/TransferStatusChecker.cs [new file with mode: 0644]
src/AssistRB/WebLowLayer.cs [new file with mode: 0644]
src/AssistRB/WindowConnection.xaml [new file with mode: 0644]
src/AssistRB/WindowConnection.xaml.cs [new file with mode: 0644]
src/AssistRB/app.config [new file with mode: 0644]

diff --git a/src/AssistRB.sln b/src/AssistRB.sln
new file mode 100644 (file)
index 0000000..5d1439b
--- /dev/null
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssistRB", "AssistRB\AssistRB.csproj", "{87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|x86 = Debug|x86
+               Release|x86 = Release|x86
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}.Debug|x86.ActiveCfg = Debug|x86
+               {87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}.Debug|x86.Build.0 = Debug|x86
+               {87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}.Release|x86.ActiveCfg = Release|x86
+               {87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}.Release|x86.Build.0 = Release|x86
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/src/AssistRB/App.xaml b/src/AssistRB/App.xaml
new file mode 100644 (file)
index 0000000..c9d8979
--- /dev/null
@@ -0,0 +1,8 @@
+<Application x:Class="AssistRB.App"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             StartupUri="MainWindow.xaml">
+    <Application.Resources>
+         
+    </Application.Resources>
+</Application>
diff --git a/src/AssistRB/App.xaml.cs b/src/AssistRB/App.xaml.cs
new file mode 100644 (file)
index 0000000..182bb5c
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Windows;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// App.xaml の相互作用ロジック
+    /// </summary>
+    public partial class App : Application
+    {
+    }
+}
diff --git a/src/AssistRB/AssistRB.csproj b/src/AssistRB/AssistRB.csproj
new file mode 100644 (file)
index 0000000..df7aea8
--- /dev/null
@@ -0,0 +1,162 @@
+<?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)' == '' ">x86</Platform>
+    <ProductVersion>8.0.30703</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{87CC2E6A-739E-457B-B2C6-E6F6878CD6ED}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>AssistRB</RootNamespace>
+    <AssemblyName>AssistRB</AssemblyName>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <TargetFrameworkProfile>
+    </TargetFrameworkProfile>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <WarningLevel>4</WarningLevel>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <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|x86' ">
+    <PlatformTarget>x86</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Web" />
+    <Reference Include="System.Xml" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Xaml">
+      <RequiredTargetFramework>4.0</RequiredTargetFramework>
+    </Reference>
+    <Reference Include="WindowsBase" />
+    <Reference Include="PresentationCore" />
+    <Reference Include="PresentationFramework" />
+  </ItemGroup>
+  <ItemGroup>
+    <ApplicationDefinition Include="App.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </ApplicationDefinition>
+    <Compile Include="DlnaItemList.cs" />
+    <Compile Include="FindRecbox.cs" />
+    <Compile Include="RecboxController.cs" />
+    <Compile Include="ToolLog.cs" />
+    <Compile Include="TransferStatusChecker.cs" />
+    <Compile Include="WebLowLayer.cs" />
+    <Compile Include="WindowConnection.xaml.cs">
+      <DependentUpon>WindowConnection.xaml</DependentUpon>
+    </Compile>
+    <Page Include="MainWindow.xaml">
+      <Generator>MSBuild:Compile</Generator>
+      <SubType>Designer</SubType>
+    </Page>
+    <Compile Include="App.xaml.cs">
+      <DependentUpon>App.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="DynamicJson.cs" />
+    <Compile Include="MainWindow.xaml.cs">
+      <DependentUpon>MainWindow.xaml</DependentUpon>
+      <SubType>Code</SubType>
+    </Compile>
+    <Page Include="WindowConnection.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </Page>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+    <None Include="app.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <AppDesigner Include="Properties\" />
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4 Client Profile %28x86 および x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+      <Visible>False</Visible>
+      <ProductName>Windows インストーラー 3.1</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+  </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/src/AssistRB/DlnaItemList.cs b/src/AssistRB/DlnaItemList.cs
new file mode 100644 (file)
index 0000000..80362f1
--- /dev/null
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net;
+
+namespace AssistRB
+{
+    public class DlnaItem
+    {
+        public string Parent { get; set; }
+        public string Id { get; set; }
+        public string Title { get; set; }
+        public string Series { get; set; }
+        public string ClassType { get; set; }
+        public Int64 Size { get; set; }
+        public string Date { get; set; }
+    }
+
+    public class DlnaItemList
+    {
+        private const string RootFolderId = "FS-Folder";
+
+        static DlnaItemList instance = null;
+
+        private List<DlnaItem> dlnaItem = new List<DlnaItem>();
+        private Dictionary<string, string> folderDictionary = new Dictionary<string, string>();
+        private List<string> invalidFolder = new List<string>();
+
+        public delegate void ListChangedDelegate();
+        public event ListChangedDelegate ListChanged;
+
+        public static DlnaItemList GetInstance()
+        {
+            if (instance == null)
+            {
+                instance = new DlnaItemList();
+                instance.InvalidateAll();
+            }
+
+            return instance;
+        }
+
+        public void InvalidateAll()
+        {
+            invalidFolder.Clear();
+            invalidFolder.Add(RootFolderId);
+        }
+
+        public void Invalidate(string folder)
+        {
+            if (invalidFolder.Contains(RootFolderId))
+            {
+                return;
+            }
+
+            if (folder == RootFolderId)
+            {
+                InvalidateAll();
+            }
+            else
+            {
+                if (!invalidFolder.Contains(folder))
+                {
+                    invalidFolder.Add(folder);
+                }
+            }
+        }
+
+        public List<DlnaItem> GetList()
+        {
+            return this.dlnaItem;
+        }
+
+        public Dictionary<string, string> GetFolderList()
+        {
+            return this.folderDictionary;
+        }
+
+        public List<string> GetInvalidList()
+        {
+            return this.invalidFolder;
+        }
+
+        public void UpdateList(
+            List<DlnaItem> newList)
+        {
+            this.dlnaItem = newList;
+
+            this.invalidFolder.Clear();
+
+            // フォルダ辞書の更新
+            folderDictionary.Clear();
+            folderDictionary.Add("FS-Folder", "ROOT");
+            foreach (DlnaItem item in this.dlnaItem)
+            {
+                if (item.ClassType == "container")
+                {
+                    folderDictionary.Add(item.Id, item.Title);
+                }
+            }
+
+            if (this.ListChanged != null)
+            {
+                this.ListChanged();
+            }
+        }
+    }
+}
diff --git a/src/AssistRB/DynamicJson.cs b/src/AssistRB/DynamicJson.cs
new file mode 100644 (file)
index 0000000..45771ed
--- /dev/null
@@ -0,0 +1,431 @@
+/*--------------------------------------------------------------------------
+* DynamicJson
+* ver 1.2.0.0 (May. 21th, 2010)
+*
+* created and maintained by neuecc <ils@neue.cc>
+* licensed under Microsoft Public License(Ms-PL)
+* http://neue.cc/
+* http://dynamicjson.codeplex.com/
+*--------------------------------------------------------------------------*/
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Dynamic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace Codeplex.Data
+{
+    public class DynamicJson : DynamicObject
+    {
+        private enum JsonType
+        {
+            @string, number, boolean, @object, array, @null
+        }
+
+        // public static methods
+
+        /// <summary>from JsonSring to DynamicJson</summary>
+        public static dynamic Parse(string json)
+        {
+            return Parse(json, Encoding.Unicode);
+        }
+
+        /// <summary>from JsonSring to DynamicJson</summary>
+        public static dynamic Parse(string json, Encoding encoding)
+        {
+            using (var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max))
+            {
+                return ToValue(XElement.Load(reader));
+            }
+        }
+
+        /// <summary>from JsonSringStream to DynamicJson</summary>
+        public static dynamic Parse(Stream stream)
+        {
+            using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max))
+            {
+                return ToValue(XElement.Load(reader));
+            }
+        }
+
+        /// <summary>from JsonSringStream to DynamicJson</summary>
+        public static dynamic Parse(Stream stream, Encoding encoding)
+        {
+            using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, encoding, XmlDictionaryReaderQuotas.Max, _ => { }))
+            {
+                return ToValue(XElement.Load(reader));
+            }
+        }
+
+        /// <summary>create JsonSring from primitive or IEnumerable or Object({public property name:property value})</summary>
+        public static string Serialize(object obj)
+        {
+            return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));
+        }
+
+        // private static methods
+
+        private static dynamic ToValue(XElement element)
+        {
+            var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
+            switch (type)
+            {
+                case JsonType.boolean:
+                    return (bool)element;
+                case JsonType.number:
+                    return (double)element;
+                case JsonType.@string:
+                    return (string)element;
+                case JsonType.@object:
+                case JsonType.array:
+                    return new DynamicJson(element, type);
+                case JsonType.@null:
+                default:
+                    return null;
+            }
+        }
+
+        private static JsonType GetJsonType(object obj)
+        {
+            if (obj == null) return JsonType.@null;
+
+            switch (Type.GetTypeCode(obj.GetType()))
+            {
+                case TypeCode.Boolean:
+                    return JsonType.boolean;
+                case TypeCode.String:
+                case TypeCode.Char:
+                case TypeCode.DateTime:
+                    return JsonType.@string;
+                case TypeCode.Int16:
+                case TypeCode.Int32:
+                case TypeCode.Int64:
+                case TypeCode.UInt16:
+                case TypeCode.UInt32:
+                case TypeCode.UInt64:
+                case TypeCode.Single:
+                case TypeCode.Double:
+                case TypeCode.Decimal:
+                case TypeCode.SByte:
+                case TypeCode.Byte:
+                    return JsonType.number;
+                case TypeCode.Object:
+                    return (obj is IEnumerable) ? JsonType.array : JsonType.@object;
+                case TypeCode.DBNull:
+                case TypeCode.Empty:
+                default:
+                    return JsonType.@null;
+            }
+        }
+
+        private static XAttribute CreateTypeAttr(JsonType type)
+        {
+            return new XAttribute("type", type.ToString());
+        }
+
+        private static object CreateJsonNode(object obj)
+        {
+            var type = GetJsonType(obj);
+            switch (type)
+            {
+                case JsonType.@string:
+                case JsonType.number:
+                    return obj;
+                case JsonType.boolean:
+                    return obj.ToString().ToLower();
+                case JsonType.@object:
+                    return CreateXObject(obj);
+                case JsonType.array:
+                    return CreateXArray(obj as IEnumerable);
+                case JsonType.@null:
+                default:
+                    return null;
+            }
+        }
+
+        private static IEnumerable<XStreamingElement> CreateXArray<T>(T obj) where T : IEnumerable
+        {
+            return obj.Cast<object>()
+                .Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o)));
+        }
+
+        private static IEnumerable<XStreamingElement> CreateXObject(object obj)
+        {
+            return obj.GetType()
+                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+                .Select(pi => new { Name = pi.Name, Value = pi.GetValue(obj, null) })
+                .Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value)));
+        }
+
+        private static string CreateJsonString(XStreamingElement element)
+        {
+            using (var ms = new MemoryStream())
+            using (var writer = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.Unicode))
+            {
+                element.WriteTo(writer);
+                writer.Flush();
+                return Encoding.Unicode.GetString(ms.ToArray());
+            }
+        }
+
+        // dynamic structure represents JavaScript Object/Array
+
+        readonly XElement xml;
+        readonly JsonType jsonType;
+
+        /// <summary>create blank JSObject</summary>
+        public DynamicJson()
+        {
+            xml = new XElement("root", CreateTypeAttr(JsonType.@object));
+            jsonType = JsonType.@object;
+        }
+
+        private DynamicJson(XElement element, JsonType type)
+        {
+            Debug.Assert(type == JsonType.array || type == JsonType.@object);
+
+            xml = element;
+            jsonType = type;
+        }
+
+        public bool IsObject { get { return jsonType == JsonType.@object; } }
+
+        public bool IsArray { get { return jsonType == JsonType.array; } }
+
+        /// <summary>has property or not</summary>
+        public bool IsDefined(string name)
+        {
+            return IsObject && (xml.Element(name) != null);
+        }
+
+        /// <summary>has property or not</summary>
+        public bool IsDefined(int index)
+        {
+            return IsArray && (xml.Elements().ElementAtOrDefault(index) != null);
+        }
+
+        /// <summary>delete property</summary>
+        public bool Delete(string name)
+        {
+            var elem = xml.Element(name);
+            if (elem != null)
+            {
+                elem.Remove();
+                return true;
+            }
+            else return false;
+        }
+
+        /// <summary>delete property</summary>
+        public bool Delete(int index)
+        {
+            var elem = xml.Elements().ElementAtOrDefault(index);
+            if (elem != null)
+            {
+                elem.Remove();
+                return true;
+            }
+            else return false;
+        }
+
+        /// <summary>mapping to Array or Class by Public PropertyName</summary>
+        public T Deserialize<T>()
+        {
+            return (T)Deserialize(typeof(T));
+        }
+
+        private object Deserialize(Type type)
+        {
+            return (IsArray) ? DeserializeArray(type) : DeserializeObject(type);
+        }
+
+        private dynamic DeserializeValue(XElement element, Type elementType)
+        {
+            var value = ToValue(element);
+            if (value is DynamicJson)
+            {
+                value = ((DynamicJson)value).Deserialize(elementType);
+            }
+            return Convert.ChangeType(value, elementType);
+        }
+
+        private object DeserializeObject(Type targetType)
+        {
+            var result = Activator.CreateInstance(targetType);
+            var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+                .Where(p => p.CanWrite)
+                .ToDictionary(pi => pi.Name, pi => pi);
+            foreach (var item in xml.Elements())
+            {
+                PropertyInfo propertyInfo;
+                if (!dict.TryGetValue(item.Name.LocalName, out propertyInfo)) continue;
+                var value = DeserializeValue(item, propertyInfo.PropertyType);
+                propertyInfo.SetValue(result, value, null);
+            }
+            return result;
+        }
+
+        private object DeserializeArray(Type targetType)
+        {
+            if (targetType.IsArray) // Foo[]
+            {
+                var elemType = targetType.GetElementType();
+                dynamic array = Array.CreateInstance(elemType, xml.Elements().Count());
+                var index = 0;
+                foreach (var item in xml.Elements())
+                {
+                    array[index++] = DeserializeValue(item, elemType);
+                }
+                return array;
+            }
+            else // List<Foo>
+            {
+                var elemType = targetType.GetGenericArguments()[0];
+                dynamic list = Activator.CreateInstance(targetType);
+                foreach (var item in xml.Elements())
+                {
+                    list.Add(DeserializeValue(item, elemType));
+                }
+                return list;
+            }
+        }
+
+        // Delete
+        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
+        {
+            result = (IsArray)
+                ? Delete((int)args[0])
+                : Delete((string)args[0]);
+            return true;
+        }
+
+        // IsDefined, if has args then TryGetMember
+        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+        {
+            if (args.Length > 0)
+            {
+                result = null;
+                return false;
+            }
+
+            result = IsDefined(binder.Name);
+            return true;
+        }
+
+        // Deserialize or foreach(IEnumerable)
+        public override bool TryConvert(ConvertBinder binder, out object result)
+        {
+            if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[]))
+            {
+                var ie = (IsArray)
+                    ? xml.Elements().Select(x => ToValue(x))
+                    : xml.Elements().Select(x => (dynamic)new KeyValuePair<string, object>(x.Name.LocalName, ToValue(x)));
+                result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie;
+            }
+            else
+            {
+                result = Deserialize(binder.Type);
+            }
+            return true;
+        }
+
+        private bool TryGet(XElement element, out object result)
+        {
+            if (element == null)
+            {
+                result = null;
+                return false;
+            }
+
+            result = ToValue(element);
+            return true;
+        }
+
+        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
+        {
+            return (IsArray)
+                ? TryGet(xml.Elements().ElementAtOrDefault((int)indexes[0]), out result)
+                : TryGet(xml.Element((string)indexes[0]), out result);
+        }
+
+        public override bool TryGetMember(GetMemberBinder binder, out object result)
+        {
+            return (IsArray)
+                ? TryGet(xml.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result)
+                : TryGet(xml.Element(binder.Name), out result);
+        }
+
+        private bool TrySet(string name, object value)
+        {
+            var type = GetJsonType(value);
+            var element = xml.Element(name);
+            if (element == null)
+            {
+                xml.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value)));
+            }
+            else
+            {
+                element.Attribute("type").Value = type.ToString();
+                element.ReplaceNodes(CreateJsonNode(value));
+            }
+
+            return true;
+        }
+
+        private bool TrySet(int index, object value)
+        {
+            var type = GetJsonType(value);
+            var e = xml.Elements().ElementAtOrDefault(index);
+            if (e == null)
+            {
+                xml.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value)));
+            }
+            else
+            {
+                e.Attribute("type").Value = type.ToString();
+                e.ReplaceNodes(CreateJsonNode(value));
+            }
+
+            return true;
+        }
+
+        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
+        {
+            return (IsArray)
+                ? TrySet((int)indexes[0], value)
+                : TrySet((string)indexes[0], value);
+        }
+
+        public override bool TrySetMember(SetMemberBinder binder, object value)
+        {
+            return (IsArray)
+                ? TrySet(int.Parse(binder.Name), value)
+                : TrySet(binder.Name, value);
+        }
+
+        public override IEnumerable<string> GetDynamicMemberNames()
+        {
+            return (IsArray)
+                ? xml.Elements().Select((x, i) => i.ToString())
+                : xml.Elements().Select(x => x.Name.LocalName);
+        }
+
+        /// <summary>Serialize to JsonString</summary>
+        public override string ToString()
+        {
+            // <foo type="null"></foo> is can't serialize. replace to <foo type="null" />
+            foreach (var elem in xml.Descendants().Where(x => x.Attribute("type").Value == "null"))
+            {
+                elem.RemoveNodes();
+            }
+            return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), xml.Elements()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/AssistRB/FindRecbox.cs b/src/AssistRB/FindRecbox.cs
new file mode 100644 (file)
index 0000000..bc6b9db
--- /dev/null
@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net;
+using System.Net.Sockets;
+using System.IO;
+using System.Diagnostics;
+using System.Threading;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// オンラインのRECBOXを探します
+    /// </summary>
+    public class FindRecbox
+    {
+        /// <summary>
+        /// 発見したRECBOXを表します
+        /// </summary>
+        public class RecboxInfo : IEquatable<RecboxInfo>
+        {
+            private string mName;
+            private byte[] mMac;
+            private IPAddress mAddress;
+            private IPAddress mMask;
+            private IPAddress mRoute;
+            private UInt16 mPort;
+
+            public string Name
+            {
+                get { return mName; }
+            }
+            public byte[] Mac
+            {
+                get { return mMac; }
+            }
+            public IPAddress Address
+            {
+                get { return mAddress; }
+            }
+            public IPAddress Mask
+            {
+                get { return mMask; }
+            }
+            public IPAddress Route
+            {
+                get { return mRoute; }
+            }
+            public UInt16 Port
+            {
+                get { return mPort; }
+            }
+
+            public RecboxInfo(
+                string name,
+                byte[] mac,
+                byte[] address,
+                byte[] mask,
+                byte[] route,
+                UInt16 port)
+            {
+                Debug.Assert(mac.Length == 6);
+                Debug.Assert(address.Length == 4);
+                Debug.Assert(mask.Length == 4);
+                Debug.Assert(route.Length == 4);
+
+                this.mName = name;
+                this.mMac = (byte[])(mac.Clone());
+                this.mAddress = new IPAddress(address);
+                this.mMask = new IPAddress(mask);
+                this.mRoute = new IPAddress(route);
+                this.mPort = port;
+
+            }
+
+            public bool Equals(RecboxInfo other)
+            {
+                if (this.mName != other.mName) return false;
+                if (this.mMac.Length != other.mMac.Length) return false;
+                for (int i = 0; i < this.mMac.Length; ++i)
+                {
+                    if (this.mMac[i] != other.mMac[i]) return false;
+                }
+                if (!this.mAddress.Equals(other.mAddress)) return false;
+                if (!this.mMask.Equals(other.mMask)) return false;
+                if (!this.mRoute.Equals(other.mRoute)) return false;
+                if (this.mPort != other.mPort) return false;
+                return true;
+            }
+        };
+
+        private List<RecboxInfo> mRecboxList = new List<RecboxInfo>();
+
+        private UInt32 requestSeq { get; set; }
+        private byte[] request = {
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x46, 0x52, 0x4f, 0x4d, 0x2d, 0x50,
+                    0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x49, 0x4f, 0x2d, 0x44, 0x41,
+                    0x54, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                    0x00, 0x00, 0x00, 0x00
+        };
+
+        /// <summary>
+        /// コンストラクタです
+        /// </summary>
+        public FindRecbox()
+        {
+            Random rand = new Random();
+            requestSeq = (UInt32)rand.Next(0x7fffffff);
+        }
+
+        /// <summary>
+        /// 発見したRECBOXのリストを返します
+        /// </summary>
+        /// <returns></returns>
+        public List<RecboxInfo> GetRecboxList()
+        {
+            return mRecboxList;
+        }
+
+        /// <summary>
+        /// 実際に検索します
+        /// </summary>
+        /// <returns>発見した数を返します。エラーが発生した場合は負数を返します。</returns>
+        public int Search()
+        {
+            int count = 0;
+            const double SearchTimeout = 5; // seconds
+
+            mRecboxList.Clear();
+
+            try
+            {
+                UdpClient udpclient = new UdpClient();
+
+                IPEndPoint broadcast = new IPEndPoint(IPAddress.Broadcast, 65);
+                udpclient.Connect(broadcast);
+
+                request[1] = (byte)((requestSeq / 0x1000000) % 0x100);
+                request[2] = (byte)((requestSeq / 0x10000) % 0x100);
+                request[3] = (byte)((requestSeq / 0x100) % 0x100);
+                request[4] = (byte)(requestSeq % 0x100);
+                ++requestSeq;
+
+                udpclient.Send(request, request.Length);
+                udpclient.Send(request, request.Length);
+                udpclient.Send(request, request.Length);
+
+                DateTime limitTime = DateTime.Now.AddSeconds(SearchTimeout);
+
+                State state = new State();
+                state.e = new IPEndPoint(IPAddress.Any, 65);
+                state.u = new UdpClient(state.e);
+
+                while (limitTime > DateTime.Now)
+                {
+                    recieved = false;
+                    state.u.BeginReceive(new AsyncCallback(processUdpResponse), state);
+
+                    while (limitTime > DateTime.Now && !recieved)
+                    {
+                        Thread.Sleep(100);
+                    }
+
+                }
+
+                udpclient.Close();
+
+                count = mRecboxList.Count;
+            }
+            catch (Exception)
+            {
+                count = -1;
+            }
+
+            return count;
+        }
+
+        /// <summary>
+        /// 非同期処理用
+        /// </summary>
+        private struct State
+        {
+            public IPEndPoint e;
+            public UdpClient u;
+        };
+
+        /// <summary>
+        /// 非同期処理用
+        /// </summary>
+        private bool recieved;
+
+        /// <summary>
+        /// 非同期処理用
+        /// </summary>
+        private void processUdpResponse(IAsyncResult result)
+        {
+            UdpClient u = (UdpClient)((State)(result.AsyncState)).u;
+            IPEndPoint e = (IPEndPoint)((State)(result.AsyncState)).e;
+
+            byte[] recieve = u.EndReceive(result, ref e);
+
+            if (recieve.Length == 132)
+            {
+                const int IPAddressOffset = 14;
+                const int NetMaskOffset = 18;
+                const int DefaultOffset = 22;
+                const int PortOffset = 26;
+                const int MACOffset = 28;
+                const int NameOffset = 34;
+                const int NameMaxLength = 80;
+                const int VendorOffset = 115;
+                const int VendorMaxLength = 16;
+
+                string name = Encoding.ASCII.GetString(recieve, NameOffset, NameMaxLength);
+                name = name.TrimEnd('\0');
+                string vendor = Encoding.ASCII.GetString(recieve, VendorOffset, VendorMaxLength);
+                vendor = vendor.TrimEnd('\0');
+
+                byte[] mac = new byte[6];
+                for (int i = 0; i < 6; ++i)
+                {
+                    mac[i] = recieve[MACOffset + i];
+                }
+
+                byte[] address = new byte[4];
+                for (int i = 0; i < 4; ++i)
+                {
+                    address[i] = recieve[IPAddressOffset + i];
+                }
+
+                byte[] mask = new byte[4];
+                for (int i = 0; i < 4; ++i)
+                {
+                    mask[i] = recieve[NetMaskOffset + i];
+                }
+
+                byte[] route = new byte[4];
+                for (int i = 0; i < 4; ++i)
+                {
+                    route[i] = recieve[DefaultOffset + i];
+                }
+
+                UInt16 port = (UInt16)(recieve[PortOffset] * 0x100 + recieve[PortOffset + 1]);
+
+                RecboxInfo recboxInfo = new RecboxInfo(
+                    name, mac, address, mask, route, port);
+
+                if (!mRecboxList.Contains(recboxInfo))
+                {
+                    mRecboxList.Add(recboxInfo);
+                }
+            }
+
+            recieved = true;
+        }
+    }
+}
diff --git a/src/AssistRB/MainWindow.xaml b/src/AssistRB/MainWindow.xaml
new file mode 100644 (file)
index 0000000..de95120
--- /dev/null
@@ -0,0 +1,96 @@
+<Window x:Class="AssistRB.MainWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        Title="MainWindow" Height="715" Width="661" xmlns:my="clr-namespace:AssistRB" Initialized="Window_Initialized">
+    <StackPanel>
+        <Grid Height="32">
+            <Button Content="全件取得" Height="23" HorizontalAlignment="Left" Margin="537,3,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="buttonLoad_Click" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+            <TextBlock Height="23" Name="targetUri" Text="TextBlock" Width="433" Margin="6,6,200,3" />
+        </Grid>
+        <Grid Height="305">
+            <DataGrid AutoGenerateColumns="True" Name="dataGrid1" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllItemList}" IsReadOnly="True" VerticalScrollBarVisibility="Visible" FrozenColumnCount="1" VerticalAlignment="Top" Height="305" />
+        </Grid>
+        <TabControl Height="308" Name="tabControl1">
+            <TabItem Header="リネーム" Name="tabItemRename">
+                <Grid>
+                    <StackPanel>
+                        <Grid Height="58">
+                            <TextBox Height="50" HorizontalAlignment="Left" Margin="1,0,0,0" Name="textBox2" VerticalAlignment="Top" Width="525" Text="^(.*≫)*([二]|((二|字))|\[(HV|二|新|無|字)\]|【無】)?(?&lt;title&gt;.*?)(新|\[字\]|[二]|(二))*$" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" />
+                            <Button Content="Check" Height="23" Margin="0,0,17,8" Name="button3" VerticalAlignment="Bottom" Click="buttonCheck_Click" HorizontalAlignment="Right" Width="75" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                            <CheckBox Content="IncludeDir" Height="16" HorizontalAlignment="Left" Margin="537,6,0,0" Name="checkBoxRenameIncludeDir" VerticalAlignment="Top" />
+                        </Grid>
+                        <Grid Height="180">
+                            <DataGrid AutoGenerateColumns="True" Name="dataGrid2" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=RenameItemList}" IsReadOnly="True" VerticalScrollBarVisibility="Visible" FrozenColumnCount="1" VerticalAlignment="Stretch" />
+                        </Grid>
+                        <Grid Height="32">
+                            <Button Content="Exec" Width="75" Height="23" Name="button2" Click="buttonExec_Click" Margin="0,6,17,2" HorizontalAlignment="Right" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                        </Grid>
+                    </StackPanel>
+                </Grid>
+            </TabItem>
+            <TabItem Header="自動フォルダ分け" Name="tabItemDispatch">
+                <Grid>
+                    <StackPanel>
+                        <Grid Height="58">
+                            <TextBox Height="50" HorizontalAlignment="Left" Margin="1,0,0,0" Name="textBoxDispatchRule" VerticalAlignment="Top" Width="525" Text="^(.*≫)*([二]|(二)|(字)|\[(HV|ニ|新|無)\])?(?&lt;title&gt;[^#]+[^  ])[ ]*#[0-9]+(.?#[0-9]+)?[ ]*(新|終|END)?$" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" />
+                            <Button Content="Check" Height="23" Margin="0,0,17,8" Name="button4" VerticalAlignment="Bottom" Click="buttonDispatchCheck_Click" HorizontalAlignment="Right" Width="75" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                        </Grid>
+                        <Grid Height="180">
+                            <DataGrid AutoGenerateColumns="True" Name="dataGridDispatchList" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=DispatchItemList}" IsReadOnly="True" VerticalScrollBarVisibility="Visible" FrozenColumnCount="1" VerticalAlignment="Stretch" />
+                        </Grid>
+                        <Grid Height="32">
+                            <Button Content="Exec" Width="75" Height="23" Name="buttonDipatchExec" Click="buttonDispatchExec_Click" Margin="0,6,17,2" HorizontalAlignment="Right" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                        </Grid>
+                    </StackPanel>
+                </Grid>
+            </TabItem>
+            <TabItem Header="個別フォルダ分け" Name="tabItemDustbox">
+                <Grid>
+                    <StackPanel>
+                        <Grid Height="58">
+                            <TextBox Height="50" HorizontalAlignment="Left" Margin="1,0,0,0" Name="textBoxDustboxRule" VerticalAlignment="Top" Width="525" Text="" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" />
+                            <Button Content="Check" Height="23" Margin="0,0,17,8" Name="buttonDustboxCheck" VerticalAlignment="Bottom" Click="buttonDustboxCheck_Click" HorizontalAlignment="Right" Width="75" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                            <CheckBox Content="IncludeDir" Height="16" HorizontalAlignment="Left" Margin="537,6,0,0" Name="checkBoxDustboxIncludeDir" VerticalAlignment="Top" />
+                        </Grid>
+                        <Grid Height="180">
+                            <DataGrid AutoGenerateColumns="True" Name="dataGridDustboxList" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=DustboxItemList}" IsReadOnly="True" VerticalScrollBarVisibility="Visible" FrozenColumnCount="1" VerticalAlignment="Stretch" />
+                        </Grid>
+                        <Grid Height="32">
+                            <Button Content="Exec" Width="75" Height="23" Name="buttonDustboxExec" Click="buttonDustboxExec_Click" Margin="0,6,17,2" HorizontalAlignment="Right" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                            <TextBlock Height="23" HorizontalAlignment="Left" Margin="89,7,0,0" Name="textBlock1" Text="移動先フォルダ" VerticalAlignment="Top" Width="96" />
+                            <TextBox Height="24" HorizontalAlignment="Left" Margin="191,5,0,0" Name="textBoxDustboxFolder" VerticalAlignment="Top" Width="335" />
+                        </Grid>
+                    </StackPanel>
+                </Grid>
+            </TabItem>
+            <TabItem Header="全移動" Name="tabItem1">
+                <Grid>
+                    <Button Content="実行" Height="23" HorizontalAlignment="Left" Margin="433,222,0,0" Name="button5" VerticalAlignment="Top" Width="75" Click="buttonAllTransfer_Click"
+                            IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                    <Button Content="転送先取得" Height="23" HorizontalAlignment="Left" Margin="38,6,0,0" Name="buttonAllTransferGetList" VerticalAlignment="Top" Width="75" Click="buttonAllTransferGetList_Click"
+                            IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}" />
+                    <ComboBox Height="24" HorizontalAlignment="Left" Margin="38,35,0,0" Name="comboBoxAllTransferUDN" VerticalAlignment="Top" Width="312"
+                              ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=ServerList}" SelectionChanged="comboBoxAllTransferUDN_SelectionChanged"
+                              IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}, Path=AllowNewRequest}">
+                        <ComboBox.ItemTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding friendlyName}"/>
+                            </DataTemplate>
+                        </ComboBox.ItemTemplate>
+                    </ComboBox>
+                    <ComboBox Height="24" HorizontalAlignment="Left" Margin="356,35,0,0" Name="comboBoxAllTransferDisk" VerticalAlignment="Top" Width="200"
+                              ItemsSource="{Binding Path=SelectedItem.storageList, ElementName=comboBoxAllTransferUDN}" >
+                        <ComboBox.ItemTemplate>
+                            <DataTemplate>
+                                <TextBlock Text="{Binding friendlyName}"/>
+                            </DataTemplate>
+                        </ComboBox.ItemTemplate>
+                    </ComboBox>
+                </Grid>
+            </TabItem>
+        </TabControl>
+        <Grid Height="32">
+            <StatusBarItem Content="{Binding Path=Log.StatusMessage, RelativeSource={RelativeSource FindAncestor, AncestorType=my:MainWindow, AncestorLevel=1}}"></StatusBarItem>
+        </Grid>
+    </StackPanel>
+</Window>
diff --git a/src/AssistRB/MainWindow.xaml.cs b/src/AssistRB/MainWindow.xaml.cs
new file mode 100644 (file)
index 0000000..585ebe7
--- /dev/null
@@ -0,0 +1,403 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Threading;
+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.Navigation;
+using System.Windows.Shapes;
+using System.Net;
+using Codeplex.Data;
+using System.ComponentModel;
+using System.Text.RegularExpressions;
+using System.Web;
+
+namespace AssistRB
+{
+    public delegate void DlnaItemUpdatedDelegate();
+
+    /// <summary>
+    /// MainWindow.xaml の相互作用ロジック
+    /// </summary>
+    public partial class MainWindow : Window, INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        private void OnPropertyChanged(string propertyName)
+        {
+            if (this.PropertyChanged != null)
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+            }
+        }
+
+        private RecboxController recbox;
+        private Thread recboxThread;
+
+        private TransferStatusChecker transferStatusChecker;
+        private Thread transferStatusThread;
+
+        public ToolLog Log
+        {
+            get
+            {
+                return ToolLog.GetInstance();
+            }
+        }
+
+        private bool isProcessing = false;
+        public bool AllowNewRequest
+        {
+            get
+            {
+                return !isProcessing;
+            }
+
+            set
+            {
+                if (isProcessing == value)
+                {
+                    isProcessing = !value;
+                    this.OnPropertyChanged("AllowNewRequest");
+                }
+            }
+        }
+
+        public void RecboxProcessDone()
+        {
+            AllowNewRequest = true;
+        }
+
+        private List<DlnaItem> allItemList = new List<DlnaItem>();
+        public List<DlnaItem> AllItemList
+        {
+            get
+            {
+                return this.allItemList;
+            }
+
+            set
+            {
+                this.allItemList = new List<DlnaItem>(value);
+                this.OnPropertyChanged("AllItemList");
+            }
+        }
+
+        public void AllItemListChanged()
+        {
+            AllItemList = DlnaItemList.GetInstance().GetList();
+        }
+
+        private List<RenameItem> renameItemList = new List<RenameItem>();
+        public List<RenameItem> RenameItemList
+        {
+            get
+            {
+                return this.renameItemList;
+            }
+
+            set
+            {
+                this.renameItemList = new List<RenameItem>(value);
+                this.OnPropertyChanged("RenameItemList");
+            }
+        }
+
+        public void RenameListChanged()
+        {
+            RenameItemList = recbox.RenameList;
+        }
+
+        private List<DispatchItem> dispatchItemList = new List<DispatchItem>();
+        public List<DispatchItem> DispatchItemList
+        {
+            get
+            {
+                return this.dispatchItemList;
+            }
+
+            set
+            {
+                this.dispatchItemList = new List<DispatchItem>(value);
+                this.OnPropertyChanged("DispatchItemList");
+            }
+        }
+
+        public void DispatchListChanged()
+        {
+            DispatchItemList = recbox.DispatchList;
+        }
+
+        private List<DustboxItem> dustboxItemList = new List<DustboxItem>();
+        public List<DustboxItem> DustboxItemList
+        {
+            get
+            {
+                return this.dustboxItemList;
+            }
+
+            set
+            {
+                this.dustboxItemList = new List<DustboxItem>(value);
+                this.OnPropertyChanged("DustboxItemList");
+            }
+        }
+
+        public void DustboxListChanged()
+        {
+            DustboxItemList = recbox.DustboxList;
+        }
+
+        private List<WebLowlayer.ServerList> serverList = new List<WebLowlayer.ServerList>();
+        public List<WebLowlayer.ServerList> ServerList
+        {
+            get
+            {
+                return this.serverList;
+            }
+
+            set
+            {
+                this.serverList = new List<WebLowlayer.ServerList>(value);
+                this.OnPropertyChanged("ServerList");
+            }
+        }
+
+        public void ServerListChanged()
+        {
+            ServerList = recbox.ServerList;
+        }
+
+        FindRecbox.RecboxInfo targetRecbox;
+
+        public MainWindow()
+        {
+            InitializeComponent();
+
+            ToolLog.GetInstance().Add("開始しました");
+
+            // ステータス監視用のスレッドを起動
+            transferStatusChecker = TransferStatusChecker.GetInstance();
+            transferStatusThread = new Thread(transferStatusChecker.worker);
+            transferStatusThread.IsBackground = true;
+            transferStatusThread.Start();
+
+            // RECBOXとの通信(メイン)のスレッドを起動
+            recbox = new RecboxController();
+            recboxThread = new Thread(recbox.worker);
+            recboxThread.IsBackground = true;
+            recboxThread.Start();
+
+            // RECBOXとステータス監視を関連付け
+            transferStatusChecker.TransferStatusChanged +=
+                new TransferStatusChecker.TransferStatusChangedDelegate(recbox.ChangeTransferProcessing);
+
+            // RECBOXのイベント追加
+            recbox.AllItemListChanged += new RecboxController.RecboxEventHandler(AllItemListChanged);
+            recbox.RenameChanged += new RecboxController.RecboxEventHandler(RenameListChanged);
+            recbox.DispatchChanged += new RecboxController.RecboxEventHandler(DispatchListChanged);
+            recbox.DustboxChanged += new RecboxController.RecboxEventHandler(DustboxListChanged);
+            recbox.ProcessDone += new RecboxController.RecboxEventHandler(RecboxProcessDone);
+            recbox.ServerListChanged += new RecboxController.RecboxEventHandler(ServerListChanged);
+        }
+
+        ~MainWindow()
+        {
+            transferStatusChecker.Terminate();
+            transferStatusThread.Join();
+
+            recbox.Stop();
+            recboxThread.Join();
+        }
+
+        private string GetTargetUrl()
+        {
+            string host_url = targetRecbox.Address.ToString();
+            if (host_url == null)
+            {
+                return null;
+            }
+
+            return "http://" + host_url + ":55247";
+        }
+
+        // Load
+        private void buttonLoad_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            if (host_url != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                DlnaItemList.GetInstance().InvalidateAll();
+
+                recbox.RequestUpdateList(host_url);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void buttonCheck_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            bool includeDir = false;
+            if (checkBoxRenameIncludeDir.IsChecked.HasValue)
+            {
+                includeDir = checkBoxRenameIncludeDir.IsChecked.Value;
+            }
+
+            recbox.RequestRenameCheck(textBox2.Text, includeDir);
+        }
+
+        // Exec
+        private void buttonExec_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            if (host_url != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                recbox.RequestRename(host_url);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void buttonDispatchCheck_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            recbox.RequestDispatchCheck(textBoxDispatchRule.Text);
+        }
+
+        // Exec
+        private void buttonDispatchExec_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            if (host_url != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                recbox.RequestDispatch(host_url);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void buttonDustboxCheck_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            bool includeDir = false;
+            if (checkBoxDustboxIncludeDir.IsChecked.HasValue)
+            {
+                includeDir = checkBoxDustboxIncludeDir.IsChecked.Value;
+            }
+
+            recbox.RequestDustboxCheck(textBoxDustboxRule.Text, includeDir);
+        }
+
+        // Exec
+        private void buttonDustboxExec_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            if (host_url != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                recbox.RequestDustbox(host_url, textBoxDustboxFolder.Text);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void buttonAllTransfer_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            WebLowlayer.ServerList server = comboBoxAllTransferUDN.SelectedItem as WebLowlayer.ServerList;
+            WebLowlayer.ServerList.StorageList storage = comboBoxAllTransferDisk.SelectedItem as WebLowlayer.ServerList.StorageList;
+            if (host_url != null && server != null && storage != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                recbox.RequestAllTransfer(host_url, server.udn, storage.id);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void buttonAllTransferGetList_Click(object sender, RoutedEventArgs e)
+        {
+            this.AllowNewRequest = false;
+
+            string host_url = GetTargetUrl();
+            if (host_url != null)
+            {
+                transferStatusChecker.Initializer(host_url);
+
+                recbox.RequestAllTransferGetList(host_url);
+            }
+            else
+            {
+                this.AllowNewRequest = true;
+            }
+        }
+
+        private void comboBoxAllTransferUDN_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            WebLowlayer.ServerList server = comboBoxAllTransferUDN.SelectedItem as WebLowlayer.ServerList;
+            if (server == null || server.storageList.Count == 0)
+            {
+                comboBoxAllTransferDisk.SelectedIndex = -1;
+            }
+            else
+            {
+                comboBoxAllTransferDisk.SelectedIndex = 0;
+            }
+        }
+
+        private void Window_Initialized(object sender, EventArgs e)
+        {
+            WindowConnection windowConnection = new WindowConnection();
+            windowConnection.ShowDialog();
+
+            targetRecbox = windowConnection.GetTargetRecbox();
+            if (targetRecbox == null)
+            {
+                this.Close();
+            }
+            else
+            {
+                targetUri.Text = targetRecbox.Address.ToString();
+            }
+        }
+    }
+}
diff --git a/src/AssistRB/Properties/AssemblyInfo.cs b/src/AssistRB/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..3ce84bd
--- /dev/null
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。
+// アセンブリに関連付けられている情報を変更するには、
+// これらの属性値を変更してください。
+[assembly: AssemblyTitle("AssistRB")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("AssistRB")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから
+// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、
+// その型の ComVisible 属性を true に設定してください。
+[assembly: ComVisible(false)]
+
+//ローカライズ可能なアプリケーションのビルドを開始するには、
+//.csproj ファイルの <UICulture>CultureYouAreCodingWith</UICulture> を
+//<PropertyGroup> 内部で設定します。たとえば、
+//ソース ファイルで英語を使用している場合、<UICulture> を en-US に設定します。次に、
+//下の NeutralResourceLanguage 属性のコメントを解除します。下の行の "en-US" を
+//プロジェクト ファイルの UICulture 設定と一致するよう更新します。
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+    ResourceDictionaryLocation.None, //テーマ固有のリソース ディクショナリが置かれている場所
+    //(リソースがページ、
+    //またはアプリケーション リソース ディクショナリに見つからない場合に使用されます)
+    ResourceDictionaryLocation.SourceAssembly //汎用リソース ディクショナリが置かれている場所
+    //(リソースがページ、
+    //アプリケーション、またはいずれのテーマ固有のリソース ディクショナリにも見つからない場合に使用されます)
+)]
+
+
+// アセンブリのバージョン情報は、以下の 4 つの値で構成されています:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を 
+// 既定値にすることができます:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AssistRB/Properties/Resources.Designer.cs b/src/AssistRB/Properties/Resources.Designer.cs
new file mode 100644 (file)
index 0000000..ed33048
--- /dev/null
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     このコードはツールによって生成されました。
+//     ランタイム バージョン:4.0.30319.225
+//
+//     このファイルへの変更は、以下の状況下で不正な動作の原因になったり、
+//     コードが再生成されるときに損失したりします。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace AssistRB.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。
+    /// </summary>
+    // このクラスは StronglyTypedResourceBuilder クラスが ResGen
+    // または Visual Studio のようなツールを使用して自動生成されました。
+    // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に
+    // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AssistRB.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、
+        ///   現在のスレッドの CurrentUICulture プロパティをオーバーライドします。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}
diff --git a/src/AssistRB/Properties/Resources.resx b/src/AssistRB/Properties/Resources.resx
new file mode 100644 (file)
index 0000000..af7dbeb
--- /dev/null
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
diff --git a/src/AssistRB/Properties/Settings.Designer.cs b/src/AssistRB/Properties/Settings.Designer.cs
new file mode 100644 (file)
index 0000000..ca55f76
--- /dev/null
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     このコードはツールによって生成されました。
+//     ランタイム バージョン:4.0.30319.225
+//
+//     このファイルへの変更は、以下の状況下で不正な動作の原因になったり、
+//     コードが再生成されるときに損失したりします。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace AssistRB.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+    }
+}
diff --git a/src/AssistRB/Properties/Settings.settings b/src/AssistRB/Properties/Settings.settings
new file mode 100644 (file)
index 0000000..033d7a5
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+  <Settings />
+</SettingsFile>
\ No newline at end of file
diff --git a/src/AssistRB/RecboxController.cs b/src/AssistRB/RecboxController.cs
new file mode 100644 (file)
index 0000000..01a7a85
--- /dev/null
@@ -0,0 +1,751 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Text.RegularExpressions;
+using System.Net;
+using System.IO;
+using Codeplex.Data;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// リネーム候補のリストのアイテム
+    /// </summary>
+    public class RenameItem
+    {
+        public string Id { get; set; }
+        public string OldName { get; set; }
+        public string NewName { get; set; }
+    }
+
+    /// <summary>
+    /// フォルダ自動振り分けのリストのアイテム
+    /// </summary>
+    public class DispatchItem
+    {
+        public string Id { get; set; }
+        public string Title { get; set; }
+        public string Parent { get; set; }
+        public string Series { get; set; }
+    }
+
+    /// <summary>
+    /// 特定フォルダへの振り分けのリストのアイテム
+    /// </summary>
+    public class DustboxItem
+    {
+        public string Id { get; set; }
+        public string Title { get; set; }
+        public string Parent { get; set; }
+    }
+
+    /// <summary>
+    /// RECBOXとの主な通信を行う
+    /// </summary>
+    class RecboxController
+    {
+        // スレッド停止のためのフラグ
+        private volatile bool stop = false;
+
+        // 要求されている処理の種類
+        private enum RequestType
+        {
+            REQUEST_NONE = 0,
+            REQUEST_UPDATE_LIST,
+            REQUEST_RENAME_CHECK,
+            REQUEST_RENAME_EXEC,
+            REQUEST_DISPATCH_CHECK,
+            REQUEST_DISPATCH_EXEC,
+            REQUEST_DUSTBOX_CHECK,
+            REQUEST_DUSTBOX_EXEC,
+            REQUEST_ALLTRANSFER_EXEC,
+            REQUEST_ALLTRANSFER_GETLIST
+        }
+        private RequestType currentRequest;
+        private Object requestTypeLocker = new object();
+        private RequestType CurrentRequest
+        {
+            get
+            {
+                lock (requestTypeLocker)
+                {
+                    return this.currentRequest;
+                }
+            }
+
+            set
+            {
+                lock (requestTypeLocker)
+                {
+                    if (currentRequest != value)
+                    {
+                        currentRequest = value;
+                    }
+                }
+            }
+        }
+
+        // 各機能のパラメータ
+        private string host_url = "";
+        private string renaempattern = "";
+        private bool renameIncludeDir = false;
+        private string dispatchpattern = "";
+        private string dustboxpattern = "";
+        private bool dustboxIncludeDir = false;
+        private string dustboxfolder = "";
+        private string udn = "";
+        private string storage = "";
+
+        // 各種イベント
+        public delegate void RecboxEventHandler();
+        public event RecboxEventHandler RenameChanged;
+        public event RecboxEventHandler AllItemListChanged;
+        public event RecboxEventHandler DispatchChanged;
+        public event RecboxEventHandler DustboxChanged;
+        public event RecboxEventHandler ProcessDone;
+        public event RecboxEventHandler ServerListChanged;
+
+        // 生成されるフィルタリングされたリスト
+        public List<RenameItem> RenameList = new List<RenameItem>();
+        public List<DispatchItem> DispatchList = new List<DispatchItem>();
+        public List<DustboxItem> DustboxList = new List<DustboxItem>();
+        public List<WebLowlayer.ServerList> ServerList = new List<WebLowlayer.ServerList>();
+
+        public RecboxController()
+        {
+        }
+
+        /// <summary>
+        /// スレッドの停止を要求する
+        /// </summary>
+        public void Stop()
+        {
+            this.stop = true;
+        }
+
+        // 処理状況の監視スレッドから更新されます
+        private bool transferProcessing = true;
+        public void ChangeTransferProcessing(bool value)
+        {
+            transferProcessing = value;
+        }
+
+        public void RequestUpdateList(string url)
+        {
+            this.host_url = url;
+            this.CurrentRequest = RequestType.REQUEST_UPDATE_LIST;
+        }
+
+        public void RequestRenameCheck(string pattern, bool includeDir)
+        {
+            this.renaempattern = pattern;
+            this.renameIncludeDir = includeDir;
+            this.CurrentRequest = RequestType.REQUEST_RENAME_CHECK;
+        }
+
+        public void RequestRename(string url)
+        {
+            this.host_url = url;
+            this.CurrentRequest = RequestType.REQUEST_RENAME_EXEC;
+        }
+
+        public void RequestDispatchCheck(string pattern)
+        {
+            this.dispatchpattern = pattern;
+            this.CurrentRequest = RequestType.REQUEST_DISPATCH_CHECK;
+        }
+
+        public void RequestDispatch(string url)
+        {
+            this.host_url = url;
+            this.CurrentRequest = RequestType.REQUEST_DISPATCH_EXEC;
+        }
+
+        public void RequestDustboxCheck(string pattern, bool includeDir)
+        {
+            this.dustboxpattern = pattern;
+            this.dustboxIncludeDir = includeDir;
+            this.CurrentRequest = RequestType.REQUEST_DUSTBOX_CHECK;
+        }
+
+        public void RequestDustbox(string url, string folder)
+        {
+            this.host_url = url;
+            this.dustboxfolder = folder;
+            this.CurrentRequest = RequestType.REQUEST_DUSTBOX_EXEC;
+        }
+
+        public void RequestAllTransfer(string url, string udn, string storage)
+        {
+            this.host_url = url;
+            this.udn = udn;
+            this.storage = storage;
+            this.CurrentRequest = RequestType.REQUEST_ALLTRANSFER_EXEC;
+        }
+
+        public void RequestAllTransferGetList(string url)
+        {
+            this.host_url = url;
+            this.CurrentRequest = RequestType.REQUEST_ALLTRANSFER_GETLIST;
+        }
+
+        public void worker()
+        {
+            while (!this.stop)
+            {
+                if (this.host_url.Length == 0)
+                {
+                    this.CurrentRequest = RequestType.REQUEST_NONE;
+                    continue;
+                }
+
+                TransferStatusChecker.GetInstance().Start();
+
+                if (this.transferProcessing)
+                {
+                    Thread.Sleep(10);
+                    continue;
+                }
+
+                RequestType request = CurrentRequest;
+                switch (request)
+                {
+                    case RequestType.REQUEST_UPDATE_LIST:
+                        {
+                            this.UpdateAllItemList();
+
+                            if (this.AllItemListChanged != null)
+                            {
+                                this.AllItemListChanged();
+                            }
+                            TransferStatusChecker.GetInstance().Stop();
+                        }
+                        break;
+
+                    case RequestType.REQUEST_RENAME_CHECK:
+                        {
+                            RenameList.Clear();
+
+                            CreateFilteredList(
+                                this.renaempattern,
+                                this.renameIncludeDir,
+                                delegate(DlnaItem item, string matchTitle)
+                                {
+                                    matchTitle = matchTitle.Replace("―", "ー");
+                                    if (matchTitle != item.Title)
+                                    {
+                                        RenameItem ren = new RenameItem();
+                                        ren.Id = item.Id;
+                                        ren.OldName = item.Title;
+                                        ren.NewName = matchTitle;
+                                        RenameList.Add(ren);
+                                    }
+                                });
+
+                            ToolLog.GetInstance().Add("リネーム予定リストを更新しました");
+
+                            if (this.RenameChanged != null)
+                            {
+                                this.RenameChanged();
+                            }
+                        }
+                        break;
+
+                    case RequestType.REQUEST_RENAME_EXEC:
+                        {
+                            Rename();
+                        }
+                        break;
+
+                    case RequestType.REQUEST_DISPATCH_CHECK:
+                        {
+                            DispatchList.Clear();
+
+                            CreateFilteredList(
+                            this.dispatchpattern,
+                            false,
+                            delegate(DlnaItem item, string matchTitle)
+                            {
+                                string series = matchTitle;
+                                series = series.Replace("*", "*");
+                                series = series.Replace(":", ":");
+                                series = series.Replace("\\", "¥");
+                                series = series.Replace("\"", "”");
+                                series = series.Replace("<", "<");
+                                series = series.Replace(">", ">");
+                                series = series.Replace("?", "?");
+                                series = series.Replace("|", "|");
+                                series = series.Replace("/", "/");
+
+                                Dictionary<string, string> folderList = DlnaItemList.GetInstance().GetFolderList();
+                                if (folderList.ContainsKey(item.Parent)
+                                    && folderList[item.Parent] != series)
+                                {
+                                    DispatchItem ren = new DispatchItem();
+                                    ren.Id = item.Id;
+                                    ren.Parent = item.Parent;
+                                    ren.Series = series;
+                                    ren.Title = item.Title;
+                                    DispatchList.Add(ren);
+                                }
+                            });
+
+                            ToolLog.GetInstance().Add("移動予定リストを更新しました");
+
+                            if (this.DispatchChanged != null)
+                            {
+                                this.DispatchChanged();
+                            }
+                        }
+                        break;
+
+                    case RequestType.REQUEST_DISPATCH_EXEC:
+                        {
+                            DispatchFolder();
+                        }
+                        break;
+
+                    case RequestType.REQUEST_DUSTBOX_CHECK:
+                        {
+                            DustboxList.Clear();
+                            CreateFilteredList(
+                                this.dustboxpattern,
+                                this.dustboxIncludeDir,
+                                delegate(DlnaItem item, string matchTitle)
+                                {
+                                    if (matchTitle != item.Title)
+                                    {
+                                        DustboxItem ren = new DustboxItem();
+                                        ren.Id = item.Id;
+                                        ren.Parent = item.Parent;
+                                        ren.Title = item.Title;
+                                        DustboxList.Add(ren);
+                                    }
+                                });
+
+                            ToolLog.GetInstance().Add("移動予定リストを更新しました");
+
+                            if (this.DustboxChanged != null)
+                            {
+                                this.DustboxChanged();
+                            }
+                        }
+                        break;
+
+                    case RequestType.REQUEST_DUSTBOX_EXEC:
+                        {
+                            DustboxFolder();
+                        }
+                        break;
+
+                    case RequestType.REQUEST_ALLTRANSFER_EXEC:
+                        {
+                            RenameList.Clear();
+                            foreach (DlnaItem dlnaitem in DlnaItemList.GetInstance().GetList().FindAll(i => i.ClassType != "container"))
+                            {
+                                RenameItem item = new RenameItem();
+                                item.Id = dlnaitem.Id;
+                                item.OldName = dlnaitem.Title;
+                                RenameList.Add(item);
+                            }
+                            AllTransfer();
+                        }
+                        break;
+                    case RequestType.REQUEST_ALLTRANSFER_GETLIST:
+                        {
+                            ServerList = WebLowlayer.GetUdnList(this.host_url);
+                            if (ServerListChanged != null)
+                            {
+                                ServerListChanged();
+                            }
+                        }
+                        break;
+                }
+
+                TransferStatusChecker.GetInstance().Stop();
+
+                // 終了のイベントを発行
+                if (request != RequestType.REQUEST_NONE)
+                {
+                    this.CurrentRequest = RequestType.REQUEST_NONE;
+
+                    if (this.ProcessDone != null)
+                    {
+                        this.ProcessDone();
+                    }
+                }
+
+                Thread.Sleep(10);
+            }
+        }
+
+        /* *************************************************************************
+         * ディスパッチャから呼ばれる高レベル処理
+         */
+        private delegate void CreateFilteredListAddItemDelegate(DlnaItem item, string matchTitle);
+        /// <summary>
+        /// 全件検索の結果をフィルタリングして処理用のリストを作ります
+        /// </summary>
+        /// <param name="pattern"></param>
+        /// <param name="includeDir"></param>
+        /// <param name="addItem"></param>
+        /// <returns></returns>
+        private bool CreateFilteredList(string pattern, bool includeDir, CreateFilteredListAddItemDelegate addItem)
+        {
+            Regex regex;
+            try
+            {
+                regex = new Regex(pattern);
+            }
+            catch (Exception)
+            {
+                ToolLog.GetInstance().Add("正規表現の表記が不正です");
+                return false;
+            }
+
+            foreach (DlnaItem item in DlnaItemList.GetInstance().GetList())
+            {
+                if (item.ClassType != "item" && item.ClassType != "container")
+                {
+                    continue;
+                }
+
+                if (!includeDir && item.ClassType == "container")
+                {
+                    continue;
+                }
+
+                Match match = regex.Match(item.Title);
+                if (match.Success)
+                {
+                    addItem(item, match.Groups["title"].Value);
+                }
+            }
+
+            return true;
+        }
+
+        private void Rename()
+        {
+            foreach (RenameItem item in RenameList)
+            {
+                ToolLog.GetInstance().Add(string.Format(
+                    "{0}を{1}にリネームします", item.OldName, item.NewName));
+
+                WebLowlayer.Rename(host_url, item.Id, item.NewName);
+            }
+
+            ToolLog.GetInstance().Add("リネームが完了しました");
+        }
+
+        private void AllTransfer()
+        {
+            foreach (RenameItem item in RenameList)
+            {
+                while (this.transferProcessing)
+                {
+                    if (this.stop)
+                    {
+                        return;
+                    }
+
+                    Thread.Sleep(10);
+                }
+
+                ToolLog.GetInstance().Add(string.Format(
+                    "{0}を転送します", item.OldName));
+
+                string[] transferitems = { item.Id };
+
+                WebLowlayer.Transfer(
+                    host_url,
+                    udn,
+                    storage,
+                    transferitems);
+
+                UpdateAllItemList();
+            }
+
+            ToolLog.GetInstance().Add("転送が完了しました");
+        }
+
+        private void DispatchFolder()
+        {
+            Dictionary<string, string> folderList = DlnaItemList.GetInstance().GetFolderList();
+
+            while (DispatchList.Count > 0)
+            {
+                string firstparent = "";
+                string series = "";
+                List<string> ids = new List<string>();
+
+                foreach (DispatchItem item in DispatchList)
+                {
+                    if (this.stop)
+                    {
+                        return;
+                    }
+
+                    if (item.Series.Length == 0)
+                    {
+                        continue;
+                    }
+
+                    // 集めている以外のシリーズは次回以降
+                    if (series.Length > 0 && (series != item.Series || firstparent != item.Parent))
+                    {
+                        continue;
+                    }
+
+                    string folderName = folderList[item.Parent];
+                    if (folderName != item.Series)
+                    {
+                        if (ids.Count == 0)
+                        {
+                            series = item.Series;
+                            firstparent = item.Parent;
+                        }
+
+                        ids.Add(item.Id);
+                    }
+
+                    if (ids.Count >= 20)
+                    {
+                        break;
+                    }
+                }
+
+                if (ids.Count > 0)
+                {
+                    string[] idsArray = ids.ToArray();
+
+                    ToolLog.GetInstance().Add(string.Format("{0}を{1}件、移動します", series, idsArray.Count()));
+
+                    string parent = "";
+                    if (folderList.ContainsValue(series))
+                    {
+                        parent = folderList.First(a => a.Value == series).Key;
+                    }
+                    else
+                    {
+                        // フォルダがないので作成する
+                        ToolLog.GetInstance().Add(string.Format("フォルダを作成します"));
+                        if (!WebLowlayer.MkDir(host_url, firstparent, series))
+                        {
+                            ToolLog.GetInstance().Add(string.Format("フォルダの作成に失敗しました({0})", series));
+                        }
+                        else
+                        {
+                            ToolLog.GetInstance().Add(string.Format("フォルダを作成しました"));
+
+                            DlnaItemList.GetInstance().Invalidate(firstparent);
+                            UpdateAllItemList(true);
+
+                            parent = folderList.First(a => a.Value == series).Key;
+                        }
+                    }
+
+                    if (parent.Length > 0)
+                    {
+                        WebLowlayer.Move(host_url, parent, idsArray);
+
+                        DlnaItemList.GetInstance().Invalidate(parent);
+                        DlnaItemList.GetInstance().Invalidate(firstparent);
+                        UpdateAllItemList(true);
+
+                        ToolLog.GetInstance().Add("移動が完了しました");
+                    }
+
+                    // 失敗しても取り除く
+                    foreach (string id in ids)
+                    {
+                        DispatchList.RemoveAll(a => a.Id == id);
+                    }
+                }
+            }
+        }
+
+        private void DustboxFolder()
+        {
+            Dictionary<string, string> folderList = DlnaItemList.GetInstance().GetFolderList();
+
+            while (DustboxList.Count > 0)
+            {
+                string firstparent = "";
+                List<string> ids = new List<string>();
+
+                foreach (DustboxItem item in DustboxList)
+                {
+                    if (this.stop)
+                    {
+                        return;
+                    }
+
+                    // 同じ親以外は次回以降に
+                    if (ids.Count != 0 && firstparent != item.Parent)
+                    {
+                        continue;
+                    }
+
+                    string folderName = folderList[item.Parent];
+                    if (ids.Count == 0)
+                    {
+                        firstparent = item.Parent;
+                    }
+
+                    ids.Add(item.Id);
+
+                    if (ids.Count >= 20)
+                    {
+                        break;
+                    }
+                }
+
+                if (ids.Count > 0)
+                {
+                    string[] idsArray = ids.ToArray();
+
+                    ToolLog.GetInstance().Add(string.Format("{0}件、移動します", idsArray.Count()));
+
+                    string parent = "";
+                    if (folderList.ContainsValue(dustboxfolder))
+                    {
+                        parent = folderList.First(a => a.Value == dustboxfolder).Key;
+                    }
+                    else
+                    {
+                        ToolLog.GetInstance().Add(string.Format("フォルダを作成します"));
+                        if (!WebLowlayer.MkDir(host_url, firstparent, dustboxfolder))
+                        {
+                            ToolLog.GetInstance().Add(string.Format("フォルダの作成に失敗しました({0})", dustboxfolder));
+                        }
+                        else
+                        {
+                            ToolLog.GetInstance().Add(string.Format("フォルダを作成しました"));
+
+                            DlnaItemList.GetInstance().Invalidate(firstparent);
+                            UpdateAllItemList();
+
+                            parent = folderList.First(a => a.Value == dustboxfolder).Key;
+                        }
+                    }
+
+                    if (parent.Length > 0)
+                    {
+                        WebLowlayer.Move(host_url, parent, idsArray);
+
+                        DlnaItemList.GetInstance().Invalidate(parent);
+                        DlnaItemList.GetInstance().Invalidate(firstparent);
+                        UpdateAllItemList();
+
+                        ToolLog.GetInstance().Add("移動が完了しました");
+                    }
+
+                    // 失敗しても取り除く
+                    foreach (string id in ids)
+                    {
+                        DustboxList.RemoveAll(a => a.Id == id);
+                    }
+                }
+            }
+        }
+
+        private void UpdateAllItemList(bool fastFlag = false)
+        {
+            Thread.Sleep(3000);
+            while (this.transferProcessing)
+            {
+                if (this.stop)
+                {
+                    return;
+                }
+
+                Thread.Sleep(10);
+            }
+
+            DlnaItemList dlist = DlnaItemList.GetInstance();
+            List<DlnaItem> newList =
+                this.UpdateListSub(
+                dlist.GetFolderList(),
+                dlist.GetInvalidList(),
+                dlist.GetList(),
+                fastFlag);
+            DlnaItemList.GetInstance().UpdateList(newList);
+        }
+
+        private List<DlnaItem> UpdateListSub(
+            Dictionary<string, string> afolderDictionary,
+            List<string> ainvalidFolder,
+            List<DlnaItem> prevList,
+            bool fastFlag = false)
+        {
+            List<DlnaItem> resultList = new List<DlnaItem>(prevList);
+            Dictionary<string, string> folderDictionary = new Dictionary<string, string>(afolderDictionary);
+            List<string> invalidFolder = new List<string>(ainvalidFolder);
+
+            ToolLog.GetInstance().Add("リストを更新します");
+
+            // フォルダ辞書にROOTは追加しておく
+            if (!folderDictionary.ContainsKey("FS-Folder"))
+            {
+                folderDictionary.Add("FS-Folder", "ROOT");
+            }
+
+            for (int i = 0; i < invalidFolder.Count; ++i)
+            {
+                // 停止要求がある場合は処理をやめる
+                if (this.stop)
+                {
+                    break;
+                }
+
+                string target = invalidFolder[i];
+
+                // これから探そうとしている階層の前回の結果を破棄する
+                resultList.RemoveAll(a => a.Parent == target);
+
+                ToolLog.GetInstance().Add(string.Format(
+                    "フォルダ {0}/{1} {2} を検索中です",
+                    i + 1, invalidFolder.Count, folderDictionary[target]));
+
+                List<DlnaItem> newitems = WebLowlayer.UpdateFolder(host_url, target);
+                foreach (DlnaItem item in newitems)
+                {
+                    if (item.ClassType == "container")
+                    {
+                        // infoは操作できるものではないので除外する
+                        if (item.Parent == "FS-Folder" && item.Title == "info")
+                        {
+                            continue;
+                        }
+
+                        // フォルダ辞書にある場合は破棄して登録しなおす
+                        if (folderDictionary.ContainsKey(item.Id))
+                        {
+                            folderDictionary.Remove(item.Id);
+                        }
+                        folderDictionary.Add(item.Id, item.Title); // 表示のため仮登録
+
+                        // 見つけたフォルダ以下も取得しなおす
+                        if (!invalidFolder.Contains(item.Id))
+                        {
+                            if (!fastFlag)
+                            {
+                                invalidFolder.Add(item.Id);
+                            }
+                        }
+                    }
+
+                    resultList.Add(item);
+                }
+            }
+
+            ToolLog.GetInstance().Add("リストを更新しました");
+
+            return resultList;
+        }
+
+    }
+}
diff --git a/src/AssistRB/SortableBindingList.cs b/src/AssistRB/SortableBindingList.cs
new file mode 100644 (file)
index 0000000..65a9069
--- /dev/null
@@ -0,0 +1,151 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel;
+using System.Linq.Expressions;
+
+namespace AssistRB
+{
+    // http://www.codeproject.com/KB/linq/bindinglist_sortable.aspx
+    public class SortableBindingList<T> : BindingList<T>
+    {
+
+        // reference to the list provided at the time of instantiation
+        List<T> originalList;
+        ListSortDirection sortDirection;
+        PropertyDescriptor sortProperty;
+
+        // function that refereshes the contents
+        // of the base classes collection of elements
+        Action<SortableBindingList<T>, List<T>>
+                       populateBaseList = (a, b) => a.ResetItems(b);
+
+        // a cache of functions that perform the sorting
+        // for a given type, property, and sort direction
+        static Dictionary<string, Func<List<T>, IEnumerable<T>>>
+           cachedOrderByExpressions = new Dictionary<string, Func<List<T>,
+                                                     IEnumerable<T>>>();
+
+        public SortableBindingList()
+        {
+            originalList = new List<T>();
+        }
+
+        public SortableBindingList(IEnumerable<T> enumerable)
+        {
+            originalList = enumerable.ToList();
+            populateBaseList(this, originalList);
+        }
+
+        public SortableBindingList(List<T> list)
+        {
+            originalList = list;
+            populateBaseList(this, originalList);
+        }
+
+        protected override void ApplySortCore(PropertyDescriptor prop,
+                                ListSortDirection direction)
+        {
+            /*
+             Look for an appropriate sort method in the cache if not found .
+             Call CreateOrderByMethod to create one. 
+             Apply it to the original list.
+             Notify any bound controls that the sort has been applied.
+             */
+
+            sortProperty = prop;
+
+            var orderByMethodName = sortDirection ==
+                ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
+            var cacheKey = typeof(T).GUID + prop.Name + orderByMethodName;
+
+            if (!cachedOrderByExpressions.ContainsKey(cacheKey))
+            {
+                CreateOrderByMethod(prop, orderByMethodName, cacheKey);
+            }
+
+            ResetItems(cachedOrderByExpressions[cacheKey](originalList).ToList());
+            ResetBindings();
+            sortDirection = sortDirection == ListSortDirection.Ascending ?
+                            ListSortDirection.Descending : ListSortDirection.Ascending;
+        }
+
+
+        private void CreateOrderByMethod(PropertyDescriptor prop,
+                     string orderByMethodName, string cacheKey)
+        {
+
+            /*
+             Create a generic method implementation for IEnumerable<T>.
+             Cache it.
+            */
+
+            var sourceParameter = Expression.Parameter(typeof(List<T>), "source");
+            var lambdaParameter = Expression.Parameter(typeof(T), "lambdaParameter");
+            var accesedMember = typeof(T).GetProperty(prop.Name);
+            var propertySelectorLambda =
+                Expression.Lambda(Expression.MakeMemberAccess(lambdaParameter,
+                                  accesedMember), lambdaParameter);
+            var orderByMethod = typeof(Enumerable).GetMethods()
+                                          .Where(a => a.Name == orderByMethodName &&
+                                                       a.GetParameters().Length == 2)
+                                          .Single()
+                                          .MakeGenericMethod(typeof(T), prop.PropertyType);
+
+            var orderByExpression = Expression.Lambda<Func<List<T>, IEnumerable<T>>>(
+                                        Expression.Call(orderByMethod,
+                                                new Expression[] { sourceParameter, 
+                                                               propertySelectorLambda }),
+                                                sourceParameter);
+
+            cachedOrderByExpressions.Add(cacheKey, orderByExpression.Compile());
+        }
+
+        protected override void RemoveSortCore()
+        {
+            ResetItems(originalList);
+        }
+
+        private void ResetItems(List<T> items)
+        {
+
+            base.ClearItems();
+
+            for (int i = 0; i < items.Count; i++)
+            {
+                base.InsertItem(i, items[i]);
+            }
+        }
+
+        protected override bool SupportsSortingCore
+        {
+            get
+            {
+                // indeed we do
+                return true;
+            }
+        }
+
+        protected override ListSortDirection SortDirectionCore
+        {
+            get
+            {
+                return sortDirection;
+            }
+        }
+
+        protected override PropertyDescriptor SortPropertyCore
+        {
+            get
+            {
+                return sortProperty;
+            }
+        }
+
+        protected override void OnListChanged(ListChangedEventArgs e)
+        {
+            originalList = base.Items.ToList();
+        }
+    }
+}
diff --git a/src/AssistRB/ToolLog.cs b/src/AssistRB/ToolLog.cs
new file mode 100644 (file)
index 0000000..5ecab2d
--- /dev/null
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// ツールのログ表示のためのクラスです
+    /// 破棄できません。
+    /// </summary>
+    public class ToolLog : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        private Object messageLock = new Object();
+
+        const int MaxLogLine = 100;
+
+        private void OnPropertyChanged(string propertyName)
+        {
+            if (this.PropertyChanged != null)
+            {
+                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+            }
+        }
+
+        private static ToolLog toolLog = null;
+
+        public static ToolLog GetInstance()
+        {
+            if (toolLog == null)
+            {
+                toolLog = new ToolLog();
+            }
+            return toolLog;
+        }
+
+        public void Add(string message)
+        {
+            this.AddOutputWindow(message);
+        }
+
+        private string statusMessage = "";
+        public string StatusMessage
+        {
+            get
+            {
+                return statusMessage;
+            }
+            set
+            {
+                if (statusMessage != value)
+                {
+                    statusMessage = value;
+                    this.OnPropertyChanged("StatusMessage");
+                }
+            }
+        }
+
+        private Queue<string> outputWindowBuffer = new Queue<string>();
+        public string OutputWindowBuffer
+        {
+            get
+            {
+                string allMessage = "";
+                foreach (string s in this.outputWindowBuffer)
+                {
+                    allMessage += s;
+                }
+                return allMessage;
+            }
+        }
+
+        public void AddOutputWindow(string message)
+        {
+            DateTime tm = new DateTime(DateTime.Now.Ticks);
+
+            string timemessage =
+                string.Format(
+                "{0:d4}/{1:d2}/{2:d2} {3:d2}:{4:d2}:{5:d2} ",
+                tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second)
+                + message
+                + "\n";
+
+            lock (messageLock)
+            {
+                this.StatusMessage = message;
+
+                if (this.outputWindowBuffer.Count > MaxLogLine)
+                {
+                    this.outputWindowBuffer.Dequeue();
+                }
+                this.outputWindowBuffer.Enqueue(timemessage);
+                this.OnPropertyChanged("OutputWindowBuffer");
+            }
+        }
+    }
+}
diff --git a/src/AssistRB/TransferStatusChecker.cs b/src/AssistRB/TransferStatusChecker.cs
new file mode 100644 (file)
index 0000000..a17d08d
--- /dev/null
@@ -0,0 +1,277 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Net;
+using System.IO;
+using Codeplex.Data;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// 定期的に通信し、転送のステータスを監視します
+    /// </summary>
+    class TransferStatusChecker
+    {
+        private static TransferStatusChecker instance = null;
+
+        public delegate void TransferStatusChangedDelegate(bool processing);
+
+        /// <summary>
+        /// 処理中と完了が切り替わったときのイベントです
+        /// </summary>
+        public event TransferStatusChangedDelegate TransferStatusChanged;
+
+        private volatile bool stop = false;
+        private volatile bool wait = true;
+        private string host_url = "";
+        private bool isProcessing = true;
+
+        private int numTotal = 0;
+        private int numDone = 0;
+        private int numError = 0;
+        private int numWait = 0;
+        private int numTransferring = 0;
+        private string nameTransfererring = "";
+        private int percentTransferring = 0;
+
+        /// <summary>
+        /// インスタンスを取得します
+        /// </summary>
+        /// <returns>TransferStatusCheckerを返します</returns>
+        public static TransferStatusChecker GetInstance()
+        {
+            if (instance == null)
+            {
+                instance = new TransferStatusChecker();
+            }
+
+            return instance;
+        }
+
+        private TransferStatusChecker()
+        {
+            // GetInstance()以外での生成を抑制
+        }
+
+        /// <summary>
+        /// ホストを設定します
+        /// </summary>
+        /// <param name="url">http://xxxxx:xxxxの形式で指定します。末尾に/は不要です。</param>
+        public void Initializer(string url)
+        {
+            this.host_url = url;
+        }
+
+        /// <summary>
+        /// スレッドで起動するメソッドです
+        /// </summary>
+        public void worker()
+        {
+            while (!this.stop)
+            {
+                if (host_url.Length > 0)
+                {
+                    this.Update();
+                }
+
+                Thread.Sleep(5 * 1000); // 5s
+            }
+        }
+
+        /// <summary>
+        /// 処理中かどうかのフラグです
+        /// </summary>
+        public bool IsProcessing
+        {
+            get
+            {
+                return this.isProcessing;
+            }
+
+            private set
+            {
+                //if (this.isProcessing != value)
+                {
+                    this.isProcessing = value;
+
+                    if (this.isProcessing)
+                    {
+                        ToolLog.GetInstance().Add(
+                            string.Format(
+                            "処理中です。ファイル {0} {1}%。Done:{2} Error:{3} / Total:{4}",
+                            this.nameTransfererring,
+                            this.percentTransferring,
+                            this.numDone,
+                            this.numError,
+                            this.numTotal
+                            ));
+                    }
+                    else
+                    {
+                        ToolLog.GetInstance().Add("処理を完了しました");
+                    }
+
+                    if (this.TransferStatusChanged != null)
+                    {
+                        this.TransferStatusChanged(this.isProcessing);
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 現在の状態を取得します
+        /// </summary>
+        private void Update()
+        {
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/transfer_status";
+
+            WebRequest request = WebRequest.Create(uri);
+            WebResponse responce;
+            try
+            {
+                responce = request.GetResponse();
+            }
+            catch (Exception)
+            {
+                return;
+            }
+            Stream stream = responce.GetResponseStream();
+            if (stream == null)
+            {
+                responce.Close();
+                return;
+            }
+
+            var json = DynamicJson.Parse(stream);
+            stream.Close();
+            responce.Close();
+
+            bool error = false;
+            bool processing = false;
+
+            numTotal = 0;
+            numDone = 0;
+            numError = 0;
+            numWait = 0;
+            numTransferring = 0;
+            nameTransfererring = "";
+            percentTransferring = 0;
+
+            if (json != null)
+            {
+                var result = json.results;
+                if (result != null && result.IsArray)
+                {
+                    foreach (var item in result)
+                    {
+                        if (item.IsDefined("status"))
+                        {
+                            ++numTotal;
+
+                            switch (item.status as string)
+                            {
+                                case "waiting":
+                                    ++numWait;
+                                    processing = true;
+                                    break;
+                                case "transferring":
+                                    ++numTransferring;
+                                    if (item.IsDefined("title"))
+                                    {
+                                        nameTransfererring = item.title;
+                                    }
+                                    else
+                                    {
+                                        nameTransfererring = "unknown";
+                                    }
+
+                                    if (item.IsDefined("transferred") && item.IsDefined("total"))
+                                    {
+                                        double total = 0;
+                                        double transferred = 0;
+                                        try
+                                        {
+                                            total = item.total;
+                                            transferred = item.transferred;
+                                        }
+                                        catch
+                                        {
+                                        }
+                                        if (total == 0)
+                                        {
+                                            percentTransferring = 100;
+                                        }
+                                        else
+                                        {
+                                            percentTransferring = (int)(transferred / total * 100);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        percentTransferring = 0;
+                                    }
+
+                                    processing = true;
+                                    break;
+                                case "cancelled":
+                                    break;
+                                case "stopped":
+                                    break;
+                                case "error":
+                                    ++numError;
+                                    error = true;
+                                    break;
+                                case "unsupported":
+                                    ++numError;
+                                    error = true;
+                                    break;
+                                case "done":
+                                    ++numDone;
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (processing)
+            {
+                this.IsProcessing = true;
+            }
+            else
+            {
+                this.IsProcessing = false;
+            }
+        }
+
+        /// <summary>
+        /// 処理の終了を要求します
+        /// </summary>
+        public void Terminate()
+        {
+            this.stop = true;
+        }
+
+        /// <summary>
+        /// 監視を開始します。
+        /// </summary>
+        public void Start()
+        {
+            this.wait = false;
+        }
+
+        /// <summary>
+        /// 監視を中断します。
+        /// </summary>
+        public void Stop()
+        {
+            this.wait = true;
+        }
+    }
+}
diff --git a/src/AssistRB/WebLowLayer.cs b/src/AssistRB/WebLowLayer.cs
new file mode 100644 (file)
index 0000000..52a1de9
--- /dev/null
@@ -0,0 +1,400 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Text.RegularExpressions;
+using System.Net;
+using System.IO;
+using Codeplex.Data;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// 低レベル処理
+    /// 1関数で完結したstatic関数群です
+    /// </summary>
+    public class WebLowlayer
+    {
+        public class ServerList
+        {
+            public string udn { get; set; }
+            public string friendlyName { get; set; }
+
+            public class StorageList
+            {
+                public string id { get; set; }
+                public string friendlyName { get; set; }
+            };
+
+            public List<StorageList> strageList = new List<StorageList>();
+            public List<StorageList> storageList
+            {
+                get
+                {
+                    return strageList;
+                }
+            }
+        };
+
+        /// <summary>
+        /// フォルダの内容を取得する
+        /// </summary>
+        /// <param name="target">フォルダのID</param>
+        /// <returns>フォルダの中身のリスト</returns>
+        public static List<DlnaItem> UpdateFolder(string host_url, string target)
+        {
+            List<DlnaItem> items = new List<DlnaItem>();
+
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/browse?";
+
+            int start = 0;
+            while (true)
+            {
+                string queryFormat = @"{0}id={1}&starting_index={2}&requested_count={3}";
+                string query = string.Format(queryFormat, uri, target, start, 100);
+
+                System.Console.WriteLine(query);
+                WebRequest request = WebRequest.Create(query);
+                WebResponse responce = request.GetResponse();
+                Stream stream = responce.GetResponseStream();
+                if (stream == null)
+                {
+                    responce.Close();
+                    break;
+                }
+
+                var json = DynamicJson.Parse(stream);
+                stream.Close();
+                responce.Close();
+
+                if (json != null)
+                {
+                    var result = json.results;
+                    if (result != null && result.IsArray)
+                    {
+                        foreach (var item in result)
+                        {
+                            if (item.IsDefined("id") && item.IsDefined("title"))
+                            {
+                                DlnaItem ditem = new DlnaItem();
+                                ditem.Parent = target;
+                                ditem.Id = item.id;
+                                ditem.Title = item.title;
+                                if (item.IsDefined("size"))
+                                {
+                                    var size = item.size;
+                                    ditem.Size = (Int64)size;
+                                }
+                                if (item.IsDefined("date"))
+                                {
+                                    ditem.Date = item.date;
+                                }
+
+                                string exp =
+                                    @"^(.*≫)*([二]|(二)|(字)|\[(HV|ニ|新|無)\])?"
+                                    + @"(?<title>[^#]+[^ ]?)"
+                                    + @"[ ]*#[0-9]+(.?#[0-9]+)?"
+                                    + @"[ ]*(新|終|END|(完))?$";
+                                Regex regex = new Regex(exp);
+                                Match match = regex.Match(ditem.Title);
+                                if (match.Success)
+                                {
+                                    ditem.Series = match.Groups["title"].Value;
+                                    ditem.Series = ditem.Series.Replace(":", ":");
+                                }
+                                else
+                                {
+                                    ditem.Series = "";
+                                }
+
+                                ditem.ClassType = item["class"];
+
+                                items.Add(ditem);
+                            }
+                        }
+                    }
+
+                    if (!json.IsDefined("startingIndex")
+                     && !json.IsDefined("totalMatches")
+                     && !json.IsDefined("numberReturned"))
+                    {
+                        break;
+                    }
+
+                    var si = json.startingIndex;
+                    int startingIndex = (int)si;
+                    var tm = json.totalMatches;
+                    int totalMatches = (int)tm;
+                    var nr = json.numberReturned;
+                    int numberReturned = (int)nr;
+
+                    if (start >= startingIndex + numberReturned)
+                    {
+                        break;
+                    }
+                    start = startingIndex + numberReturned;
+
+                    if (start >= totalMatches)
+                    {
+                        break;
+                    }
+                }
+
+            }
+            return items;
+        }
+
+        /// <summary>
+        /// ディレクトリを作ります
+        /// </summary>
+        /// <param name="parent">親ディレクトリのID</param>
+        /// <param name="name">ディレクトリ名</param>
+        /// <returns>成功すればtrueを返す</returns>
+        public static bool MkDir(string host_url, string parent, string name)
+        {
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/make_directory";
+
+            WebClient wc = new WebClient();
+            System.Collections.Specialized.NameValueCollection ps =
+                new System.Collections.Specialized.NameValueCollection();
+            ps.Add("parent_id", parent);
+            ps.Add("name", name);
+            try
+            {
+                byte[] resdata = wc.UploadValues(uri, ps);
+            }
+            catch (Exception e)
+            {
+                if (e is WebException)
+                {
+                    WebException we = e as WebException;
+                }
+                return false;
+            }
+            finally
+            {
+                wc.Dispose();
+            }
+
+            DlnaItemList.GetInstance().Invalidate(parent);
+
+            return true;
+        }
+
+        /// <summary>
+        /// ファイルを移動します(ネットワーク転送ではなくコンテンツ移動です)
+        /// 移動元のアイテムは同じディレクトリに存在していないとダメなようです
+        /// 移動自体は非同期処理です
+        /// </summary>
+        /// <param name="parent">移動先のディレクトリのID</param>
+        /// <param name="items">移動するアイテム</param>
+        /// <returns>即時エラーが返ってこなければtrueを返します</returns>
+        public static bool Move(string host_url, string parent, string[] items)
+        {
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/move";
+
+            WebClient wc = new WebClient();
+            System.Collections.Specialized.NameValueCollection ps =
+                new System.Collections.Specialized.NameValueCollection();
+
+            string src_ids = string.Join(",", items);
+
+            ps.Add("src_ids", src_ids);
+            ps.Add("dst_id", parent);
+            try
+            {
+                byte[] resdata = wc.UploadValues(uri, ps);
+            }
+            catch (Exception e)
+            {
+                if (e is WebException)
+                {
+                    WebException we = e as WebException;
+                }
+                return false;
+            }
+            finally
+            {
+                wc.Dispose();
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// ファイルを移動します(ネットワーク転送です)
+        /// 移動自体は非同期処理です
+        /// </summary>
+        /// <param name="items">移動するアイテム</param>
+        /// <returns>即時エラーが返ってこなければtrueを返します</returns>
+        public static bool Transfer(string host_url, string dst_udn, string dst_id, string[] items)
+        {
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/transfer";
+
+            WebClient wc = new WebClient();
+            System.Collections.Specialized.NameValueCollection ps =
+                new System.Collections.Specialized.NameValueCollection();
+
+            string src_ids = string.Join(",", items);
+
+            ps.Add("src_ids", src_ids);
+            ps.Add("dst_udn", dst_udn);
+            ps.Add("dst_id", dst_id);
+            try
+            {
+                byte[] resdata = wc.UploadValues(uri, ps);
+            }
+            catch (Exception e)
+            {
+                if (e is WebException)
+                {
+                    WebException we = e as WebException;
+                }
+                return false;
+            }
+            finally
+            {
+                wc.Dispose();
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// 名前を変更します
+        /// </summary>
+        /// <param name="id">アイテムのID</param>
+        /// <param name="name">変更後の名前</param>
+        /// <returns>変更出来ればtrueを返します</returns>
+        public static bool Rename(string host_url, string id, string name)
+        {
+            string tool_api_url = host_url + "/dms/transfer_tool/api";
+            string uri = tool_api_url + "/rename";
+
+            WebClient wc = new WebClient();
+            System.Collections.Specialized.NameValueCollection ps =
+                new System.Collections.Specialized.NameValueCollection();
+
+            ps.Add("id", id);
+            ps.Add("new_name", name);
+            try
+            {
+                byte[] resdata = wc.UploadValues(uri, ps);
+            }
+            catch (Exception e)
+            {
+                if (e is WebException)
+                {
+                    WebException we = e as WebException;
+                }
+                return false;
+            }
+            finally
+            {
+                wc.Dispose();
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// </summary>
+        public static List<ServerList> GetUdnList(string host_url)
+        {
+            List<ServerList> serverList = new List<ServerList>();
+
+            {
+                string tool_api_url = host_url + "/dms/transfer_tool/api";
+                string uri = tool_api_url + "/dms_list";
+
+                string query = uri;
+
+                WebRequest request = WebRequest.Create(query);
+                WebResponse responce = request.GetResponse();
+                Stream stream = responce.GetResponseStream();
+                if (stream == null)
+                {
+                    responce.Close();
+                    return null;
+                }
+
+                var json = DynamicJson.Parse(stream);
+                stream.Close();
+                responce.Close();
+
+                if (json != null)
+                {
+                    var result = json.results;
+                    if (result != null && result.IsArray)
+                    {
+                        foreach (var item in result)
+                        {
+                            if (item.IsDefined("udn") && item.IsDefined("friendlyName"))
+                            {
+                                ServerList server = new ServerList();
+                                server.udn = item.udn;
+                                server.friendlyName = item.friendlyName;
+                                serverList.Add(server);
+                            }
+                        }
+                    }
+                }
+            }
+
+            foreach (ServerList server in serverList)
+            {
+                string tool_api_url = host_url + "/dms/transfer_tool/api";
+                string uri = tool_api_url + "/rec_dest_list";
+
+                WebClient wc = new WebClient();
+                System.Collections.Specialized.NameValueCollection ps =
+                    new System.Collections.Specialized.NameValueCollection();
+
+                ps.Add("udn", server.udn);
+                try
+                {
+                    byte[] resdata = wc.UploadValues(uri, ps);
+
+                    Stream stream = new MemoryStream(resdata);
+                    if (stream == null)
+                    {
+                        wc.Dispose();
+                        continue;
+                    }
+
+                    var json = DynamicJson.Parse(stream);
+                    stream.Close();
+
+                    if (json != null)
+                    {
+                        var result = json.results;
+                        if (result != null && result.IsArray)
+                        {
+                            foreach (var item in result)
+                            {
+                                if (item.IsDefined("id") && item.IsDefined("friendlyName"))
+                                {
+                                    ServerList.StorageList storage = new ServerList.StorageList();
+                                    storage.id = item.id;
+                                    storage.friendlyName = item.friendlyName;
+                                    server.strageList.Add(storage);
+                                }
+                            }
+                        }
+                    }
+                }
+                catch (Exception)
+                {
+                }
+                wc.Dispose();
+            }
+
+            return serverList;
+        }
+    }
+}
diff --git a/src/AssistRB/WindowConnection.xaml b/src/AssistRB/WindowConnection.xaml
new file mode 100644 (file)
index 0000000..4fd70c0
--- /dev/null
@@ -0,0 +1,20 @@
+<Window x:Class="AssistRB.WindowConnection"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        Title="接続選択" Height="162" Width="441">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="72*" />
+            <RowDefinition Height="65*" />
+        </Grid.RowDefinitions>
+        <TextBlock Height="56" HorizontalAlignment="Left" Margin="2,0,0,0" Name="textBlock1" Text="" VerticalAlignment="Top" Width="412" Grid.Row="1" />
+        <ComboBox Height="24" HorizontalAlignment="Left" Name="comboBox1" VerticalAlignment="Top" Width="412" Margin="2,0,0,0">
+            <ComboBox.ItemTemplate>
+                <DataTemplate>
+                    <TextBlock Text="{Binding Name}"/>
+                </DataTemplate>
+            </ComboBox.ItemTemplate>
+        </ComboBox>
+        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="337,30,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
+    </Grid>
+</Window>
diff --git a/src/AssistRB/WindowConnection.xaml.cs b/src/AssistRB/WindowConnection.xaml.cs
new file mode 100644 (file)
index 0000000..d3a2d2d
--- /dev/null
@@ -0,0 +1,160 @@
+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;
+using System.Threading;
+using System.Threading.Tasks;
+using System.ComponentModel;
+using System.Net;
+
+namespace AssistRB
+{
+    /// <summary>
+    /// WindowConnection.xaml の相互作用ロジック
+    /// </summary>
+    public partial class WindowConnection : Window
+    {
+        List<FindRecbox.RecboxInfo> recboxList = new List<FindRecbox.RecboxInfo>();
+        BackgroundWorker findWorker = new BackgroundWorker();
+
+        public WindowConnection()
+        {
+            InitializeComponent();
+
+            findWorker.DoWork += new DoWorkEventHandler(findDoWork);
+            findWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(findCompleted);
+            findWorker.RunWorkerAsync();
+
+            button1.IsEnabled = false;
+            WriteLog("Recboxを検索しています");
+        }
+
+        private void findDoWork(object sender, DoWorkEventArgs e)
+        {
+            FindRecbox findrecbox = new FindRecbox();
+            findrecbox.Search();
+            recboxList = findrecbox.GetRecboxList();
+        }
+
+        private void findCompleted(object sender, RunWorkerCompletedEventArgs e)
+        {
+            comboBox1.ItemsSource = recboxList;
+            if (recboxList.Count == 0)
+            {
+                comboBox1.SelectedIndex = -1;
+                WriteLog("Recboxが見つかりませんでした");
+            }
+            else
+            {
+                comboBox1.SelectedIndex = 0;
+                button1.IsEnabled = true;
+                WriteLog("Recboxが見つかりました");
+            }
+
+            findWorker.Dispose();
+        }
+
+        private object lockObject = new object();
+        private bool hasError;
+        private System.Threading.Timer timer;
+
+        private void button1_Click(object sender, RoutedEventArgs e)
+        {
+            button1.IsEnabled = false;
+
+            targetRecbox = comboBox1.SelectedItem as FindRecbox.RecboxInfo;
+            initializeComplete = false;
+
+            hasError = false;
+
+            Task task = new Task(() =>
+            {
+                {
+                    WriteLog("接続を開始します\n");
+
+                    if (targetRecbox == null)
+                    {
+                        WriteLog("接続先が選択されていません。ツールを終了してください。\n");
+                        return;
+                    }
+
+                    try
+                    {
+                        WebClient web = new WebClient();
+                        byte[] recv = web.DownloadData("http://" + targetRecbox.Address);
+                    WriteLog("トップ画面を取得しました\n");
+                    }
+                    catch
+                    {
+                        hasError = true;
+                    }
+                }
+
+                if (!hasError)
+                {
+                    try
+                    {
+                        WebClient web = new WebClient();
+                        byte[] recv = web.DownloadData("http://" + targetRecbox.Address + "/start/link.cgi");
+                    WriteLog("コンテンツ画面に移動します\n");
+
+                    WriteLog("接続が完了しました\n");
+                    }
+                    catch
+                    {
+                        hasError = true;
+                    }
+
+                    initializeComplete = true;
+
+                    Finished();
+                }
+            });
+
+            task.Start();
+        }
+
+        private FindRecbox.RecboxInfo targetRecbox;
+        private bool initializeComplete = false;
+
+        private void WriteLog(string s)
+        {
+            textBlock1.Dispatcher.Invoke(new WriteLogDelegate(WriteLogFunc), s);
+        }
+
+        private delegate void WriteLogDelegate(string s);
+        private void WriteLogFunc(string s)
+        {
+            textBlock1.Text = s;
+        }
+
+        private void Finished()
+        {
+            this.Dispatcher.Invoke(new FinishedDelegate(FinishedFunc));
+        }
+
+        private delegate void FinishedDelegate();
+        private void FinishedFunc()
+        {
+            this.Close();
+        }
+
+        public FindRecbox.RecboxInfo GetTargetRecbox()
+        {
+            if (initializeComplete)
+            {
+                return targetRecbox;
+            }
+
+            return null;
+        }
+    }
+}
diff --git a/src/AssistRB/app.config b/src/AssistRB/app.config
new file mode 100644 (file)
index 0000000..e365603
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>