OSDN Git Service

#30244 自動テスト用svn:ignoreの指定ミスを修正
[wptscs/wpts.git] / Wptscs / ConfigForm.cs
index 91b136d..ef25cfd 100644 (file)
@@ -3,7 +3,7 @@
 //      Wikipedia翻訳支援ツール設定画面クラスソース</summary>
 //
 // <copyright file="ConfigForm.cs" company="honeplusのメモ帳">
-//      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 // <author>
 //      Honeplus</author>
 // ================================================================================================
@@ -15,12 +15,16 @@ namespace Honememo.Wptscs
     using System.ComponentModel;
     using System.Data;
     using System.Drawing;
+    using System.Linq;
     using System.Reflection;
     using System.Text;
+    using System.Threading;
     using System.Windows.Forms;
     using Honememo.Utilities;
     using Honememo.Wptscs.Models;
     using Honememo.Wptscs.Properties;
+    using Honememo.Wptscs.Utilities;
+    using Honememo.Wptscs.Websites;
 
     /// <summary>
     /// Wikipedia翻訳支援ツール設定画面のクラスです。
@@ -54,7 +58,7 @@ namespace Honememo.Wptscs
             this.InitializeComponent();
 
             // 設定対象のConfigを受け取る
-            this.config = Utilities.Validate.NotNull(config, "config");
+            this.config = Honememo.Utilities.Validate.NotNull(config, "config");
         }
 
         #endregion
@@ -88,6 +92,8 @@ namespace Honememo.Wptscs
                 this.textBoxCacheExpire.Text = Settings.Default.CacheExpire.Days.ToString();
                 this.textBoxUserAgent.Text = Settings.Default.UserAgent;
                 this.textBoxReferer.Text = Settings.Default.Referer;
+                this.textBoxMaxConnectRetries.Text = Settings.Default.MaxConnectRetries.ToString();
+                this.textBoxConnectRetryTime.Text = Settings.Default.ConnectRetryTime.ToString();
                 this.checkBoxIgnoreError.Checked = Settings.Default.IgnoreError;
                 this.labelApplicationName.Text = FormUtils.ApplicationName();
                 AssemblyCopyrightAttribute copyright = Attribute.GetCustomAttribute(
@@ -130,23 +136,24 @@ namespace Honememo.Wptscs
                 Settings.Default.CacheExpire = new TimeSpan(int.Parse(this.textBoxCacheExpire.Text), 0, 0, 0);
                 Settings.Default.UserAgent = this.textBoxUserAgent.Text;
                 Settings.Default.Referer = this.textBoxReferer.Text;
+                Settings.Default.MaxConnectRetries = int.Parse(this.textBoxMaxConnectRetries.Text);
+                Settings.Default.ConnectRetryTime = int.Parse(this.textBoxConnectRetryTime.Text);
                 Settings.Default.IgnoreError = this.checkBoxIgnoreError.Checked;
 
                 // 設定をファイルに保存
                 Settings.Default.Save();
                 try
                 {
-                    this.config.Save(Settings.Default.ConfigurationFile);
+                    this.config.Save();
 
                     // 全部成功なら画面を閉じる
                     // ※ エラーの場合、どうしても駄目ならキャンセルボタンで閉じてもらう
-                    this.Close();
+                    this.DialogResult = DialogResult.OK;
                 }
                 catch (Exception ex)
                 {
                     // 異常時はエラーメッセージを表示
-                    // ※ この場合でもConfigオブジェクトは更新済みのため設定は一時的に有効
-                    System.Diagnostics.Debug.WriteLine(ex.StackTrace);
+                    System.Diagnostics.Debug.WriteLine(ex.ToString());
                     FormUtils.ErrorDialog(Resources.ErrorMessageConfigSaveFailed, ex.Message);
                 }
             }
@@ -194,7 +201,7 @@ namespace Honememo.Wptscs
             // 空または日付として認識可能な値の場合OK
             string value = e.FormattedValue.ToString();
             DateTime dummy;
-            if (String.IsNullOrWhiteSpace(value) || DateTime.TryParse(value, out dummy))
+            if (string.IsNullOrWhiteSpace(value) || DateTime.TryParse(value, out dummy))
             {
                 return;
             }
@@ -215,7 +222,7 @@ namespace Honememo.Wptscs
             // ※ 他の列で消さないのは、エラーを出しているのがRowValidatingの場合もあるから
             if (this.dataGridViewItems.Columns[e.ColumnIndex].Name == "ColumnTimestamp")
             {
-                this.dataGridViewItems.Rows[e.RowIndex].ErrorText = String.Empty;
+                this.dataGridViewItems.Rows[e.RowIndex].ErrorText = string.Empty;
             }
         }
 
@@ -230,15 +237,19 @@ namespace Honememo.Wptscs
             // ※ ただし全列が空(新規行など)の場合は無視
             if (e.RowIndex >= 0)
             {
-                string value = FormUtils.ToString(this.dataGridViewItems["ColumnTimestamp", e.RowIndex]);
-                if (String.IsNullOrWhiteSpace(value)
-                    && !this.IsEmptyDataGridViewItemsRow(this.dataGridViewItems.Rows[e.RowIndex]))
+                DataGridViewRow row = this.dataGridViewItems.Rows[e.RowIndex];
+                if (string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnTimestamp"]))
+                    && !this.IsEmptyDataGridViewItemsRow(row))
                 {
-                    this.dataGridViewItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.Bisque;
+                    // 背景色を変更
+                    row.DefaultCellStyle.BackColor = Color.Bisque;
                 }
-                else
+                else if (row.InheritedStyle.BackColor != this.dataGridViewItems.DefaultCellStyle.BackColor)
                 {
-                    this.dataGridViewItems.Rows[e.RowIndex].DefaultCellStyle.BackColor = this.dataGridViewItems.DefaultCellStyle.BackColor;
+                    // 背景色を戻す
+                    // ※ DefaultCellStyleプロパティにアクセスしたタイミングでインスタンスが
+                    //    作成されてしまうため、InheritedStyleを調べて変更が必要な場合だけアクセス
+                    row.DefaultCellStyle.BackColor = this.dataGridViewItems.DefaultCellStyle.BackColor;
                 }
             }
         }
@@ -253,9 +264,9 @@ namespace Honememo.Wptscs
             // 翻訳元、記事名、翻訳先が未入力の場合、バリデートNGメッセージを表示
             // ※ ただし全列が空(新規行など)の場合は無視
             DataGridViewRow row = this.dataGridViewItems.Rows[e.RowIndex];
-            if ((String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromCode"]))
-                || String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToCode"]))
-                || String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromTitle"])))
+            if ((string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromCode"]))
+                || string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToCode"]))
+                || string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromTitle"])))
                 && !this.IsEmptyDataGridViewItemsRow(row))
             {
                 row.ErrorText = Resources.WarningMessageEmptyTranslationDictionary;
@@ -330,7 +341,7 @@ namespace Honememo.Wptscs
                 };
 
                 string timestamp = FormUtils.ToString(row.Cells["ColumnTimestamp"]);
-                if (!String.IsNullOrWhiteSpace(timestamp))
+                if (!string.IsNullOrWhiteSpace(timestamp))
                 {
                     item.Timestamp = DateTime.Parse(timestamp);
 
@@ -354,12 +365,12 @@ namespace Honememo.Wptscs
         /// <returns>空の場合<c>true</c>。</returns>
         private bool IsEmptyDataGridViewItemsRow(DataGridViewRow row)
         {
-            return String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromCode"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromTitle"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnAlias"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToCode"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToTitle"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnTimestamp"]));
+            return string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromCode"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnFromTitle"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnAlias"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToCode"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnToTitle"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnTimestamp"]));
         }
 
         #endregion
@@ -383,12 +394,12 @@ namespace Honememo.Wptscs
             }
 
             // 各行にデータを取り込み
-            foreach (IDictionary<string, string> record in table)
+            foreach (IDictionary<string, string[]> record in table)
             {
                 // 行を追加しその行を取得
                 DataGridViewRow row = view.Rows[view.Rows.Add()];
 
-                foreach (KeyValuePair<string, string> cell in record)
+                foreach (KeyValuePair<string, string[]> cell in record)
                 {
                     // 上で登録した列では足りなかった場合、その都度生成する
                     if (!view.Columns.Contains(cell.Key))
@@ -396,13 +407,22 @@ namespace Honememo.Wptscs
                         this.AddTranslationTableColumn(view.Columns, cell.Key, cell.Key);
                     }
 
-                    row.Cells[cell.Key].Value = cell.Value;
+                    // 改行区切りで表示
+                    row.Cells[cell.Key].Value = string.Join("\n", cell.Value);
                 }
             }
 
-            // 列幅をデータ長に応じて自動調整
-            // ※ 常に行ってしまうと、読み込みに時間がかかるため
-            view.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
+            // 可能であれば現在表示中の言語の列の昇順でソートする
+            // ※ 無ければenで試みる
+            string code = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName;
+            if (view.Columns.Contains(code))
+            {
+                view.Sort(view.Columns[code], ListSortDirection.Ascending);
+            }
+            else if (view.Columns.Contains("en"))
+            {
+                view.Sort(view.Columns["en"], ListSortDirection.Ascending);
+            }
         }
 
         /// <summary>
@@ -415,14 +435,15 @@ namespace Honememo.Wptscs
             TranslationTable table = new TranslationTable();
             foreach (DataGridViewRow row in view.Rows)
             {
-                IDictionary<string, string> record = new SortedDictionary<string, string>();
+                IDictionary<string, string[]> record = new SortedDictionary<string, string[]>();
                 foreach (DataGridViewCell cell in row.Cells)
                 {
                     // 空のセルは格納しない、該当の組み合わせは消える
                     string value = FormUtils.ToString(cell);
-                    if (!String.IsNullOrWhiteSpace(value))
+                    if (!string.IsNullOrWhiteSpace(value))
                     {
-                        record[cell.OwningColumn.Name] = value;
+                        // 改行区切りの配列で格納
+                        record[cell.OwningColumn.Name] = CollectionUtils.Trim(value.Split('\n'));
                     }
                 }
 
@@ -456,11 +477,11 @@ namespace Honememo.Wptscs
         {
             Language.LanguageName name;
             if (lang.Names.TryGetValue(
-                System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName, out name))
+                Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName, out name))
             {
-                if (!String.IsNullOrEmpty(name.Name))
+                if (!string.IsNullOrEmpty(name.Name))
                 {
-                    return String.Format(Resources.HeadingViewHeaderText, name.Name, lang.Code);
+                    return string.Format(Resources.HeadingViewHeaderText, name.Name, lang.Code);
                 }
             }
 
@@ -481,34 +502,35 @@ namespace Honememo.Wptscs
             try
             {
                 // 変更前の設定を保存
-                if (!String.IsNullOrEmpty(this.comboBoxLanguageSelectedText))
+                if (!string.IsNullOrEmpty(this.comboBoxLanguageSelectedText))
                 {
                     // 設定が存在しなければ自動生成される
                     this.SaveChangedValue(this.GetMediaWikiNeedCreate(this.config.Websites, this.comboBoxLanguageSelectedText));
                 }
 
                 // 変更後の値に応じて、画面表示を更新
-                string code = ObjectUtils.ToString(this.comboBoxLanguage.SelectedItem).Trim();
-                if (!String.IsNullOrEmpty(code))
+                if (!string.IsNullOrEmpty(this.comboBoxLanguage.Text))
                 {
                     // 設定が存在しなければ基本的に自動生成されるのでそのまま使用
-                    this.LoadCurrentValue(this.GetMediaWikiNeedCreate(this.config.Websites, code));
+                    this.LoadCurrentValue(this.GetMediaWikiNeedCreate(this.config.Websites, this.comboBoxLanguage.Text));
 
                     // 各入力欄を有効に
+                    this.buttonLanguageRemove.Enabled = true;
                     this.groupBoxServer.Enabled = true;
                     this.groupBoxLanguage.Enabled = true;
 
                     // 現在の選択値を更新
-                    this.comboBoxLanguageSelectedText = code;
+                    this.comboBoxLanguageSelectedText = this.comboBoxLanguage.Text;
                 }
                 else
                 {
                     // 各入力欄を無効に
+                    this.buttonLanguageRemove.Enabled = false;
                     this.groupBoxServer.Enabled = false;
                     this.groupBoxLanguage.Enabled = false;
 
                     // 現在の選択値を更新
-                    this.comboBoxLanguageSelectedText = String.Empty;
+                    this.comboBoxLanguageSelectedText = string.Empty;
                 }
             }
             catch (Exception ex)
@@ -519,46 +541,47 @@ namespace Honememo.Wptscs
         }
 
         /// <summary>
-        /// è¨\80èª\9eã\82³ã\83³ã\83\9cã\83\9cã\83\83ã\82¯ã\82¹ã\82­ã\83¼å\85¥å\8a\9b時の処理。
+        /// è¨\80èª\9eã\81®è¿½å\8a ã\83\9cã\82¿ã\83³æ\8a¼ä¸\8b時の処理。
         /// </summary>
         /// <param name="sender">イベント発生オブジェクト。</param>
         /// <param name="e">発生したイベント。</param>
-        private void ComboBoxLanguage_KeyDown(object sender, KeyEventArgs e)
+        private void ButtonLunguageAdd_Click(object sender, EventArgs e)
         {
-            // エンターキーが押された場合、現在の値が一覧に無ければ登録する(フォーカスを失ったときの処理)
-            if (e.KeyCode == Keys.Enter)
+            // 言語追加用ダイアログで言語コードを入力
+            using (AddLanguageDialog form = new AddLanguageDialog(this.config))
             {
-                System.Diagnostics.Debug.WriteLine("ComboBoxLanguage::_KeyDown > " + this.comboBoxLanguage.Text);
-                this.ComboBoxLanguage_Leave(sender, e);
+                if (form.ShowDialog() == DialogResult.OK)
+                {
+                    // 値をコンボボックスと見出しの対訳表に追加、登録した値を選択状態に変更
+                    this.comboBoxLanguage.Items.Add(form.LanguageCode);
+                    this.dataGridViewHeading.Columns.Add(form.LanguageCode, form.LanguageCode);
+                    this.comboBoxLanguage.SelectedItem = form.LanguageCode;
+                }
             }
         }
 
         /// <summary>
-        /// è¨\80èª\9eã\82³ã\83³ã\83\9cã\83\9cã\83\83ã\82¯ã\82¹ã\83\95ã\82©ã\83¼ã\82«ã\82¹å\96ªå¤±時の処理。
+        /// è¨\80èª\9eã\81®å\89\8aé\99¤ã\83\9cã\82¿ã\83³æ\8a¼ä¸\8b時の処理。
         /// </summary>
         /// <param name="sender">イベント発生オブジェクト。</param>
         /// <param name="e">発生したイベント。</param>
-        private void ComboBoxLanguage_Leave(object sender, EventArgs e)
+        private void ButtonLanguageRemove_Click(object sender, EventArgs e)
         {
-            System.Diagnostics.Debug.WriteLine("ComboBoxLanguage::_Leave > " + this.comboBoxLanguage.Text);
-
-            this.comboBoxLanguage.Text = this.comboBoxLanguage.Text.Trim().ToLower();
-            if (String.IsNullOrEmpty(this.comboBoxLanguage.Text))
+            // 表示されている言語を設定から削除する
+            for (int i = this.config.Websites.Count - 1; i >= 0; i--)
             {
-                // 空にしたとき、変更でイベントが起こらないようなので、強制的に呼ぶ
-                this.ComboBoxLanguuage_SelectedIndexChanged(sender, e);
+                if (this.config.Websites[i].Language.Code == this.comboBoxLanguage.Text)
+                {
+                    // 万が一複数あれば全て削除
+                    this.config.Websites.RemoveAt(i);
+                }
             }
-            else if (!this.comboBoxLanguage.Items.Contains(this.comboBoxLanguage.Text))
-            {
-                // 現在の値が一覧に無ければ登録する
-                this.comboBoxLanguage.Items.Add(this.comboBoxLanguage.Text);
 
-                // 登録した場合、見出しの対訳表にも列を追加
-                this.dataGridViewHeading.Columns.Add(this.comboBoxLanguage.Text, this.comboBoxLanguage.Text);
-
-                // 登録した値を選択状態に変更
-                this.comboBoxLanguage.SelectedItem = this.comboBoxLanguage.Text;
-            }
+            // 値を見出しの対訳表とコンボボックスからも削除し、表示を更新する
+            this.comboBoxLanguageSelectedText = null;
+            this.dataGridViewHeading.Columns.Remove(this.comboBoxLanguage.Text);
+            this.comboBoxLanguage.Items.Remove(this.comboBoxLanguage.Text);
+            this.ComboBoxLanguuage_SelectedIndexChanged(sender, e);
         }
 
         /// <summary>
@@ -572,7 +595,7 @@ namespace Honememo.Wptscs
             TextBox box = (TextBox)sender;
             box.Text = StringUtils.DefaultString(box.Text).Trim();
             int value;
-            if (!String.IsNullOrEmpty(box.Text) && !int.TryParse(box.Text, out value))
+            if (!string.IsNullOrEmpty(box.Text) && !int.TryParse(box.Text, out value))
             {
                 this.errorProvider.SetError(box, Resources.WarningMessageIgnoreNumericNamespace);
                 e.Cancel = true;
@@ -580,6 +603,22 @@ namespace Honememo.Wptscs
         }
 
         /// <summary>
+        /// 括弧のスタイルボックスバリデート処理。
+        /// </summary>
+        /// <param name="sender">イベント発生オブジェクト。</param>
+        /// <param name="e">発生したイベント。</param>
+        private void TextBoxBracket_Validating(object sender, CancelEventArgs e)
+        {
+            // 空か$1が含まれる文字列のみ許可
+            TextBox box = (TextBox)sender;
+            if (!string.IsNullOrEmpty(box.Text) && !box.Text.Contains("$1"))
+            {
+                this.errorProvider.SetError(box, Resources.WarningMessageUnformatedBracket);
+                e.Cancel = true;
+            }
+        }
+
+        /// <summary>
         /// 言語の設定表の行編集時のバリデート処理。
         /// </summary>
         /// <param name="sender">イベント発生オブジェクト。</param>
@@ -597,7 +636,7 @@ namespace Honememo.Wptscs
             // 言語コードは必須、またトリムして小文字に変換
             string code = FormUtils.ToString(row.Cells["ColumnCode"]).Trim().ToLower();
             row.Cells["ColumnCode"].Value = code;
-            if (String.IsNullOrEmpty(code))
+            if (string.IsNullOrEmpty(code))
             {
                 row.ErrorText = Resources.WarningMessageEmptyCodeColumn;
                 e.Cancel = true;
@@ -605,8 +644,8 @@ namespace Honememo.Wptscs
             }
 
             // 略称を設定する場合、呼称を必須とする
-            if (!String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnShortName"]))
-                && String.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnName"])))
+            if (!string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnShortName"]))
+                && string.IsNullOrWhiteSpace(FormUtils.ToString(row.Cells["ColumnName"])))
             {
                 row.ErrorText = Resources.WarningMessageShortNameColumnOnly;
                 e.Cancel = true;
@@ -694,6 +733,9 @@ namespace Honememo.Wptscs
                 this.dataGridViewLanguageName["ColumnName", index].Value = name.Value.Name;
                 this.dataGridViewLanguageName["ColumnShortName", index].Value = name.Value.ShortName;
             }
+
+            // 言語コードの昇順でソート
+            this.dataGridViewLanguageName.Sort(this.dataGridViewLanguageName.Columns["ColumnCode"], ListSortDirection.Ascending);
         }
 
         /// <summary>
@@ -721,13 +763,24 @@ namespace Honememo.Wptscs
 
             // MediaWikiクラス分の読み込み
             this.textBoxExportPath.Text = StringUtils.DefaultString(site.ExportPath);
-            this.textBoxNamespacePath.Text = StringUtils.DefaultString(site.NamespacePath);
+            this.textBoxMetaApi.Text = StringUtils.DefaultString(site.MetaApi);
             this.textBoxTemplateNamespace.Text = site.TemplateNamespace.ToString();
             this.textBoxCategoryNamespace.Text = site.CategoryNamespace.ToString();
             this.textBoxFileNamespace.Text = site.FileNamespace.ToString();
             this.textBoxRedirect.Text = StringUtils.DefaultString(site.Redirect);
-            this.textBoxDocumentationTemplate.Text = StringUtils.DefaultString(site.DocumentationTemplate);
+
+            // Template:Documentionは改行区切りのマルチテキストとして扱う
+            StringBuilder b = new StringBuilder();
+            foreach (string s in site.DocumentationTemplates)
+            {
+                b.Append(s).Append(Environment.NewLine);
+            }
+
+            this.textBoxDocumentationTemplate.Text = b.ToString();
             this.textBoxDocumentationTemplateDefaultPage.Text = StringUtils.DefaultString(site.DocumentationTemplateDefaultPage);
+            this.textBoxLinkInterwikiFormat.Text = StringUtils.DefaultString(site.LinkInterwikiFormat);
+            this.textBoxLangFormat.Text = StringUtils.DefaultString(site.LangFormat);
+            this.checkBoxHasLanguagePage.Checked = site.HasLanguagePage;
         }
 
         /// <summary>
@@ -746,13 +799,12 @@ namespace Honememo.Wptscs
             }
 
             // 表から呼称の情報も保存
-            this.dataGridViewLanguageName.Sort(this.dataGridViewLanguageName.Columns["ColumnCode"], ListSortDirection.Ascending);
             lang.Names.Clear();
             for (int y = 0; y < this.dataGridViewLanguageName.RowCount - 1; y++)
             {
                 // 値が入ってないとかはガードしているはずだが、一応チェック
                 string code = FormUtils.ToString(this.dataGridViewLanguageName["ColumnCode", y]).Trim();
-                if (!String.IsNullOrEmpty(code))
+                if (!string.IsNullOrEmpty(code))
                 {
                     Language.LanguageName name = new Language.LanguageName();
                     name.Name = FormUtils.ToString(this.dataGridViewLanguageName["ColumnName", y]).Trim();
@@ -787,7 +839,7 @@ namespace Honememo.Wptscs
             this.SaveChangedValue((Website)site);
 
             // 初期値を持つパラメータがあるため、全て変更された場合のみ格納する。
-            // â\80» ã\82\82ã\81\86ã\81¡ã\82\87ã\81£ã\81¨ç¶ºéº\97ã\81«æ\9b¸ã\81\8dã\81\9fã\81\8bã\81£ã\81\9fã\81\8cã\80\81ã\83ªã\83\95ã\83¬ã\82¯ã\82·ã\83§ã\83³ã\82\92使ã\82\8fã\81ªã\81\84ã\81¨å\85±é\80\9aå\8c\96ã\81§ã\81\8dã\81ªã\81\95ã\81\9dã\81\86ã\81 ったので力技
+            // â\80» ã\82\82ã\81\86ã\81¡ã\82\87ã\81£ã\81¨ç¶ºéº\97ã\81«æ\9b¸ã\81\8dã\81\9fã\81\8bã\81£ã\81\9fã\81\8cã\80\81ã\81\86ã\81¾ã\81\84æ\89\8bã\81\8cæ\80\9dã\81\84ã\81¤ã\81\8bã\81ªã\81\8bったので力技
             //    MediaWikiクラス側で行わないのは、場合によっては意図的に初期値と同じ値を設定すること
             //    もありえるから(初期値が変わる可能性がある場合など)。
             string str = StringUtils.DefaultString(this.textBoxExportPath.Text).Trim();
@@ -796,10 +848,10 @@ namespace Honememo.Wptscs
                 site.ExportPath = str;
             }
             
-            str = StringUtils.DefaultString(this.textBoxNamespacePath.Text).Trim();
-            if (str != site.NamespacePath)
+            str = StringUtils.DefaultString(this.textBoxMetaApi.Text).Trim();
+            if (str != site.MetaApi)
             {
-                site.NamespacePath = str;
+                site.MetaApi = str;
             }
 
             str = StringUtils.DefaultString(this.textBoxRedirect.Text).Trim();
@@ -808,10 +860,15 @@ namespace Honememo.Wptscs
                 site.Redirect = str;
             }
 
-            str = StringUtils.DefaultString(this.textBoxDocumentationTemplate.Text).Trim();
-            if (str != site.DocumentationTemplate)
+            // Template:Documentionの設定は行ごとに格納
+            // ※ この値は初期値を持たないパラメータ
+            site.DocumentationTemplates.Clear();
+            foreach (string s in StringUtils.DefaultString(this.textBoxDocumentationTemplate.Text).Split('\n'))
             {
-                site.DocumentationTemplate = str;
+                if (!string.IsNullOrWhiteSpace(s))
+                {
+                    site.DocumentationTemplates.Add(s.Trim());
+                }
             }
 
             str = StringUtils.DefaultString(this.textBoxDocumentationTemplateDefaultPage.Text).Trim();
@@ -820,8 +877,22 @@ namespace Honememo.Wptscs
                 site.DocumentationTemplateDefaultPage = str;
             }
 
+            str = StringUtils.DefaultString(this.textBoxLinkInterwikiFormat.Text).Trim();
+            if (str != site.LinkInterwikiFormat)
+            {
+                site.LinkInterwikiFormat = str;
+            }
+
+            str = StringUtils.DefaultString(this.textBoxLangFormat.Text).Trim();
+            if (str != site.LangFormat)
+            {
+                site.LangFormat = str;
+            }
+
+            site.HasLanguagePage = this.checkBoxHasLanguagePage.Checked;
+
             // 以下、数値へのparseは事前にチェックしてあるので、ここではチェックしない
-            if (!String.IsNullOrWhiteSpace(this.textBoxTemplateNamespace.Text))
+            if (!string.IsNullOrWhiteSpace(this.textBoxTemplateNamespace.Text))
             {
                 int num = int.Parse(this.textBoxTemplateNamespace.Text);
                 if (site.TemplateNamespace != num)
@@ -830,7 +901,7 @@ namespace Honememo.Wptscs
                 }
             }
 
-            if (!String.IsNullOrWhiteSpace(this.textBoxCategoryNamespace.Text))
+            if (!string.IsNullOrWhiteSpace(this.textBoxCategoryNamespace.Text))
             {
                 int num = int.Parse(this.textBoxCategoryNamespace.Text);
                 if (site.CategoryNamespace != num)
@@ -839,7 +910,7 @@ namespace Honememo.Wptscs
                 }
             }
 
-            if (!String.IsNullOrWhiteSpace(this.textBoxFileNamespace.Text))
+            if (!string.IsNullOrWhiteSpace(this.textBoxFileNamespace.Text))
             {
                 int num = int.Parse(this.textBoxFileNamespace.Text);
                 if (site.FileNamespace != num)
@@ -856,20 +927,36 @@ namespace Honememo.Wptscs
         #region その他タブのイベントのメソッド
         
         /// <summary>
-        /// キャッシュ有効期限ボックスバリデート処理。
+        /// キャッシュ有効期限ボックスバリデート処理。
         /// </summary>
         /// <param name="sender">イベント発生オブジェクト。</param>
         /// <param name="e">発生したイベント。</param>
         private void TextBoxCacheExpire_Validating(object sender, CancelEventArgs e)
         {
-            TextBox box = (TextBox)sender;
-            box.Text = StringUtils.DefaultString(box.Text).Trim();
-            int expire;
-            if (!int.TryParse(box.Text, out expire) || expire < 0)
-            {
-                this.errorProvider.SetError(box, Resources.WarningMessageIgnoreCacheExpire);
-                e.Cancel = true;
-            }
+            // 値が0以上の数値かをチェック
+            this.TextBoxGreaterThanValidating((TextBox)sender, e, 0, Resources.WarningMessageIgnoreCacheExpire);
+        }
+
+        /// <summary>
+        /// リトライ回数ボックスバリデート処理。
+        /// </summary>
+        /// <param name="sender">イベント発生オブジェクト。</param>
+        /// <param name="e">発生したイベント。</param>
+        private void TextBoxMaxConnectRetries_Validating(object sender, CancelEventArgs e)
+        {
+            // 値が0以上の数値かをチェック
+            this.TextBoxGreaterThanValidating((TextBox)sender, e, 0, Resources.WarningMessageIgnoreMaxConnectRetries);
+        }
+
+        /// <summary>
+        /// ウェイト時間ボックスバリデート処理。
+        /// </summary>
+        /// <param name="sender">イベント発生オブジェクト。</param>
+        /// <param name="e">発生したイベント。</param>
+        private void TextBoxConnectRetryTime_Validating(object sender, CancelEventArgs e)
+        {
+            // 値が0以上の数値かをチェック
+            this.TextBoxGreaterThanValidating((TextBox)sender, e, 0, Resources.WarningMessageIgnoreConnectRetryTime);
         }
 
         /// <summary>
@@ -883,10 +970,32 @@ namespace Honememo.Wptscs
             System.Diagnostics.Process.Start(((LinkLabel)sender).Text);
         }
 
+        #region イベント実装支援用メソッド
+
+        /// <summary>
+        /// メッセージのみ差し替え可能なテキストボックス用の値がxx以上の数値か、のバリデート処理。
+        /// </summary>
+        /// <param name="box">イベント発生テキストボックス。</param>
+        /// <param name="e">発生したイベント。</param>
+        /// <param name="num">比較対象の数値。</param>
+        /// <param name="message">バリデートメッセージ。</param>
+        private void TextBoxGreaterThanValidating(TextBox box, CancelEventArgs e, int num, string message)
+        {
+            box.Text = StringUtils.DefaultString(box.Text).Trim();
+            int value;
+            if (!int.TryParse(box.Text, out value) || value < num)
+            {
+                this.errorProvider.SetError(box, message);
+                e.Cancel = true;
+            }
+        }
+
+        #endregion
+
         #endregion
 
         #region 共通のイベントメソッド
-        
+
         /// <summary>
         /// 汎用のエラープロバイダ初期化処理。
         /// </summary>
@@ -904,7 +1013,7 @@ namespace Honememo.Wptscs
         /// <param name="e">発生したイベント。</param>
         private void ResetErrorText_RowValidated(object sender, DataGridViewCellEventArgs e)
         {
-            ((DataGridView)sender).Rows[e.RowIndex].ErrorText = String.Empty;
+            ((DataGridView)sender).Rows[e.RowIndex].ErrorText = string.Empty;
         }
 
         /// <summary>
@@ -917,7 +1026,7 @@ namespace Honememo.Wptscs
             // 全行のエラーメッセージを解除
             foreach (DataGridViewRow row in ((DataGridView)sender).Rows)
             {
-                row.ErrorText = String.Empty;
+                row.ErrorText = string.Empty;
             }
         }
 
@@ -928,33 +1037,50 @@ namespace Honememo.Wptscs
         /// <summary>
         /// 記事の置き換え対訳表の日付並び替え用クラスです。
         /// </summary>
-        /// <remarks>取得日時の降順でソート、空の列は先頭にします。</remarks>
+        /// <remarks>
+        /// 取得日時の降順でソート、空の列は先頭にします。
+        /// 取得日時が同じ場合、先頭の列から順に昇順でソート。
+        /// </remarks>
         public class TranslationDictionaryViewComparer : System.Collections.IComparer
         {
             /// <summary>
+            /// 取得日時が同じ場合にソートに用いる列名。
+            /// </summary>
+            private static readonly string[] SortOrder = new string[] { "ColumnFromCode", "ColumnToCode", "ColumnFromTitle" };
+
+            /// <summary>
             /// 2行を比較し、一方が他方より小さいか、等しいか、大きいかを示す値を返します。
             /// </summary>
-            /// <param name="y">比較する最初の行です。</param>
-            /// <param name="x">比較する 2 番目の行。</param>
-            /// <returns>1以下:xはyより小さい, 0:等しい, 1以上:xはyより大きい</returns>
-            public int Compare(object y, object x)
-            {
-                string xstr = ObjectUtils.ToString(((DataGridViewRow)x).Cells["ColumnTimestamp"].Value);
-                string ystr = ObjectUtils.ToString(((DataGridViewRow)y).Cells["ColumnTimestamp"].Value);
-                if (String.IsNullOrWhiteSpace(xstr) && String.IsNullOrWhiteSpace(ystr))
-                {
-                    return 0;
-                }
-                else if (String.IsNullOrWhiteSpace(xstr))
+            /// <param name="x">比較する最初の行です。</param>
+            /// <param name="y">比較する2番目の行。</param>
+            /// <returns>0未満:xはyより小さい, 0:xとyは等しい, 0より大きい:xはyより大きい。</returns>
+            public int Compare(object x, object y)
+            {
+                DataGridViewRow xrow = (DataGridViewRow)x;
+                DataGridViewRow yrow = (DataGridViewRow)y;
+
+                // 取得日時列の降順でソート、ただし空の列は先頭にする
+                int compare = StringUtils.CompareNullsLast(
+                    FormUtils.ToString(xrow.Cells["ColumnTimestamp"]),
+                    FormUtils.ToString(yrow.Cells["ColumnTimestamp"]));
+                if (compare != 0)
                 {
-                    return 1;
+                    return compare * -1;
                 }
-                else if (String.IsNullOrWhiteSpace(ystr))
+
+                // 取得日時列が同じ場合、残りの列の昇順でソート
+                foreach (string column in SortOrder)
                 {
-                    return -1;
+                    compare = string.Compare(
+                        FormUtils.ToString(xrow.Cells[column]),
+                        FormUtils.ToString(yrow.Cells[column]));
+                    if (compare != 0)
+                    {
+                        return compare;
+                    }
                 }
 
-                return xstr.CompareTo(ystr);
+                return 0;
             }
         }