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.Threading;
22 using System.Windows.Forms;
23 using Honememo.Utilities;
24 using Honememo.Wptscs.Logics;
25 using Honememo.Wptscs.Models;
26 using Honememo.Wptscs.Properties;
27 using Honememo.Wptscs.Utilities;
28 using Honememo.Wptscs.Websites;
31 /// Wikipedia翻訳支援ツール主画面のクラスです。
33 public partial class MainForm : Form
38 /// 現在読み込んでいるアプリケーションの設定。
40 private Config config;
45 private Translator translator;
50 private int logLength;
57 /// コンストラクタ。初期化メソッド呼び出しのみ。
61 // Windows フォーム デザイナで生成されたコード
62 this.InitializeComponent();
72 /// <param name="sender">イベント発生オブジェクト。</param>
73 /// <param name="e">発生したイベント。</param>
74 private void MainForm_Load(object sender, EventArgs e)
77 if (!this.LoadConfig())
79 // 読み込み失敗時はどうしようもないのでそのまま終了
83 this.translator = null;
84 Control.CheckForIllegalCrossThreadCalls = false;
86 // コンボボックス・表示言語選択メニューの初期設定
87 this.InitializeComboBox();
88 this.InitializeDropDownButtonLanguage();
91 this.textBoxSaveDirectory.Text = Settings.Default.SaveDirectory;
92 this.comboBoxSource.SelectedItem = Settings.Default.LastSelectedSource;
93 this.comboBoxTarget.SelectedItem = Settings.Default.LastSelectedTarget;
97 /// フォームクローズ時の処理。処理状態を保存。
99 /// <param name="sender">イベント発生オブジェクト。</param>
100 /// <param name="e">発生したイベント。</param>
101 private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
103 // 現在の出力先フォルダ、翻訳元/先言語、また必要であれば表示言語を保存
108 /// 翻訳元コンボボックス変更時の処理。
110 /// <param name="sender">イベント発生オブジェクト。</param>
111 /// <param name="e">発生したイベント。</param>
112 private void ComboBoxSource_SelectedIndexChanged(object sender, EventArgs e)
115 this.labelSource.Text = String.Empty;
116 this.linkLabelSourceURL.Text = "http://";
117 if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
119 // その言語の、ユーザーが使用している言語での表示名を表示
120 // (日本語環境だったら日本語を、英語だったら英語を)
121 Language.LanguageName name;
122 this.labelSource.Text = String.Empty;
123 if (this.config.GetWebsite(this.comboBoxSource.Text) != null &&
124 this.config.GetWebsite(this.comboBoxSource.Text).Language.Names.TryGetValue(
125 Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName,
128 this.labelSource.Text = name.Name;
132 this.linkLabelSourceURL.Text = this.config.GetWebsite(
133 this.comboBoxSource.Text).Location;
138 /// リンクラベルのリンククリック時の処理。
140 /// <param name="sender">イベント発生オブジェクト。</param>
141 /// <param name="e">発生したイベント。</param>
142 private void LinkLabelSourceURL_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
145 System.Diagnostics.Process.Start(((LinkLabel)sender).Text);
149 /// 翻訳先コンボボックス変更時の処理。
151 /// <param name="sender">イベント発生オブジェクト。</param>
152 /// <param name="e">発生したイベント。</param>
153 private void ComboBoxTarget_SelectedIndexChanged(object sender, EventArgs e)
156 this.labelTarget.Text = String.Empty;
157 if (!String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
159 this.comboBoxTarget.Text = this.comboBoxTarget.Text.Trim().ToLower();
161 // その言語の、ユーザーが使用している言語での表示名を表示
162 // (日本語環境だったら日本語を、英語だったら英語を)
163 if (this.config.GetWebsite(this.comboBoxTarget.Text) != null)
165 this.labelTarget.Text = this.config.GetWebsite(this.comboBoxTarget.Text)
166 .Language.Names[Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName].Name;
174 /// <param name="sender">イベント発生オブジェクト。</param>
175 /// <param name="e">発生したイベント。</param>
176 private void ButtonConfig_Click(object sender, EventArgs e)
179 ConfigForm form = new ConfigForm(this.config);
182 // 戻ってきたら設定ファイルを再読み込み
183 // ※ キャンセル時もインスタンスは更新されてしまうので
187 string backupSourceSelected = this.comboBoxSource.Text;
188 string backupSourceTarget = this.comboBoxTarget.Text;
189 this.InitializeComboBox();
190 this.comboBoxSource.SelectedItem = backupSourceSelected;
191 this.comboBoxTarget.SelectedItem = backupSourceTarget;
197 /// <param name="sender">イベント発生オブジェクト。</param>
198 /// <param name="e">発生したイベント。</param>
199 private void ButtonSaveDirectory_Click(object sender, EventArgs e)
201 // フォルダ名が入力されている場合、それを初期位置に設定
202 if (!String.IsNullOrEmpty(this.textBoxSaveDirectory.Text))
204 this.folderBrowserDialogSaveDirectory.SelectedPath = this.textBoxSaveDirectory.Text;
208 if (this.folderBrowserDialogSaveDirectory.ShowDialog() == System.Windows.Forms.DialogResult.OK)
210 // フォルダが選択された場合、フォルダ名に選択されたフォルダを設定
211 this.textBoxSaveDirectory.Text = this.folderBrowserDialogSaveDirectory.SelectedPath;
216 /// 出力先テキストボックスフォーカス喪失時の処理。
218 /// <param name="sender">イベント発生オブジェクト。</param>
219 /// <param name="e">発生したイベント。</param>
220 private void TextBoxSaveDirectory_Leave(object sender, EventArgs e)
223 this.textBoxSaveDirectory.Text = this.textBoxSaveDirectory.Text.Trim();
229 /// <param name="sender">イベント発生オブジェクト。</param>
230 /// <param name="e">発生したイベント。</param>
231 private void ButtonRun_Click(object sender, EventArgs e)
234 if (String.IsNullOrWhiteSpace(this.comboBoxSource.Text))
236 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedSource);
237 this.comboBoxSource.Focus();
240 else if (String.IsNullOrWhiteSpace(this.comboBoxTarget.Text))
242 FormUtils.WarningDialog(Resources.WarningMessageNotSelectedTarget);
243 this.comboBoxTarget.Focus();
246 else if (!String.IsNullOrWhiteSpace(this.comboBoxSource.Text)
247 && this.comboBoxSource.Text == this.comboBoxTarget.Text)
249 FormUtils.WarningDialog(Resources.WarningMessageEqualsSourceAndTarget);
250 this.comboBoxTarget.Focus();
253 else if (String.IsNullOrWhiteSpace(this.textBoxSaveDirectory.Text))
255 FormUtils.WarningDialog(Resources.WarningMessageEmptySaveDirectory);
256 this.textBoxSaveDirectory.Focus();
259 else if (!Directory.Exists(this.textBoxSaveDirectory.Text))
261 FormUtils.WarningDialog(Resources.WarningMessageIgnoreSaveDirectory);
262 this.textBoxSaveDirectory.Focus();
265 else if (String.IsNullOrWhiteSpace(this.textBoxArticle.Text))
267 FormUtils.WarningDialog(Resources.WarningMessageEmptyArticle);
268 this.textBoxArticle.Focus();
273 this.LockOperation();
276 this.backgroundWorkerRun.RunWorkerAsync();
282 /// <param name="sender">イベント発生オブジェクト。</param>
283 /// <param name="e">発生したイベント。</param>
284 private void ButtonStop_Click(object sender, EventArgs e)
287 this.buttonStop.Enabled = false;
288 if (this.backgroundWorkerRun.IsBusy == true)
290 System.Diagnostics.Debug.WriteLine("MainForm.-Stop_Click > 処理中断");
291 this.backgroundWorkerRun.CancelAsync();
292 if (this.translator != null)
294 this.translator.CancellationPending = true;
300 /// 実行ボタン バックグラウンド処理(スレッド)。
302 /// <param name="sender">イベント発生オブジェクト。</param>
303 /// <param name="e">発生したイベント。</param>
304 private void BackgroundWorkerRun_DoWork(object sender, DoWorkEventArgs e)
308 // 初期化と開始メッセージ、別スレッドになるので表示言語も再度設定
309 Program.LoadSelectedCulture();
310 this.textBoxLog.Clear();
312 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStart, FormUtils.ApplicationName(), DateTime.Now));
314 // 翻訳支援処理ロジックのオブジェクトを生成
317 this.translator = Translator.Create(this.config, this.comboBoxSource.Text, this.comboBoxTarget.Text);
319 catch (NotImplementedException)
321 // 設定ファイルに対応していないパターンが書かれている場合の例外、将来の拡張用
322 this.textBoxLog.AppendText(String.Format(Resources.InformationMessageDevelopingMethod, "MediaWiki以外の処理"));
323 FormUtils.InformationDialog(Resources.InformationMessageDevelopingMethod, "MediaWiki以外の処理");
327 // ログ・処理状態更新通知を受け取るためのイベント登録
329 this.translator.LogUpdate += new EventHandler(this.GetLogUpdate);
330 this.translator.StatusUpdate += new EventHandler(this.GetStatusUpdate);
331 this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Start(); });
337 this.translator.Run(this.textBoxArticle.Text.Trim());
339 catch (ApplicationException)
341 // 中止要求で停止した場合、その旨イベントに格納する
342 e.Cancel = this.backgroundWorkerRun.CancellationPending;
348 this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Stop(); });
351 // 実行結果から、ログと変換後テキストをファイル出力
352 this.WriteResult(success);
354 catch (WebException ex)
356 // 想定外の通信エラー(↓とまとめてもよいが、こちらはサーバーの状況などで発生しやすいので)
357 this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageConnectionFailed, ex.Message) + Environment.NewLine);
358 if (ex.Response != null)
360 // 出せるならエラーとなったURLも出力
361 this.textBoxLog.AppendText(Resources.RightArrow + " " + String.Format(Resources.LogMessageErrorURL, ex.Response.ResponseUri) + Environment.NewLine);
367 this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageDevelopmentError, ex.Message, ex.StackTrace) + Environment.NewLine);
372 /// 実行ボタン バックグラウンド処理(終了時)。
374 /// <param name="sender">イベント発生オブジェクト。</param>
375 /// <param name="e">発生したイベント。</param>
376 private void BackgroundWorkerRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
379 // ※ 微妙に時間がかかるので、ステータスバーに通知
382 this.toolStripStatusLabelStatus.Text = Resources.StatusCacheUpdating;
389 this.toolStripStatusLabelStatus.Text = String.Empty;
394 FormUtils.WarningDialog(
395 Resources.WarningMessageCacheSaveFailed,
404 /// ステータスバー処理時間更新タイマー処理。
406 /// <param name="sender">イベント発生オブジェクト。</param>
407 /// <param name="e">発生したイベント。</param>
408 private void TimerStatusStopwatch_Tick(object sender, EventArgs e)
411 this.toolStripStatusLabelStopwatch.Text = String.Format(Resources.ElapsedTime, this.translator.Stopwatch.Elapsed);
415 /// 表示言語選択メニュー日本語クリック時の処理。
417 /// <param name="sender">イベント発生オブジェクト。</param>
418 /// <param name="e">発生したイベント。</param>
419 private void ToolStripMenuItemJapanese_Click(object sender, EventArgs e)
422 this.ChangeCultureAndRestart("ja-JP");
426 /// 表示言語選択メニュー英語(US)クリック時の処理。
428 /// <param name="sender">イベント発生オブジェクト。</param>
429 /// <param name="e">発生したイベント。</param>
430 private void ToolStripMenuItemEnglishUS_Click(object sender, EventArgs e)
432 // 表示言語を英語(US)に設定し再起動する
433 this.ChangeCultureAndRestart("en-US");
437 /// 表示言語選択メニュー英語(GB)クリック時の処理。
439 /// <param name="sender">イベント発生オブジェクト。</param>
440 /// <param name="e">発生したイベント。</param>
441 private void ToolStripMenuItemEnglishGB_Click(object sender, EventArgs e)
443 // 表示言語を英語(GB)に設定し再起動する
444 this.ChangeCultureAndRestart("en-GB");
448 /// 表示言語選択メニュー自動クリック時の処理。
450 /// <param name="sender">イベント発生オブジェクト。</param>
451 /// <param name="e">発生したイベント。</param>
452 private void ToolStripMenuItemAuto_Click(object sender, EventArgs e)
455 this.ChangeCultureAndRestart(String.Empty);
463 /// 翻訳元/先言語コンボボックスの初期化処理。
465 private void InitializeComboBox()
468 this.comboBoxSource.Items.Clear();
469 this.comboBoxTarget.Items.Clear();
471 // 設定ファイルに存在する全言語を選択肢として登録する
472 foreach (Website site in this.config.Websites)
474 this.comboBoxSource.Items.Add(site.Language.Code);
475 this.comboBoxTarget.Items.Add(site.Language.Code);
480 /// 表示言語選択メニューの初期化処理。
482 private void InitializeDropDownButtonLanguage()
485 ToolStripMenuItem item;
486 switch (Settings.Default.LastSelectedLanguage)
489 item = this.toolStripMenuItemEnglishUS;
492 item = this.toolStripMenuItemEnglishGB;
495 item = this.toolStripMenuItemJapanese;
498 item = this.toolStripMenuItemAuto;
502 // 選択中の項目をチェック状態&押下不能とする
504 item.Enabled = false;
505 if (item != this.toolStripMenuItemAuto)
507 // 自動以外の場合、ステータスバーの表示も更新
508 this.toolStripDropDownButtonLanguage.Text = item.Text;
515 /// <returns>読み込み成功時は<c>true</c>。</returns>
516 private bool LoadConfig()
519 // ※ 微妙に時間がかかるので、ステータスバーに通知
522 this.toolStripStatusLabelStatus.Text = Resources.StatusConfigReading;
525 this.config = Config.GetInstance(Settings.Default.ConfigurationFile);
529 this.toolStripStatusLabelStatus.Text = String.Empty;
532 catch (FileNotFoundException ex)
535 System.Diagnostics.Debug.WriteLine(
536 "MainForm.LoadConfig > 設定ファイル読み込み失敗 : " + ex.Message);
537 FormUtils.ErrorDialog(
538 Resources.ErrorMessageConfigNotFound,
539 Settings.Default.ConfigurationFile);
545 System.Diagnostics.Debug.WriteLine(
546 "MainForm.LoadConfig > 設定ファイル読み込み時エラー : " + ex.ToString());
547 FormUtils.ErrorDialog(
548 Resources.ErrorMessageConfigLordFailed,
560 private void SaveSettings()
562 // 現在の出力先フォルダ、翻訳元/先言語を保存
563 // ※ 表示言語については必要な場合のみ更新するため、変更したタイミングで更新、ここでは反映だけ
564 Settings.Default.SaveDirectory = this.textBoxSaveDirectory.Text;
565 Settings.Default.LastSelectedSource = this.comboBoxSource.Text;
566 Settings.Default.LastSelectedTarget = this.comboBoxTarget.Text;
567 Settings.Default.Save();
573 private void LockOperation()
576 this.groupBoxTransfer.Enabled = false;
577 this.groupBoxSaveDirectory.Enabled = false;
578 this.textBoxArticle.Enabled = false;
579 this.buttonRun.Enabled = false;
580 this.toolStripDropDownButtonLanguage.Enabled = false;
581 this.toolStripDropDownButtonConfig.Enabled = false;
584 this.buttonStop.Enabled = true;
590 private void Release()
593 this.buttonStop.Enabled = false;
596 this.groupBoxTransfer.Enabled = true;
597 this.groupBoxSaveDirectory.Enabled = true;
598 this.textBoxArticle.Enabled = true;
599 this.buttonRun.Enabled = true;
600 this.toolStripDropDownButtonLanguage.Enabled = true;
601 this.toolStripDropDownButtonConfig.Enabled = true;
605 /// 翻訳支援処理のログ・変換後テキストをファイル出力。
607 /// <param name="success">翻訳支援処理が成功した場合<c>true</c>。</param>
608 private void WriteResult(bool success)
610 // 若干時間がかかるのでステータスバーに通知
611 this.toolStripStatusLabelStatus.Text = Resources.StatusFileWriting;
617 this.MakeFileName(out fileName, out logName, this.textBoxArticle.Text.Trim(), this.textBoxSaveDirectory.Text);
621 // 翻訳支援処理成功時は変換後テキストを出力
624 File.WriteAllText(Path.Combine(this.textBoxSaveDirectory.Text, fileName), this.translator.Text);
625 this.textBoxLog.AppendText(String.Format(Resources.LogMessageEnd, fileName, logName));
629 this.textBoxLog.AppendText(String.Format(Resources.LogMessageFileSaveFailed, Path.Combine(this.textBoxSaveDirectory.Text, fileName), ex.Message));
630 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStop, logName));
635 this.textBoxLog.AppendText(String.Format(Resources.LogMessageStop, logName));
641 File.WriteAllText(Path.Combine(this.textBoxSaveDirectory.Text, logName), this.textBoxLog.Text);
645 this.textBoxLog.AppendText(String.Format(Resources.LogMessageFileSaveFailed, Path.Combine(this.textBoxSaveDirectory.Text, logName), ex.Message));
651 this.toolStripStatusLabelStatus.Text = String.Empty;
656 /// 渡された文字列から.txtと.logの重複していないファイル名を作成。
658 /// <param name="fileName">出力結果ファイル名。</param>
659 /// <param name="logName">出力ログファイル名。</param>
660 /// <param name="text">出力する結果テキスト。</param>
661 /// <param name="dir">出力先ディレクトリ。</param>
662 /// <returns><c>true</c> 出力成功</returns>
663 private bool MakeFileName(out string fileName, out string logName, string text, string dir)
665 // 出力先フォルダに存在しないファイル名(の拡張子より前)を作成
666 // ※渡されたWikipedia等の記事名にファイル名に使えない文字が含まれている場合、_ に置き換える
667 // また、ファイル名が重複している場合、xx[0].txtのように連番を付ける
668 string fileNameBase = FormUtils.ReplaceInvalidFileNameChars(text);
669 fileName = fileNameBase + ".txt";
670 logName = fileNameBase + ".log";
671 bool success = false;
672 for (int i = 0; i < 100000; i++)
674 // ※100000まで試して空きが見つからないことは無いはず、もし見つからなかったら最後のを上書き
675 if (!File.Exists(Path.Combine(dir, fileName))
676 && !File.Exists(Path.Combine(dir, logName)))
682 fileName = fileNameBase + "[" + i + "]" + ".txt";
683 logName = fileNameBase + "[" + i + "]" + ".log";
691 /// 翻訳支援処理クラスのログ更新イベント用。
693 /// <param name="sender">イベント発生オブジェクト。</param>
694 /// <param name="e">発生したイベント。</param>
695 private void GetLogUpdate(object sender, EventArgs e)
697 // 前回以降に追加されたログをテキストボックスに出力
698 int length = this.translator.Log.Length;
699 if (length > this.logLength)
701 this.textBoxLog.AppendText(this.translator.Log.Substring(this.logLength, length - this.logLength));
704 this.logLength = length;
708 /// 翻訳支援処理クラスの処理状態更新イベント用。
710 /// <param name="sender">イベント発生オブジェクト。</param>
711 /// <param name="e">発生したイベント。</param>
712 private void GetStatusUpdate(object sender, EventArgs e)
715 this.toolStripStatusLabelStatus.Text = this.translator.Status;
719 /// アプリケーションの現在の表示言語を変更、再起動する。
721 /// <param name="name">変更先カルチャ名。</param>
722 private void ChangeCultureAndRestart(string name)
724 // 表示言語設定を保存した後、アプリケーションを再起動
725 Settings.Default.LastSelectedLanguage = name;
727 Application.Restart();