1 // ================================================================================================
3 // Wikipedia翻訳支援ツール主画面クラスソース</summary>
5 // <copyright file="MainForm.cs" company="honeplusのメモ帳">
6 // Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
9 // ================================================================================================
11 namespace Honememo.Wptscs
14 using System.Collections.Generic;
15 using System.ComponentModel;
21 using System.Windows.Forms;
22 using Honememo.Utilities;
23 using Honememo.Wptscs.Logics;
24 using Honememo.Wptscs.Models;
25 using Honememo.Wptscs.Properties;
26 using Honememo.Wptscs.Utilities;
27 using Honememo.Wptscs.Websites;
30 /// Wikipedia翻訳支援ツール主画面のクラスです。
32 public partial class MainForm : Form
37 /// 現在読み込んでいるアプリケーションの設定。
39 private Config config;
44 private Translator translator;
49 private int logLength;
56 /// コンストラクタ。初期化メソッド呼び出しのみ。
60 // Windows フォーム デザイナで生成されたコード
61 this.InitializeComponent();
71 /// <param name="sender">イベント発生オブジェクト。</param>
72 /// <param name="e">発生したイベント。</param>
73 private void MainForm_Load(object sender, EventArgs e)
76 if (!this.LoadConfig())
78 // 読み込み失敗時はどうしようもないのでそのまま終了
82 this.translator = null;
83 Control.CheckForIllegalCrossThreadCalls = false;
89 this.textBoxSaveDirectory.Text = Settings.Default.SaveDirectory;
90 this.comboBoxSource.SelectedItem = Settings.Default.LastSelectedSource;
91 this.comboBoxTarget.SelectedItem = Settings.Default.LastSelectedTarget;
94 this.ComboBoxSource_SelectedIndexChanged(sender, e);
95 this.ComboBoxTarget_SelectedIndexChanged(sender, e);
99 /// フォームクローズ時の処理。処理状態を保存。
101 /// <param name="sender">イベント発生オブジェクト。</param>
102 /// <param name="e">発生したイベント。</param>
103 private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
105 // 現在の作業フォルダ、絞込み文字列を保存
106 Settings.Default.SaveDirectory = this.textBoxSaveDirectory.Text;
107 Settings.Default.LastSelectedSource = this.comboBoxSource.Text;
108 Settings.Default.LastSelectedTarget = this.comboBoxTarget.Text;
109 Settings.Default.Save();
113 /// 翻訳元コンボボックス変更時の処理。
115 /// <param name="sender">イベント発生オブジェクト。</param>
116 /// <param name="e">発生したイベント。</param>
117 private void ComboBoxSource_SelectedIndexChanged(object sender, EventArgs e)
120 this.labelSource.Text = String.Empty;
121 this.linkLabelSourceURL.Text = "http://";
122 if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
124 // その言語の、ユーザーが使用している言語での表示名を表示
125 // (日本語環境だったら日本語を、英語だったら英語を)
126 Language.LanguageName name;
127 this.labelSource.Text = String.Empty;
128 if (this.config.GetWebsite(this.comboBoxSource.Text) != null &&
129 this.config.GetWebsite(this.comboBoxSource.Text).Language.Names.TryGetValue(
130 System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
133 this.labelSource.Text = name.Name;
137 this.linkLabelSourceURL.Text = this.config.GetWebsite(
138 this.comboBoxSource.Text).Location;
143 /// 翻訳元コンボボックスフォーカス喪失時の処理。
145 /// <param name="sender">イベント発生オブジェクト。</param>
146 /// <param name="e">発生したイベント。</param>
147 private void ComboBoxSource_Leave(object sender, EventArgs e)
149 // 直接入力された場合の対策、変更時の処理をコール
150 this.ComboBoxSource_SelectedIndexChanged(sender, e);
154 /// リンクラベルのリンククリック時の処理。
156 /// <param name="sender">イベント発生オブジェクト。</param>
157 /// <param name="e">発生したイベント。</param>
158 private void LinkLabelSourceURL_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
161 System.Diagnostics.Process.Start(((LinkLabel)sender).Text);
165 /// 翻訳先コンボボックス変更時の処理。
167 /// <param name="sender">イベント発生オブジェクト。</param>
168 /// <param name="e">発生したイベント。</param>
169 private void ComboBoxTarget_SelectedIndexChanged(object sender, EventArgs e)
172 this.labelTarget.Text = String.Empty;
173 if (!String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
175 this.comboBoxTarget.Text = this.comboBoxTarget.Text.Trim().ToLower();
177 // その言語の、ユーザーが使用している言語での表示名を表示
178 // (日本語環境だったら日本語を、英語だったら英語を)
179 if (this.config.GetWebsite(this.comboBoxTarget.Text) != null)
181 this.labelTarget.Text = this.config.GetWebsite(
182 this.comboBoxTarget.Text).Language.Names[System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName].Name;
188 /// 翻訳先コンボボックスフォーカス喪失時の処理。
190 /// <param name="sender">イベント発生オブジェクト。</param>
191 /// <param name="e">発生したイベント。</param>
192 private void ComboBoxTarget_Leave(object sender, EventArgs e)
194 // 直接入力された場合の対策、変更時の処理をコール
195 this.ComboBoxTarget_SelectedIndexChanged(sender, e);
201 /// <param name="sender">イベント発生オブジェクト。</param>
202 /// <param name="e">発生したイベント。</param>
203 private void ButtonConfig_Click(object sender, EventArgs e)
206 ConfigForm form = new ConfigForm(this.config);
209 // 戻ってきたら設定ファイルを再読み込み
210 // ※ キャンセル時もインスタンスは更新されてしまうので
214 string backupSourceSelected = this.comboBoxSource.Text;
215 string backupSourceTarget = this.comboBoxTarget.Text;
217 this.comboBoxSource.SelectedItem = backupSourceSelected;
218 this.comboBoxTarget.SelectedItem = backupSourceTarget;
221 this.ComboBoxSource_SelectedIndexChanged(sender, e);
222 this.ComboBoxTarget_SelectedIndexChanged(sender, e);
228 /// <param name="sender">イベント発生オブジェクト。</param>
229 /// <param name="e">発生したイベント。</param>
230 private void ButtonSaveDirectory_Click(object sender, EventArgs e)
232 // フォルダ名が入力されている場合、それを初期位置に設定
233 if (!String.IsNullOrEmpty(this.textBoxSaveDirectory.Text))
235 this.folderBrowserDialogSaveDirectory.SelectedPath = this.textBoxSaveDirectory.Text;
239 if (this.folderBrowserDialogSaveDirectory.ShowDialog() == System.Windows.Forms.DialogResult.OK)
241 // フォルダが選択された場合、フォルダ名に選択されたフォルダを設定
242 this.textBoxSaveDirectory.Text = this.folderBrowserDialogSaveDirectory.SelectedPath;
247 /// 出力先テキストボックスフォーカス喪失時の処理。
249 /// <param name="sender">イベント発生オブジェクト。</param>
250 /// <param name="e">発生したイベント。</param>
251 private void TextBoxSaveDirectory_Leave(object sender, EventArgs e)
254 this.textBoxSaveDirectory.Text = this.textBoxSaveDirectory.Text.Trim();
260 /// <param name="sender">イベント発生オブジェクト。</param>
261 /// <param name="e">発生したイベント。</param>
262 private void ButtonRun_Click(object sender, EventArgs e)
265 if (String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
267 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedSource);
268 this.comboBoxSource.Focus();
271 else if (String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
273 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedTarget);
274 this.comboBoxTarget.Focus();
277 else if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text)
278 && this.comboBoxSource.Text == this.comboBoxTarget.Text)
280 FormUtils.WarningDialog(Resources.WarningMessageEqualsSourceAndTarget);
281 this.comboBoxTarget.Focus();
284 else if (String.IsNullOrWhiteSpace(this.textBoxSaveDirectory.Text))
286 FormUtils.WarningDialog(Resources.WarningMessageEmptySaveDirectory);
287 this.textBoxSaveDirectory.Focus();
290 else if (!Directory.Exists(this.textBoxSaveDirectory.Text))
292 FormUtils.WarningDialog(Resources.WarningMessageIgnoreSaveDirectory);
293 this.textBoxSaveDirectory.Focus();
296 else if (String.IsNullOrWhiteSpace(this.textBoxArticle.Text))
298 FormUtils.WarningDialog(Resources.WarningMessageEmptyArticle);
299 this.textBoxArticle.Focus();
304 this.LockOperation();
307 this.backgroundWorkerRun.RunWorkerAsync();
313 /// <param name="sender">イベント発生オブジェクト。</param>
314 /// <param name="e">発生したイベント。</param>
315 private void ButtonStop_Click(object sender, EventArgs e)
318 this.buttonStop.Enabled = false;
319 if (this.backgroundWorkerRun.IsBusy == true)
321 System.Diagnostics.Debug.WriteLine("MainForm.-Stop_Click > 処理中断");
322 this.backgroundWorkerRun.CancelAsync();
323 if (this.translator != null)
325 this.translator.CancellationPending = true;
331 /// 実行ボタン バックグラウンド処理(スレッド)。
333 /// <param name="sender">イベント発生オブジェクト。</param>
334 /// <param name="e">発生したイベント。</param>
335 private void BackgroundWorkerRun_DoWork(object sender, DoWorkEventArgs e)
340 this.textBoxLog.Clear();
342 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStart, FormUtils.ApplicationName(), DateTime.Now.ToString("F")));
344 // 翻訳支援処理ロジックのオブジェクトを生成
347 this.translator = Translator.Create(this.config, this.comboBoxSource.Text, this.comboBoxTarget.Text);
349 catch (NotImplementedException)
351 // 設定ファイルに対応していないパターンが書かれている場合の例外、将来の拡張用
352 this.textBoxLog.AppendText(String.Format(Resources.InformationMessageDevelopingMethod, "MediaWiki以外の処理"));
353 FormUtils.InformationDialog(Resources.InformationMessageDevelopingMethod, "MediaWiki以外の処理");
357 // ログ・処理状態更新通知を受け取るためのイベント登録
359 this.translator.LogUpdate += new EventHandler(this.GetLogUpdate);
360 this.translator.StatusUpdate += new EventHandler(this.GetStatusUpdate);
361 this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Start(); });
367 this.translator.Run(this.textBoxArticle.Text.Trim());
369 catch (ApplicationException)
371 // 中止要求で停止した場合、その旨イベントに格納する
372 e.Cancel = this.backgroundWorkerRun.CancellationPending;
378 this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Stop(); });
381 // 実行結果から、ログと変換後テキストをファイル出力
382 this.WriteResult(success);
384 catch (WebException ex)
386 // 想定外の通信エラー(↓とまとめてもよいが、こちらはサーバーの状況などで発生しやすいので)
387 this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageConnectionFailed, ex.Message) + Environment.NewLine);
388 if (ex.Response != null)
390 // 出せるならエラーとなったURLも出力
391 this.textBoxLog.AppendText(Resources.RightArrow + " " + String.Format(Resources.LogMessageErrorURL, ex.Response.ResponseUri) + Environment.NewLine);
397 this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageDevelopmentError, ex.Message, ex.StackTrace) + Environment.NewLine);
402 /// 実行ボタン バックグラウンド処理(終了時)。
404 /// <param name="sender">イベント発生オブジェクト。</param>
405 /// <param name="e">発生したイベント。</param>
406 private void BackgroundWorkerRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
409 // ※ 微妙に時間がかかるので、ステータスバーに通知
412 this.toolStripStatusLabelStatus.Text = Resources.StatusCacheUpdating;
419 this.toolStripStatusLabelStatus.Text = String.Empty;
424 FormUtils.WarningDialog(
425 Resources.WarningMessageCacheSaveFailed,
434 /// ステータスバー処理時間更新タイマー処理。
436 /// <param name="sender">イベント発生オブジェクト。</param>
437 /// <param name="e">発生したイベント。</param>
438 private void TimerStatusStopwatch_Tick(object sender, EventArgs e)
441 this.toolStripStatusLabelStopwatch.Text = String.Format(Resources.ElapsedTime, this.translator.Stopwatch.Elapsed);
451 private void Initialize()
454 this.comboBoxSource.Items.Clear();
455 this.comboBoxTarget.Items.Clear();
457 // 設定ファイルに存在する全言語を選択肢として登録する
458 foreach (Website site in this.config.Websites)
460 this.comboBoxSource.Items.Add(site.Language.Code);
461 this.comboBoxTarget.Items.Add(site.Language.Code);
468 /// <returns>読み込み成功時は<c>true</c>。</returns>
469 private bool LoadConfig()
472 // ※ 微妙に時間がかかるので、ステータスバーに通知
475 this.toolStripStatusLabelStatus.Text = Resources.StatusConfigReading;
478 this.config = Config.GetInstance(Settings.Default.ConfigurationFile);
482 this.toolStripStatusLabelStatus.Text = String.Empty;
485 catch (FileNotFoundException ex)
488 System.Diagnostics.Debug.WriteLine(
489 "MainForm.LoadConfig > 設定ファイル読み込み失敗 : " + ex.Message);
490 FormUtils.ErrorDialog(
491 Resources.ErrorMessageConfigNotFound,
492 Settings.Default.ConfigurationFile);
498 System.Diagnostics.Debug.WriteLine(
499 "MainForm.LoadConfig > 設定ファイル読み込み時エラー : " + ex.StackTrace);
500 FormUtils.ErrorDialog(
501 Resources.ErrorMessageConfigLordFailed,
513 private void LockOperation()
516 this.groupBoxTransfer.Enabled = false;
517 this.groupBoxSaveDirectory.Enabled = false;
518 this.textBoxArticle.Enabled = false;
519 this.buttonRun.Enabled = false;
522 this.buttonStop.Enabled = true;
528 private void Release()
531 this.buttonStop.Enabled = false;
534 this.groupBoxTransfer.Enabled = true;
535 this.groupBoxSaveDirectory.Enabled = true;
536 this.textBoxArticle.Enabled = true;
537 this.buttonRun.Enabled = true;
541 /// 翻訳支援処理のログ・変換後テキストをファイル出力。
543 /// <param name="success">翻訳支援処理が成功した場合<c>true</c>。</param>
544 private void WriteResult(bool success)
546 // 若干時間がかかるのでステータスバーに通知
547 this.toolStripStatusLabelStatus.Text = Resources.StatusFileWriting;
553 this.MakeFileName(out fileName, out logName, this.textBoxArticle.Text.Trim(), this.textBoxSaveDirectory.Text);
557 // 翻訳支援処理成功時は変換後テキストを出力
560 File.WriteAllText(Path.Combine(this.textBoxSaveDirectory.Text, fileName), this.translator.Text);
561 this.textBoxLog.AppendText(String.Format(Resources.LogMessageEnd, fileName, logName));
565 this.textBoxLog.AppendText(String.Format(Resources.LogMessageFileSaveFailed, Path.Combine(this.textBoxSaveDirectory.Text, fileName), ex.Message));
566 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStop, logName));
571 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStop, logName));
577 File.WriteAllText(Path.Combine(this.textBoxSaveDirectory.Text, logName), this.textBoxLog.Text);
581 this.textBoxLog.AppendText(String.Format(Resources.LogMessageFileSaveFailed, Path.Combine(this.textBoxSaveDirectory.Text, logName), ex.Message));
587 this.toolStripStatusLabelStatus.Text = String.Empty;
592 /// 渡された文字列から.txtと.logの重複していないファイル名を作成。
594 /// <param name="fileName">出力結果ファイル名。</param>
595 /// <param name="logName">出力ログファイル名。</param>
596 /// <param name="text">出力する結果テキスト。</param>
597 /// <param name="dir">出力先ディレクトリ。</param>
598 /// <returns><c>true</c> 出力成功</returns>
599 private bool MakeFileName(out string fileName, out string logName, string text, string dir)
601 // 出力先フォルダに存在しないファイル名(の拡張子より前)を作成
602 // ※渡されたWikipedia等の記事名にファイル名に使えない文字が含まれている場合、_ に置き換える
603 // また、ファイル名が重複している場合、xx[0].txtのように連番を付ける
604 string fileNameBase = FormUtils.ReplaceInvalidFileNameChars(text);
605 fileName = fileNameBase + ".txt";
606 logName = fileNameBase + ".log";
607 bool success = false;
608 for (int i = 0; i < 100000; i++)
610 // ※100000まで試して空きが見つからないことは無いはず、もし見つからなかったら最後のを上書き
611 if (!File.Exists(Path.Combine(dir, fileName))
612 && !File.Exists(Path.Combine(dir, logName)))
618 fileName = fileNameBase + "[" + i + "]" + ".txt";
619 logName = fileNameBase + "[" + i + "]" + ".log";
627 /// 翻訳支援処理クラスのログ更新イベント用。
629 /// <param name="sender">イベント発生オブジェクト。</param>
630 /// <param name="e">発生したイベント。</param>
631 private void GetLogUpdate(object sender, EventArgs e)
633 // 前回以降に追加されたログをテキストボックスに出力
634 int length = this.translator.Log.Length;
635 if (length > this.logLength)
637 this.textBoxLog.AppendText(this.translator.Log.Substring(this.logLength, length - this.logLength));
640 this.logLength = length;
644 /// 翻訳支援処理クラスの処理状態更新イベント用。
646 /// <param name="sender">イベント発生オブジェクト。</param>
647 /// <param name="e">発生したイベント。</param>
648 private void GetStatusUpdate(object sender, EventArgs e)
651 this.toolStripStatusLabelStatus.Text = this.translator.Status;