OSDN Git Service

a34ae2416fab9b81b10fdaaec415590fbf05f01e
[wptscs/wpts.git] / Wptscs / Utilities / FormUtils.cs
1 // ================================================================================================
2 // <summary>
3 //      フォーム処理に関するユーティリティクラスソース。</summary>
4 //
5 // <copyright file="FormUtils.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Wptscs.Utilities
12 {
13     using System;
14     using System.Collections.Generic;
15     using System.IO;
16     using System.Reflection;
17     using System.Windows.Forms;
18     using Honememo.Models;
19
20     // ※ プロパティを含むので、そのまま他のプロジェクトに流用することはできない
21     using Honememo.Wptscs.Properties;
22
23     /// <summary>
24     /// フォーム処理に関するユーティリティクラスです。
25     /// </summary>
26     public static class FormUtils
27     {
28         #region リソース関連
29
30         /// <summary>
31         /// バージョン情報を含んだアプリケーション名を返す。
32         /// </summary>
33         /// <returns>アプリケーション名</returns>
34         public static string ApplicationName()
35         {
36             // アセンブリからバージョン情報を取得し、書式化して返す
37             // ビルド番号・リビジョンは無視
38             // ※例外なし。もし万が一発生する場合はそのまま投げる
39             Version ver = Assembly.GetExecutingAssembly().GetName().Version;
40             return String.Format(Resources.ApplicationName, ver.Major, ver.Minor);
41         }
42
43         /// <summary>
44         /// 指定されたファイルを UserAppDataPath
45         /// → 旧バージョンのUserAppDataPath
46         /// → StartupPath の順に探索し、その際のパスを返す。
47         /// </summary>
48         /// <param name="fileName">ファイル名。</param>
49         /// <param name="compatible">探索する旧バージョンの最大。</param>
50         /// <returns>ファイルが存在したパス、どこにも存在しない場合は<c>null</c>。</returns>
51         /// <remarks>アセンブリ名が変わっている場合、旧バージョンは探索不可。</remarks>
52         public static string SearchUserAppData(string fileName, string compatible)
53         {
54             // ※ 以下GetFilesAtUserAppDataと共通化してもよいが、性能に影響あるかと考え自前で処理
55             // 現在の UserAppDataPath を探索
56             string path = Path.Combine(Application.UserAppDataPath, fileName);
57             if (File.Exists(path))
58             {
59                 return path;
60             }
61
62             // 可能であれば、旧バージョンの UserAppDataPath を探索
63             if (!String.IsNullOrEmpty(compatible))
64             {
65                 // ファイルが見つかるまで探索
66                 foreach (string dir in FormUtils.GetCompatibleUserAppDataPaths(compatible))
67                 {
68                     path = Path.Combine(dir, fileName);
69                     if (File.Exists(path))
70                     {
71                         return path;
72                     }
73                 }
74             }
75
76             // どこにも無い場合は、exeと同じフォルダを探索
77             path = Path.Combine(Application.StartupPath, fileName);
78             if (File.Exists(path))
79             {
80                 return path;
81             }
82
83             return null;
84         }
85
86         /// <summary>
87         /// 指定されたファイルを UserAppDataPath
88         /// → StartupPath の順に探索し、その際のパスを返す。
89         /// </summary>
90         /// <param name="fileName">ファイル名。</param>
91         /// <returns>ファイルが存在したパス、どこにも存在しない場合は<c>null</c>。</returns>
92         public static string SearchUserAppData(string fileName)
93         {
94             // オーバーロードメソッドをコール
95             return FormUtils.SearchUserAppData(fileName, null);
96         }
97
98         /// <summary>
99         /// UserAppDataPath
100         /// → 旧バージョンのUserAppDataPath
101         /// → StartupPath から、指定した検索パターンに一致するファイル名を返す。
102         /// </summary>
103         /// <param name="searchPattern">ファイル名と対応させる検索文字列。</param>
104         /// <param name="compatible">探索する旧バージョンの最大。</param>
105         /// <returns>
106         /// 指定した検索パターンに一致するファイル名を格納するString配列。ファイル名には完全パスを含む。
107         /// 同名のファイルが複数のパスに存在する場合、最初に発見したもののみを返す。
108         /// </returns>
109         /// <exception cref="ArgumentException"><paramref name="searchPattern"/>に有効なパターンが含まれていない場合。</exception>
110         /// <exception cref="ArgumentNullException"><paramref name="searchPattern"/>が<c>null</c>の場合。</exception>
111         /// <exception cref="UnauthorizedAccessException">呼び出し元に、必要なアクセス許可がない場合。</exception>
112         /// <remarks>アセンブリ名が変わっている場合、旧バージョンは探索不可。</remarks>
113         public static string[] GetFilesAtUserAppData(string searchPattern, string compatible)
114         {
115             // 現在の UserAppDataPath を探索
116             List<string> files = new List<string>();
117             IgnoreCaseSet names = new IgnoreCaseSet();
118             if (Directory.Exists(Application.UserAppDataPath))
119             {
120                 FormUtils.MergeFiles(files, names, Directory.GetFiles(Application.UserAppDataPath, searchPattern));
121             }
122
123             // 可能であれば、旧バージョンの UserAppDataPath を探索
124             if (!String.IsNullOrEmpty(compatible))
125             {
126                 // 各ディレクトリのファイル名を取得
127                 foreach (string dir in FormUtils.GetCompatibleUserAppDataPaths(compatible))
128                 {
129                     FormUtils.MergeFiles(files, names, Directory.GetFiles(dir, searchPattern));
130                 }
131             }
132
133             // 最後に、exeと同じフォルダを探索
134             FormUtils.MergeFiles(files, names, Directory.GetFiles(Application.StartupPath, searchPattern));
135             return files.ToArray();
136         }
137
138         /// <summary>
139         /// UserAppDataPath
140         /// → StartupPath から、指定した検索パターンに一致するファイル名を返す。
141         /// </summary>
142         /// <param name="searchPattern">ファイル名と対応させる検索文字列。</param>
143         /// <returns>
144         /// 指定した検索パターンに一致するファイル名を格納するString配列。ファイル名には完全パスを含む。
145         /// 同名のファイルが複数のパスに存在する場合、最初に発見したもののみを返す。
146         /// </returns>
147         /// <exception cref="ArgumentException"><paramref name="searchPattern"/>に有効なパターンが含まれていない場合。</exception>
148         /// <exception cref="ArgumentNullException"><paramref name="searchPattern"/>が<c>null</c>の場合。</exception>
149         /// <exception cref="UnauthorizedAccessException">呼び出し元に、必要なアクセス許可がない場合。</exception>
150         public static string[] GetFilesAtUserAppData(string searchPattern)
151         {
152             // オーバーロードメソッドをコール
153             return FormUtils.GetFilesAtUserAppData(searchPattern, null);
154         }
155
156         /// <summary>
157         /// UserAppDataPath → StartupPath から、全ファイル名を返す。
158         /// </summary>
159         /// <returns>
160         /// フォルダ内の全ファイル名を格納するString配列。ファイル名には完全パスを含む。
161         /// 同名のファイルが複数のパスに存在する場合、最初に発見したもののみを返す。
162         /// </returns>
163         /// <exception cref="UnauthorizedAccessException">呼び出し元に、必要なアクセス許可がない場合。</exception>
164         public static string[] GetFilesAtUserAppData()
165         {
166             // オーバーロードメソッドをコール
167             return FormUtils.GetFilesAtUserAppData("*", null);
168         }
169
170         /// <summary>
171         /// 文字列中のファイル名に使用できない文字を「_」に置換。
172         /// また、&amp;nbsp;由来の半角スペース (u00a0) も普通の半角スペース (u0020) に置換する。
173         /// </summary>
174         /// <param name="fileName">ファイル名。</param>
175         /// <returns>置換後のファイル名。</returns>
176         public static string ReplaceInvalidFileNameChars(string fileName)
177         {
178             // 渡された文字列にファイル名に使えない文字が含まれている場合、_ に置き換える
179             string result = fileName;
180             foreach (char c in Path.GetInvalidFileNameChars())
181             {
182                 result = result.Replace(c, '_');
183             }
184
185             // &nbsp;由来の半角スペース (u00a0) も普通の半角スペース (u0020) に置き換える
186             result = result.Replace(' ', ' ');
187             return result;
188         }
189
190         #endregion
191
192         #region null値許容メソッド
193
194         /// <summary>
195         /// <see cref="DataGridViewCell"/>が<c>null</c>の場合に空の文字列を返す<see cref="Object.ToString"/>。
196         /// </summary>
197         /// <param name="obj">値を<see cref="Object.ToString"/>するセル。<c>null</c>も可。</param>
198         /// <returns>渡されたセルの<see cref="DataGridViewCell.Value"/>を<see cref="Object.ToString"/>した結果。<c>null</c>の場合には空の文字列。</returns>
199         public static string ToString(DataGridViewCell obj)
200         {
201             return FormUtils.ToString(obj, String.Empty);
202         }
203
204         /// <summary>
205         /// <see cref="DataGridViewCell"/>が<c>null</c>の場合に指定された文字列を返す<see cref="Object.ToString"/>。
206         /// </summary>
207         /// <param name="obj">値を<see cref="Object.ToString"/>するセル。<c>null</c>も可。</param>
208         /// <param name="nullStr">渡されたセルが<c>null</c>の場合に返される文字列。<c>null</c>も可。</param>
209         /// <returns>渡されたセルの<see cref="DataGridViewCell.Value"/>を<see cref="Object.ToString"/>した結果。<c>null</c>の場合には指定された文字列。</returns>
210         public static string ToString(DataGridViewCell obj, string nullStr)
211         {
212             if (obj == null)
213             {
214                 return nullStr;
215             }
216             else if (obj.Value == null)
217             {
218                 return nullStr;
219             }
220
221             return obj.Value.ToString();
222         }
223
224         #endregion
225
226         #region ダイアログ
227
228         /// <summary>
229         /// 単純デザインの通知ダイアログ(入力された文字列を表示)。
230         /// </summary>
231         /// <param name="msg">メッセージ。</param>
232         public static void InformationDialog(string msg)
233         {
234             // 渡された文字列で通知ダイアログを表示
235             MessageBox.Show(
236                 msg,
237                 Resources.InformationTitle,
238                 MessageBoxButtons.OK,
239                 MessageBoxIcon.Information);
240         }
241
242         /// <summary>
243         /// 単純デザインの通知ダイアログ(入力された文字列を書式化して表示)。
244         /// </summary>
245         /// <param name="format">書式項目を含んだメッセージ。</param>
246         /// <param name="args">書式設定対象オブジェクト配列。</param>
247         public static void InformationDialog(string format, params object[] args)
248         {
249             // オーバーロードメソッドをコール
250             FormUtils.InformationDialog(String.Format(format, args));
251         }
252
253         /// <summary>
254         /// 単純デザインの警告ダイアログ(入力された文字列を表示)。
255         /// </summary>
256         /// <param name="msg">メッセージ。</param>
257         public static void WarningDialog(string msg)
258         {
259             // 渡された文字列で警告ダイアログを表示
260             MessageBox.Show(
261                 msg,
262                 Resources.WarningTitle,
263                 MessageBoxButtons.OK,
264                 MessageBoxIcon.Warning);
265         }
266
267         /// <summary>
268         /// 単純デザインの警告ダイアログ(入力された文字列を書式化して表示)。
269         /// </summary>
270         /// <param name="format">書式項目を含んだメッセージ。</param>
271         /// <param name="args">書式設定対象オブジェクト配列。</param>
272         public static void WarningDialog(string format, params object[] args)
273         {
274             // オーバーロードメソッドをコール
275             FormUtils.WarningDialog(String.Format(format, args));
276         }
277
278         /// <summary>
279         /// 単純デザインのエラーダイアログ(入力された文字列を表示)。
280         /// </summary>
281         /// <param name="msg">メッセージ。</param>
282         public static void ErrorDialog(string msg)
283         {
284             // 渡された文字列でエラーダイアログを表示
285             MessageBox.Show(
286                 msg,
287                 Resources.ErrorTitle,
288                 MessageBoxButtons.OK,
289                 MessageBoxIcon.Error);
290         }
291
292         /// <summary>
293         /// 単純デザインのエラーダイアログ(入力された文字列を書式化して表示)。
294         /// </summary>
295         /// <param name="format">書式項目を含んだメッセージ。</param>
296         /// <param name="args">書式設定対象オブジェクト配列。</param>
297         public static void ErrorDialog(string format, params object[] args)
298         {
299             // オーバーロードメソッドをコール
300             FormUtils.ErrorDialog(String.Format(format, args));
301         }
302
303         #endregion
304
305         #region テーブル処理
306
307         /// <summary>
308         /// <see cref="DataGridViewRow"/>が空行かを判定する。
309         /// </summary>
310         /// <param name="row">1行。</param>
311         /// <returns>空行の場合<c>true</c>。</returns>
312         public static bool IsEmptyRow(DataGridViewRow row)
313         {
314             foreach (DataGridViewCell cell in row.Cells)
315             {
316                 if (!String.IsNullOrEmpty(FormUtils.ToString(cell)))
317                 {
318                     return false;
319                 }
320             }
321
322             return true;
323         }
324
325         #endregion
326
327         #region 内部メソッド
328
329         /// <summary>
330         /// 指定されたバージョン以上の旧バージョンのUserAppDataPathを取得する。
331         /// </summary>
332         /// <param name="compatible">探索する旧バージョンの最大。</param>
333         /// <returns>旧バージョンと自バージョンの間のフォルダ名を格納するString配列。フォルダ名には完全パスを含む。</returns>
334         /// <remarks>
335         /// フォルダが異なる同じファイル名のパスが存在する場合、登録しない。
336         /// アセンブリ名が変わっている場合、旧バージョンは探索不可。
337         /// </remarks>
338         private static string[] GetCompatibleUserAppDataPaths(string compatible)
339         {
340             // UserAppDataPath は
341             // <ベースパス>\<CompanyName>\<ProductName>\<ProductVersion>
342             // という構成のはずなので、一つ上のフォルダから自分より前のフォルダを探索
343             List<string> paths = new List<string>();
344             string parent = Path.GetDirectoryName(Application.UserAppDataPath);
345             if (!String.IsNullOrEmpty(parent))
346             {
347                 // 現在のバージョンのフォルダ名
348                 string now = Path.GetFileName(Application.UserAppDataPath);
349
350                 // 同じ階層のフォルダをすべて取得し、降順にソート
351                 string[] directories = Directory.GetDirectories(parent);
352                 Array.Sort(directories);
353                 Array.Reverse(directories);
354
355                 // 指定された互換バージョンと自バージョンの間のパスのみを取得
356                 foreach (string dir in directories)
357                 {
358                     string ver = Path.GetFileName(dir);
359                     if (compatible.CompareTo(ver) <= 0 && ver.CompareTo(now) < 0)
360                     {
361                         paths.Add(dir);
362                     }
363                 }
364             }
365
366             return paths.ToArray();
367         }
368
369         /// <summary>
370         /// <see cref="GetFilesAtUserAppData(string, string)"/>用のファイル名リストのマージを行う。
371         /// </summary>
372         /// <param name="mergeto">マージ先ファイル名リスト。</param>
373         /// <param name="names">比較高速化用のパスを含まないファイル名セット。</param>
374         /// <param name="mergefrom">マージ元ファイル名リスト。</param>
375         /// <remarks>フォルダが異なる同じファイル名のパスが存在する場合、登録しない。</remarks>
376         private static void MergeFiles(IList<string> mergeto, IgnoreCaseSet names, IList<string> mergefrom)
377         {
378             foreach (string file in mergefrom)
379             {
380                 string name = Path.GetFileName(file);
381                 if (!names.Contains(name))
382                 {
383                     mergeto.Add(file);
384                     names.Add(name);
385                 }
386             }
387         }
388
389         #endregion
390     }
391 }