OSDN Git Service

svnプロパティをファイルの種類に応じたものに更新
[wptscs/wpts.git] / Wptscs / MainForm.cs
1 // ================================================================================================
2 // <summary>
3 //      Wikipedia翻訳支援ツール主画面クラスソース</summary>
4 //
5 // <copyright file="MainForm.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Wptscs
12 {
13     using System;
14     using System.Collections.Generic;
15     using System.ComponentModel;
16     using System.Data;
17     using System.Drawing;
18     using System.IO;
19     using System.Text;
20     using System.Windows.Forms;
21     using Honememo.Utilities;
22     using Honememo.Wptscs.Logics;
23     using Honememo.Wptscs.Models;
24     using Honememo.Wptscs.Properties;
25
26     /// <summary>
27     /// Wikipedia翻訳支援ツール主画面のクラスです。
28     /// </summary>
29     public partial class MainForm : Form
30     {
31         #region private変数
32
33         /// <summary>
34         /// 現在読み込んでいるアプリケーションの設定。
35         /// </summary>
36         private Config config;
37
38         /// <summary>
39         /// 検索支援処理クラスのオブジェクト。
40         /// </summary>
41         private Translator translate;
42
43         /// <summary>
44         /// 表示済みログ文字列長。
45         /// </summary>
46         private int logLastLength;
47
48         #endregion
49
50         #region コンストラクタ
51
52         /// <summary>
53         /// コンストラクタ。初期化メソッド呼び出しのみ。
54         /// </summary>
55         public MainForm()
56         {
57             // Windows フォーム デザイナで生成されたコード
58             this.InitializeComponent();
59         }
60
61         #endregion
62
63         #region 各イベントのメソッド
64
65         /// <summary>
66         /// フォームロード時の処理。初期化。
67         /// </summary>
68         /// <param name="sender">イベント発生オブジェクト。</param>
69         /// <param name="e">発生したイベント。</param>
70         private void MainForm_Load(object sender, EventArgs e)
71         {
72             // 設定ファイルの読み込み
73             if (!this.LoadConfig())
74             {
75                 // 読み込み失敗時はどうしようもないのでそのまま終了
76                 this.Close();
77             }
78
79             this.translate = null;
80             Control.CheckForIllegalCrossThreadCalls = false;
81
82             // コンボボックス設定
83             this.Initialize();
84
85             // 前回の処理状態を復元
86             this.textBoxSaveDirectory.Text = Settings.Default.SaveDirectory;
87             this.comboBoxSource.SelectedText = Settings.Default.LastSelectedSource;
88             this.comboBoxTarget.SelectedText = Settings.Default.LastSelectedTarget;
89
90             // コンボボックス変更時の処理をコール
91             this.ComboBoxSource_SelectedIndexChanged(sender, e);
92             this.ComboBoxTarget_SelectedIndexChanged(sender, e);
93         }
94
95         /// <summary>
96         /// フォームクローズ時の処理。処理状態を保存。
97         /// </summary>
98         /// <param name="sender">イベント発生オブジェクト。</param>
99         /// <param name="e">発生したイベント。</param>
100         private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
101         {
102             // 現在の作業フォルダ、絞込み文字列を保存
103             Settings.Default.SaveDirectory = this.textBoxSaveDirectory.Text;
104             Settings.Default.LastSelectedSource = this.comboBoxSource.Text;
105             Settings.Default.LastSelectedTarget = this.comboBoxTarget.Text;
106             Settings.Default.Save();
107         }
108
109         /// <summary>
110         /// 翻訳元コンボボックス変更時の処理。
111         /// </summary>
112         /// <param name="sender">イベント発生オブジェクト。</param>
113         /// <param name="e">発生したイベント。</param>
114         private void ComboBoxSource_SelectedIndexChanged(object sender, EventArgs e)
115         {
116             // ラベルに言語名を表示する
117             this.labelSource.Text = String.Empty;
118             this.linkLabelSourceURL.Text = "http://";
119             if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
120             {
121                 this.comboBoxSource.Text = this.comboBoxSource.Text.Trim().ToLower();
122
123                 // その言語の、ユーザーが使用している言語での表示名を表示
124                 // (日本語環境だったら日本語を、英語だったら英語を)
125                 Language.LanguageName name;
126                 this.labelSource.Text = String.Empty;
127                 if (this.config.GetWebsite(this.comboBoxSource.Text) != null &&
128                     this.config.GetWebsite(this.comboBoxSource.Text).Language.Names.TryGetValue(
129                     System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
130                     out name))
131                 {
132                     this.labelSource.Text = name.Name;
133                 }
134
135                 // サーバーURLの表示
136                 this.linkLabelSourceURL.Text = this.config.GetWebsite(
137                     this.comboBoxSource.Text).Location;
138             }
139         }
140
141         /// <summary>
142         /// 翻訳元コンボボックスフォーカス喪失時の処理。
143         /// </summary>
144         /// <param name="sender">イベント発生オブジェクト。</param>
145         /// <param name="e">発生したイベント。</param>
146         private void ComboBoxSource_Leave(object sender, EventArgs e)
147         {
148             // 直接入力された場合の対策、変更時の処理をコール
149             this.ComboBoxSource_SelectedIndexChanged(sender, e);
150         }
151
152         /// <summary>
153         /// リンクラベルのリンククリック時の処理。
154         /// </summary>
155         /// <param name="sender">イベント発生オブジェクト。</param>
156         /// <param name="e">発生したイベント。</param>
157         private void LinkLabelSourceURL_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
158         {
159             // リンクを開く
160             System.Diagnostics.Process.Start(((LinkLabel)sender).Text);
161         }
162
163         /// <summary>
164         /// 翻訳先コンボボックス変更時の処理。
165         /// </summary>
166         /// <param name="sender">イベント発生オブジェクト。</param>
167         /// <param name="e">発生したイベント。</param>
168         private void ComboBoxTarget_SelectedIndexChanged(object sender, EventArgs e)
169         {
170             // ラベルに言語名を表示する
171             this.labelTarget.Text = String.Empty;
172             if (!String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
173             {
174                 this.comboBoxTarget.Text = this.comboBoxTarget.Text.Trim().ToLower();
175
176                 // その言語の、ユーザーが使用している言語での表示名を表示
177                 // (日本語環境だったら日本語を、英語だったら英語を)
178                 if (this.config.GetWebsite(
179                     this.comboBoxTarget.Text) != null)
180                 {
181                     this.labelTarget.Text = this.config.GetWebsite(
182                         this.comboBoxTarget.Text).Language.Names[System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName].Name;
183                 }
184             }
185         }
186
187         /// <summary>
188         /// 翻訳先コンボボックスフォーカス喪失時の処理。
189         /// </summary>
190         /// <param name="sender">イベント発生オブジェクト。</param>
191         /// <param name="e">発生したイベント。</param>
192         private void ComboBoxTarget_Leave(object sender, EventArgs e)
193         {
194             // 直接入力された場合の対策、変更時の処理をコール
195             this.ComboBoxTarget_SelectedIndexChanged(sender, e);
196         }
197
198         /// <summary>
199         /// 設定ボタン押下時の処理。
200         /// </summary>
201         /// <param name="sender">イベント発生オブジェクト。</param>
202         /// <param name="e">発生したイベント。</param>
203         private void ButtonConfig_Click(object sender, EventArgs e)
204         {
205             // 設定画面を開く
206             ConfigForm form = new ConfigForm(this.config);
207             form.ShowDialog();
208
209             // 戻ってきたら設定ファイルを再読み込み
210             // ※ 万が一エラーでもとりあえず続行
211             this.LoadConfig();
212
213             // コンボボックス設定
214             string backupSourceSelected = this.comboBoxSource.SelectedText;
215             string backupSourceTarget = this.comboBoxTarget.SelectedText;
216             this.Initialize();
217             this.comboBoxSource.SelectedText = backupSourceSelected;
218             this.comboBoxTarget.SelectedText = backupSourceTarget;
219
220             // コンボボックス変更時の処理をコール
221             this.ComboBoxSource_SelectedIndexChanged(sender, e);
222             this.ComboBoxTarget_SelectedIndexChanged(sender, e);
223         }
224
225         /// <summary>
226         /// 参照ボタン押下時の処理。
227         /// </summary>
228         /// <param name="sender">イベント発生オブジェクト。</param>
229         /// <param name="e">発生したイベント。</param>
230         private void ButtonSaveDirectory_Click(object sender, EventArgs e)
231         {
232             // フォルダ名が入力されている場合、それを初期位置に設定
233             if (!String.IsNullOrEmpty(this.textBoxSaveDirectory.Text))
234             {
235                 this.folderBrowserDialogSaveDirectory.SelectedPath = this.textBoxSaveDirectory.Text;
236             }
237
238             // フォルダ選択画面をオープン
239             if (this.folderBrowserDialogSaveDirectory.ShowDialog() == System.Windows.Forms.DialogResult.OK)
240             {
241                 // フォルダが選択された場合、フォルダ名に選択されたフォルダを設定
242                 this.textBoxSaveDirectory.Text = this.folderBrowserDialogSaveDirectory.SelectedPath;
243             }
244         }
245
246         /// <summary>
247         /// 出力先テキストボックスフォーカス喪失時の処理。
248         /// </summary>
249         /// <param name="sender">イベント発生オブジェクト。</param>
250         /// <param name="e">発生したイベント。</param>
251         private void TextBoxSaveDirectory_Leave(object sender, EventArgs e)
252         {
253             // 空白を削除
254             this.textBoxSaveDirectory.Text = this.textBoxSaveDirectory.Text.Trim();
255         }
256
257         /// <summary>
258         /// 実行ボタン押下時の処理。
259         /// </summary>
260         /// <param name="sender">イベント発生オブジェクト。</param>
261         /// <param name="e">発生したイベント。</param>
262         private void ButtonRun_Click(object sender, EventArgs e)
263         {
264             // フォーム入力値をチェック
265             if (String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
266             {
267                 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedSource);
268                 this.comboBoxSource.Focus();
269                 return;
270             }
271             else if (String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
272             {
273                 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedTarget);
274                 this.comboBoxTarget.Focus();
275                 return;
276             }
277             else if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text)
278                 && this.comboBoxSource.Text == this.comboBoxTarget.Text)
279             {
280                 FormUtils.WarningDialog(Resources.WarningMessageEqualsSourceAndTarget);
281                 this.comboBoxTarget.Focus();
282                 return;
283             }
284             else if (String.IsNullOrWhiteSpace(this.textBoxSaveDirectory.Text))
285             {
286                 FormUtils.WarningDialog(Resources.WarningMessageEmptySaveDirectory);
287                 this.textBoxSaveDirectory.Focus();
288                 return;
289             }
290             else if (!Directory.Exists(this.textBoxSaveDirectory.Text))
291             {
292                 FormUtils.WarningDialog(Resources.WarningMessageIgnoreSaveDirectory);
293                 this.textBoxSaveDirectory.Focus();
294                 return;
295             }
296             else if (String.IsNullOrWhiteSpace(this.textBoxArticle.Text))
297             {
298                 FormUtils.WarningDialog(Resources.WarningMessageEmptyArticle);
299                 this.textBoxArticle.Focus();
300                 return;
301             }
302
303             // 画面をロック
304             this.LockOperation();
305
306             // バックグラウンド処理を実行
307             this.backgroundWorkerRun.RunWorkerAsync();
308         }
309
310         /// <summary>
311         /// 中止ボタン押下時の処理。
312         /// </summary>
313         /// <param name="sender">イベント発生オブジェクト。</param>
314         /// <param name="e">発生したイベント。</param>
315         private void ButtonStop_Click(object sender, EventArgs e)
316         {
317             // 処理を中断
318             this.buttonStop.Enabled = false;
319             if (this.backgroundWorkerRun.IsBusy == true)
320             {
321                 System.Diagnostics.Debug.WriteLine("MainForm.-Stop_Click > 処理中断");
322                 this.backgroundWorkerRun.CancelAsync();
323                 if (this.translate != null)
324                 {
325                     this.translate.CancellationPending = true;
326                 }
327             }
328         }
329
330         /// <summary>
331         /// 実行ボタン バックグラウンド処理(スレッド)。
332         /// </summary>
333         /// <param name="sender">イベント発生オブジェクト。</param>
334         /// <param name="e">発生したイベント。</param>
335         private void BackgroundWorkerRun_DoWork(object sender, DoWorkEventArgs e)
336         {
337             try
338             {
339                 // 翻訳支援処理の前処理
340                 this.textBoxLog.Clear();
341                 this.logLastLength = 0;
342                 this.textBoxLog.AppendText(
343                     String.Format(
344                         Resources.LogMessage_Start,
345                         FormUtils.ApplicationName(),
346                         DateTime.Now.ToString("F")));
347
348                 // 処理結果とログのための出力ファイル名を作成
349                 string fileName;
350                 string logName;
351                 this.MakeFileName(out fileName, out logName, this.textBoxArticle.Text.Trim(), this.textBoxSaveDirectory.Text);
352
353                 // 翻訳支援処理を実行し、結果とログをファイルに出力
354                 try
355                 {
356                     this.translate = Translator.Create(this.config, this.comboBoxSource.Text, this.comboBoxTarget.Text);
357                 }
358                 catch (NotImplementedException)
359                 {
360                     // 将来の拡張用
361                     this.textBoxLog.AppendText(String.Format(Resources.InformationMessage_DevelopingMethod, "Wikipedia以外の処理"));
362                     FormUtils.InformationDialog(Resources.InformationMessage_DevelopingMethod, "Wikipedia以外の処理");
363                     return;
364                 }
365
366                 this.translate.LogUpdate += new EventHandler(this.GetLogUpdate);
367
368                 // 実行前に、ユーザーから中止要求がされているかをチェック
369                 if (this.backgroundWorkerRun.CancellationPending)
370                 {
371                     this.textBoxLog.AppendText(String.Format(Resources.LogMessage_Stop, logName));
372                 }
373                 else
374                 {
375                     // 翻訳支援処理を実行
376                     bool successFlag = this.translate.Run(this.textBoxArticle.Text.Trim());
377
378                     // 処理に時間がかかるため、出力ファイル名を再確認
379                     this.MakeFileName(out fileName, out logName, this.textBoxArticle.Text.Trim(), this.textBoxSaveDirectory.Text);
380                     if (successFlag)
381                     {
382                         // 処理結果を出力
383                         try
384                         {
385                             StreamWriter sw = new StreamWriter(Path.Combine(this.textBoxSaveDirectory.Text, fileName));
386                             try
387                             {
388                                 sw.Write(this.translate.Text);
389                                 this.textBoxLog.AppendText(String.Format(Resources.LogMessage_End, fileName, logName));
390                             }
391                             finally
392                             {
393                                 sw.Close();
394                             }
395                         }
396                         catch (Exception ex)
397                         {
398                             this.textBoxLog.AppendText(String.Format(Resources.LogMessage_ErrorFileSave, Path.Combine(this.textBoxSaveDirectory.Text, fileName), ex.Message));
399                             this.textBoxLog.AppendText(String.Format(Resources.LogMessage_Stop, logName));
400                         }
401                     }
402                     else
403                     {
404                         this.textBoxLog.AppendText(String.Format(Resources.LogMessage_Stop, logName));
405                     }
406                 }
407
408                 // ログを出力
409                 try
410                 {
411                     StreamWriter sw = new StreamWriter(Path.Combine(this.textBoxSaveDirectory.Text, logName));
412                     try
413                     {
414                         sw.Write(this.textBoxLog.Text);
415                     }
416                     finally
417                     {
418                         sw.Close();
419                     }
420                 }
421                 catch (Exception ex)
422                 {
423                     this.textBoxLog.AppendText(String.Format(Resources.LogMessage_ErrorFileSave, Path.Combine(this.textBoxSaveDirectory.Text, logName), ex.Message));
424                 }
425             }
426             catch (Exception ex)
427             {
428                 this.textBoxLog.AppendText("\r\n" + String.Format(Resources.ErrorMessageDevelopmentError, ex.Message, ex.StackTrace) + "\r\n");
429                 System.Diagnostics.Debug.WriteLine("MainForm.backgroundWorkerRun_DoWork > 想定外のエラー : " + ex.Message);
430                 System.Diagnostics.Debug.WriteLine(ex.StackTrace);
431             }
432         }
433
434         /// <summary>
435         /// 実行ボタン バックグラウンド処理(終了時)。
436         /// </summary>
437         /// <param name="sender">イベント発生オブジェクト。</param>
438         /// <param name="e">発生したイベント。</param>
439         private void BackgroundWorkerRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
440         {
441             // 設定ファイルのキャッシュ情報を更新
442             try
443             {
444                 this.config.Save(Settings.Default.ConfigurationFile);
445             }
446             catch (Exception ex)
447             {
448                 FormUtils.WarningDialog(
449                     Resources.WarningMessageCacheSaveFailed,
450                     ex.Message);
451             }
452
453             // 画面をロック中から解放
454             this.Release();
455         }
456
457         #endregion
458
459         #region それ以外のメソッド
460
461         /// <summary>
462         /// 画面初期化処理。
463         /// </summary>
464         private void Initialize()
465         {
466             // コンボボックス設定
467             this.comboBoxSource.Items.Clear();
468             this.comboBoxTarget.Items.Clear();
469
470             // 設定ファイルに存在する全言語を選択肢として登録する
471             foreach (Website site in this.config.Websites)
472             {
473                 this.comboBoxSource.Items.Add(site.Language.Code);
474                 this.comboBoxTarget.Items.Add(site.Language.Code);
475             }
476         }
477
478         /// <summary>
479         /// 設定ファイル読み込み。
480         /// </summary>
481         /// <returns>読み込み成功時は<c>true</c>。</returns>
482         private bool LoadConfig()
483         {
484             // 設定ファイルの読み込み
485             try
486             {
487                 this.config = Config.GetInstance(Settings.Default.ConfigurationFile);
488             }
489             catch (FileNotFoundException ex)
490             {
491                 // 設定ファイルが見つからない場合
492                 System.Diagnostics.Debug.WriteLine(
493                     "MainForm.LoadConfig > 設定ファイル読み込み失敗 : " + ex.Message);
494                 FormUtils.ErrorDialog(
495                     Resources.ErrorMessageConfigNotFound,
496                     Settings.Default.ConfigurationFile);
497
498                 return false;
499             }
500             catch (Exception ex)
501             {
502                 System.Diagnostics.Debug.WriteLine(
503                     "MainForm.LoadConfig > 設定ファイル読み込み時エラー : " + ex.StackTrace);
504                 FormUtils.ErrorDialog(
505                     Resources.ErrorMessageConfigLordFailed,
506                     ex.Message);
507
508                 return false;
509             }
510
511             return true;
512         }
513
514         /// <summary>
515         /// 画面をロック中に移行。
516         /// </summary>
517         private void LockOperation()
518         {
519             // 各種ボタンなどを入力不可に変更
520             this.groupBoxTransfer.Enabled = false;
521             this.groupBoxSaveDirectory.Enabled = false;
522             this.textBoxArticle.Enabled = false;
523             this.buttonRun.Enabled = false;
524
525             // 中止ボタンを有効に変更
526             this.buttonStop.Enabled = true;
527         }
528
529         /// <summary>
530         /// 画面をロック中から解放。
531         /// </summary>
532         private void Release()
533         {
534             // 中止ボタンを入力不可に変更
535             this.buttonStop.Enabled = false;
536
537             // 各種ボタンなどを有効に変更
538             this.groupBoxTransfer.Enabled = true;
539             this.groupBoxSaveDirectory.Enabled = true;
540             this.textBoxArticle.Enabled = true;
541             this.buttonRun.Enabled = true;
542         }
543
544         /// <summary>
545         /// 渡された文字列から.txtと.logの重複していないファイル名を作成。
546         /// </summary>
547         /// <param name="fileName">出力結果ファイル名。</param>
548         /// <param name="logName">出力ログファイル名。</param>
549         /// <param name="text">出力する結果テキスト。</param>
550         /// <param name="dir">出力先ディレクトリ。</param>
551         /// <returns><c>true</c> 出力成功</returns>
552         private bool MakeFileName(out string fileName, out string logName, string text, string dir)
553         {
554             // 出力先フォルダに存在しないファイル名(の拡張子より前)を作成
555             // ※渡されたWikipedia等の記事名にファイル名に使えない文字が含まれている場合、_ に置き換える
556             //   また、ファイル名が重複している場合、xx[0].txtのように連番を付ける
557             string fileNameBase = FormUtils.ReplaceInvalidFileNameChars(text);
558             fileName = fileNameBase + ".txt";
559             logName = fileNameBase + ".log";
560             bool success = false;
561             for (int i = 0; i < 100000; i++)
562             {
563                 // ※100000まで試して空きが見つからないことは無いはず、もし見つからなかったら最後のを上書き
564                 if (!File.Exists(Path.Combine(dir, fileName))
565                     && !File.Exists(Path.Combine(dir, logName)))
566                 {
567                     success = true;
568                     break;
569                 }
570
571                 fileName = fileNameBase + "[" + i + "]" + ".txt";
572                 logName = fileNameBase + "[" + i + "]" + ".log";
573             }
574
575             // 結果設定
576             return success;
577         }
578
579         /// <summary>
580         /// 翻訳支援処理クラスのイベント用。
581         /// </summary>
582         /// <param name="sender">イベント発生オブジェクト。</param>
583         /// <param name="e">発生したイベント。</param>
584         private void GetLogUpdate(object sender, System.EventArgs e)
585         {
586             // 前回以降に追加されたログをテキストボックスに出力
587             int length = this.translate.Log.Length;
588             if (length > this.logLastLength)
589             {
590                 this.textBoxLog.AppendText(this.translate.Log.Substring(this.logLastLength, length - this.logLastLength));
591             }
592
593             this.logLastLength = length;
594         }
595
596         #endregion
597     }
598 }