OSDN Git Service

string.IsNullOrEmpty の nullable annotation あり版のメソッドを追加
[opentween/open-tween.git] / OpenTween / FilterDialog.cs
index 4a62981..a32303d 100644 (file)
@@ -24,6 +24,8 @@
 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
 // Boston, MA 02110-1301, USA.
 
+#nullable enable
+
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -36,6 +38,7 @@ using System.Linq.Expressions;
 using System.Text.RegularExpressions;
 using System.IO;
 using System.Collections.Specialized;
+using OpenTween.Models;
 
 namespace OpenTween
 {
@@ -43,9 +46,13 @@ namespace OpenTween
     {
         private EDITMODE _mode;
         private bool _directAdd;
-        private TabInformations _sts;
-        private string _cur;
-        private List<string> idlist = new List<string>();
+        private MultiSelectionState _multiSelState = MultiSelectionState.None;
+        private readonly TabInformations _sts;
+
+        private List<TabModel> tabs = new List<TabModel>();
+        private int selectedTabIndex = -1;
+
+        private readonly List<string> idlist = new List<string>();
 
         private enum EDITMODE
         {
@@ -54,121 +61,178 @@ namespace OpenTween
             None,
         }
 
+        private enum EnableButtonMode
+        {
+            NotSelected,
+            Enable,
+            Disable,
+        }
+
+        [Flags]
+        private enum MultiSelectionState
+        {
+            None = 0,
+            MoveSelected = 1,
+            SelectAll = 2,
+        }
+
+        private EnableButtonMode RuleEnableButtonMode
+        {
+            get => this._ruleEnableButtonMode;
+            set
+            {
+                this._ruleEnableButtonMode = value;
+
+                this.buttonRuleToggleEnabled.Text = value == FilterDialog.EnableButtonMode.Enable
+                    ? Properties.Resources.EnableButtonCaption
+                    : Properties.Resources.DisableButtonCaption;
+                this.buttonRuleToggleEnabled.Enabled = value != EnableButtonMode.NotSelected;
+            }
+        }
+        private EnableButtonMode _ruleEnableButtonMode = FilterDialog.EnableButtonMode.NotSelected;
+
+        public TabModel? SelectedTab
+            => this.selectedTabIndex != -1 ? this.tabs[this.selectedTabIndex] : null;
+
         public FilterDialog()
         {
-            InitializeComponent();
+            this.InitializeComponent();
+
+            this._sts = TabInformations.GetInstance();
+            this.RefreshListTabs();
         }
 
-        private void SetFilters(string tabName)
+        private void RefreshListTabs()
+        {
+            this.tabs = this._sts.Tabs.Append(this._sts.MuteTab).ToList();
+
+            using (ControlTransaction.Update(this.ListTabs))
+            {
+                var selectedTab = this.ListTabs.SelectedItem;
+                this.ListTabs.Items.Clear();
+                this.ListTabs.Items.AddRange(this.tabs.ToArray());
+                this.ListTabs.SelectedIndex = this.tabs.FindIndex(x => x == selectedTab);
+            }
+        }
+
+        private void SetFilters(TabModel tab)
         {
             if (ListTabs.Items.Count == 0) return;
 
             ListFilters.Items.Clear();
-            ListFilters.Items.AddRange(_sts.Tabs[tabName].GetFilters());
-            if (ListFilters.Items.Count > 0) ListFilters.SelectedIndex = 0;
 
-            if (TabInformations.GetInstance().IsDefaultTab(tabName))
+            if (tab is FilterTabModel filterTab)
+                ListFilters.Items.AddRange(filterTab.GetFilters());
+
+            if (ListFilters.Items.Count > 0)
+                ListFilters.SelectedIndex = 0;
+            else
+                ShowDetail();
+
+            if (tab.IsDefaultTabType)
             {
                 CheckProtected.Checked = true;
                 CheckProtected.Enabled = false;
             }
             else
             {
-                CheckProtected.Checked = _sts.Tabs[tabName].Protected;
+                CheckProtected.Checked = tab.Protected;
                 CheckProtected.Enabled = true;
             }
 
-            CheckManageRead.Checked = _sts.Tabs[tabName].UnreadManage;
-            CheckNotifyNew.Checked = _sts.Tabs[tabName].Notify;
+            CheckManageRead.CheckedChanged -= this.CheckManageRead_CheckedChanged;
+            CheckManageRead.Checked = tab.UnreadManage;
+            CheckManageRead.CheckedChanged += this.CheckManageRead_CheckedChanged;
 
-            int idx = ComboSound.Items.IndexOf(_sts.Tabs[tabName].SoundFile);
+            CheckNotifyNew.CheckedChanged -= this.CheckNotifyNew_CheckedChanged;
+            CheckNotifyNew.Checked = tab.Notify;
+            CheckNotifyNew.CheckedChanged += this.CheckNotifyNew_CheckedChanged;
+
+            var idx = ComboSound.Items.IndexOf(tab.SoundFile);
             if (idx == -1) idx = 0;
             ComboSound.SelectedIndex = idx;
 
             if (_directAdd) return;
 
+            if (tab.TabType == MyCommon.TabUsageType.Mute)
+            {
+                this.ButtonRenameTab.Enabled = false;
+                this.CheckManageRead.Enabled = false;
+                this.CheckNotifyNew.Enabled = false;
+                this.ComboSound.Enabled = false;
+
+                this.GroupBox1.Visible = false;
+                this.labelMuteTab.Visible = true;
+            }
+            else
+            {
+                this.ButtonRenameTab.Enabled = true;
+                this.CheckManageRead.Enabled = true;
+                this.CheckNotifyNew.Enabled = true;
+                this.ComboSound.Enabled = true;
+
+                this.GroupBox1.Visible = true;
+                this.labelMuteTab.Visible = false;
+            }
+
             ListTabs.Enabled = true;
             GroupTab.Enabled = true;
             ListFilters.Enabled = true;
-            if (ListFilters.SelectedIndex != -1)
-            {
-                ShowDetail();
-            }
             EditFilterGroup.Enabled = false;
-            switch (TabInformations.GetInstance().Tabs[tabName].TabType)
-            {
-                case MyCommon.TabUsageType.Home:
-                case MyCommon.TabUsageType.DirectMessage:
-                case MyCommon.TabUsageType.Favorites:
-                case MyCommon.TabUsageType.PublicSearch:
-                case MyCommon.TabUsageType.Lists:
-                case MyCommon.TabUsageType.Related:
-                case MyCommon.TabUsageType.UserTimeline:
-                    ButtonNew.Enabled = false;
+
+            if (tab.IsDistributableTabType)
+            {
+                ButtonNew.Enabled = true;
+                if (ListFilters.SelectedIndex > -1)
+                {
+                    ButtonEdit.Enabled = true;
+                    ButtonDelete.Enabled = true;
+                    ButtonRuleUp.Enabled = true;
+                    ButtonRuleDown.Enabled = true;
+                    ButtonRuleCopy.Enabled = true;
+                    ButtonRuleMove.Enabled = true;
+                    buttonRuleToggleEnabled.Enabled = true;
+                }
+                else
+                {
                     ButtonEdit.Enabled = false;
                     ButtonDelete.Enabled = false;
                     ButtonRuleUp.Enabled = false;
                     ButtonRuleDown.Enabled = false;
                     ButtonRuleCopy.Enabled = false;
                     ButtonRuleMove.Enabled = false;
-                    break;
-                default:
-                    ButtonNew.Enabled = true;
-                    if (ListFilters.SelectedIndex > -1)
-                    {
-                        ButtonEdit.Enabled = true;
-                        ButtonDelete.Enabled = true;
-                        ButtonRuleUp.Enabled = true;
-                        ButtonRuleDown.Enabled = true;
-                        ButtonRuleCopy.Enabled = true;
-                        ButtonRuleMove.Enabled = true;
-                    }
-                    else
-                    {
-                        ButtonEdit.Enabled = false;
-                        ButtonDelete.Enabled = false;
-                        ButtonRuleUp.Enabled = false;
-                        ButtonRuleDown.Enabled = false;
-                        ButtonRuleCopy.Enabled = false;
-                        ButtonRuleMove.Enabled = false;
-                    }
-                    break;
-            }
-            switch (TabInformations.GetInstance().Tabs[tabName].TabType)
-            {
-                case MyCommon.TabUsageType.Home:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_Home;
-                    break;
-                case MyCommon.TabUsageType.Mentions:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_Mentions;
-                    break;
-                case MyCommon.TabUsageType.DirectMessage:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_DirectMessage;
-                    break;
-                case MyCommon.TabUsageType.Favorites:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_Favorites;
-                    break;
-                case MyCommon.TabUsageType.UserDefined:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_UserDefined;
-                    break;
-                case MyCommon.TabUsageType.PublicSearch:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_PublicSearch;
-                    break;
-                case MyCommon.TabUsageType.Lists:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_Lists;
-                    break;
-                case MyCommon.TabUsageType.Related:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_Related;
-                    break;
-                case MyCommon.TabUsageType.UserTimeline:
-                    LabelTabType.Text = Properties.Resources.TabUsageTypeName_UserTimeline;
-                    break;
-                default:
-                    LabelTabType.Text = "UNKNOWN";
-                    break;
-            }
-            ButtonRenameTab.Enabled = true;
-            if (TabInformations.GetInstance().IsDefaultTab(tabName) || TabInformations.GetInstance().Tabs[tabName].Protected)
+                    buttonRuleToggleEnabled.Enabled = false;
+                }
+            }
+            else
+            {
+                ButtonNew.Enabled = false;
+                ButtonEdit.Enabled = false;
+                ButtonDelete.Enabled = false;
+                ButtonRuleUp.Enabled = false;
+                ButtonRuleDown.Enabled = false;
+                ButtonRuleCopy.Enabled = false;
+                ButtonRuleMove.Enabled = false;
+                buttonRuleToggleEnabled.Enabled = false;
+            }
+
+            this.LabelTabType.Text = tab.TabType switch
+            {
+                MyCommon.TabUsageType.Home => Properties.Resources.TabUsageTypeName_Home,
+                MyCommon.TabUsageType.Mentions => Properties.Resources.TabUsageTypeName_Mentions,
+                MyCommon.TabUsageType.DirectMessage => Properties.Resources.TabUsageTypeName_DirectMessage,
+                MyCommon.TabUsageType.Favorites => Properties.Resources.TabUsageTypeName_Favorites,
+                MyCommon.TabUsageType.UserDefined => Properties.Resources.TabUsageTypeName_UserDefined,
+                MyCommon.TabUsageType.PublicSearch => Properties.Resources.TabUsageTypeName_PublicSearch,
+                MyCommon.TabUsageType.Lists => Properties.Resources.TabUsageTypeName_Lists,
+                MyCommon.TabUsageType.Related => Properties.Resources.TabUsageTypeName_Related,
+                MyCommon.TabUsageType.UserTimeline => Properties.Resources.TabUsageTypeName_UserTimeline,
+                MyCommon.TabUsageType.Mute => "Mute",
+                MyCommon.TabUsageType.SearchResults => "SearchResults",
+                _ => "UNKNOWN",
+            };
+
+            if (tab.IsDefaultTabType || tab.Protected)
             {
                 ButtonDeleteTab.Enabled = false;
             }
@@ -179,9 +243,13 @@ namespace OpenTween
             ButtonClose.Enabled = true;
         }
 
-        public void SetCurrent(string TabName)
+        public void SetCurrent(string tabName)
         {
-            _cur = TabName;
+            var index = this.tabs.FindIndex(x => x.TabName == tabName);
+            if (index == -1)
+                throw new ArgumentException($"Unknown tab: {tabName}", nameof(tabName));
+
+            this.selectedTabIndex = index;
         }
 
         public void AddNewFilter(string id, string msg)
@@ -193,6 +261,7 @@ namespace OpenTween
             ButtonRuleDown.Enabled = false;
             ButtonRuleCopy.Enabled = false;
             ButtonRuleMove.Enabled = false;
+            buttonRuleToggleEnabled.Enabled = false;
             ButtonDelete.Enabled = false;
             ButtonClose.Enabled = false;
             EditFilterGroup.Enabled = true;
@@ -252,6 +321,7 @@ namespace OpenTween
             ButtonRuleDown.Enabled = false;
             ButtonRuleCopy.Enabled = false;
             ButtonRuleMove.Enabled = false;
+            buttonRuleToggleEnabled.Enabled = false;
             ButtonDelete.Enabled = false;
             ButtonClose.Enabled = false;
             EditFilterGroup.Enabled = true;
@@ -301,7 +371,7 @@ namespace OpenTween
 
             ShowDetail();
 
-            int idx = ListFilters.SelectedIndex;
+            var idx = ListFilters.SelectedIndex;
             ListFilters.SelectedIndex = -1;
             ListFilters.SelectedIndex = idx;
             ListFilters.Enabled = false;
@@ -314,6 +384,7 @@ namespace OpenTween
             ButtonRuleDown.Enabled = false;
             ButtonRuleCopy.Enabled = false;
             ButtonRuleMove.Enabled = false;
+            buttonRuleToggleEnabled.Enabled = false;
             EditFilterGroup.Enabled = true;
             ListTabs.Enabled = false;
             GroupTab.Enabled = false;
@@ -323,29 +394,31 @@ namespace OpenTween
 
         private void ButtonDelete_Click(object sender, EventArgs e)
         {
-            if (ListFilters.SelectedIndex == -1) return;
-            string tmp = "";
-            DialogResult rslt;
+            var selectedCount = ListFilters.SelectedIndices.Count;
+            if (selectedCount == 0) return;
+
+            string tmp;
 
-            if (ListFilters.SelectedIndices.Count == 1)
+            if (selectedCount == 1)
             {
-                tmp = string.Format(Properties.Resources.ButtonDelete_ClickText1, "\r\n", ListFilters.SelectedItem.ToString());
-                rslt = MessageBox.Show(tmp, Properties.Resources.ButtonDelete_ClickText2,
-                            MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
+                tmp = string.Format(Properties.Resources.ButtonDelete_ClickText1, Environment.NewLine, ListFilters.SelectedItem);
             }
             else
             {
-                tmp = string.Format(Properties.Resources.ButtonDelete_ClickText3, ListFilters.SelectedIndices.Count.ToString());
-                rslt = MessageBox.Show(tmp, Properties.Resources.ButtonDelete_ClickText2,
-                            MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
+                tmp = string.Format(Properties.Resources.ButtonDelete_ClickText3, selectedCount);
             }
+
+            var rslt = MessageBox.Show(tmp, Properties.Resources.ButtonDelete_ClickText2, MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
             if (rslt == DialogResult.Cancel) return;
 
-            for (int idx = ListFilters.Items.Count - 1; idx >= 0; idx--)
+            var indices = ListFilters.SelectedIndices.Cast<int>().Reverse().ToArray();  // 後ろの要素から削除
+            var tab = (FilterTabModel)this.SelectedTab!;
+
+            using (ControlTransaction.Update(ListFilters))
             {
-                if (ListFilters.GetSelected(idx))
+                foreach (var idx in indices)
                 {
-                    _sts.Tabs[ListTabs.SelectedItem.ToString()].RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
+                    tab.RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
                     ListFilters.Items.RemoveAt(idx);
                 }
             }
@@ -371,6 +444,7 @@ namespace OpenTween
                 ButtonRuleDown.Enabled = true;
                 ButtonRuleCopy.Enabled = true;
                 ButtonRuleMove.Enabled = true;
+                buttonRuleToggleEnabled.Enabled = true;
             }
             else
             {
@@ -380,6 +454,7 @@ namespace OpenTween
                 ButtonRuleDown.Enabled = false;
                 ButtonRuleCopy.Enabled = false;
                 ButtonRuleMove.Enabled = false;
+                buttonRuleToggleEnabled.Enabled = false;
             }
             ButtonClose.Enabled = true;
             if (_directAdd)
@@ -394,7 +469,7 @@ namespace OpenTween
 
             if (ListFilters.SelectedIndex > -1)
             {
-                PostFilterRule fc = (PostFilterRule)ListFilters.SelectedItem;
+                var fc = (PostFilterRule)ListFilters.SelectedItem;
                 if (fc.UseNameField)
                 {
                     RadioAND.Checked = true;
@@ -475,6 +550,7 @@ namespace OpenTween
                 ButtonRuleDown.Enabled = true;
                 ButtonRuleCopy.Enabled = true;
                 ButtonRuleMove.Enabled = true;
+                buttonRuleToggleEnabled.Enabled = true;
             }
             else
             {
@@ -517,12 +593,13 @@ namespace OpenTween
                 ButtonRuleDown.Enabled = false;
                 ButtonRuleCopy.Enabled = false;
                 ButtonRuleMove.Enabled = false;
+                buttonRuleToggleEnabled.Enabled = false;
             }
         }
 
         private void RadioAND_CheckedChanged(object sender, EventArgs e)
         {
-            bool flg = RadioAND.Checked;
+            var flg = RadioAND.Checked;
             UID.Enabled = flg;
             MSG1.Enabled = flg;
             MSG2.Enabled = !flg;
@@ -530,11 +607,8 @@ namespace OpenTween
 
         private void ButtonOK_Click(object sender, EventArgs e)
         {
-            bool isBlankMatch = false;
-            bool isBlankExclude = false;
-
             //入力チェック
-            if (!CheckMatchRule(out isBlankMatch) || !CheckExcludeRule(out isBlankExclude))
+            if (!CheckMatchRule(out var isBlankMatch) || !CheckExcludeRule(out var isBlankExclude))
             {
                 return;
             }
@@ -544,24 +618,36 @@ namespace OpenTween
                 return;
             }
 
-            int i = ListFilters.SelectedIndex;
-            PostFilterRule ft;
+            var tab = (FilterTabModel)this.SelectedTab!;
+            var i = ListFilters.SelectedIndex;
 
-            ft = new PostFilterRule();
+            PostFilterRule ft;
+            if (_mode == EDITMODE.AddNew)
+                ft = new PostFilterRule();
+            else
+                ft = (PostFilterRule)this.ListFilters.SelectedItem;
 
-            ft.MoveMatches = OptMove.Checked;
-            ft.MarkMatches = CheckMark.Checked;
+            if (tab.TabType != MyCommon.TabUsageType.Mute)
+            {
+                ft.MoveMatches = OptMove.Checked;
+                ft.MarkMatches = CheckMark.Checked;
+            }
+            else
+            {
+                ft.MoveMatches = true;
+                ft.MarkMatches = false;
+            }
 
-            string bdy = "";
+            var bdy = "";
             if (RadioAND.Checked)
             {
                 ft.FilterName = UID.Text;
-                TweenMain owner = (TweenMain)this.Owner;
-                int cnt = owner.AtIdSupl.ItemCount;
+                var owner = (TweenMain)this.Owner;
+                var cnt = owner.AtIdSupl.ItemCount;
                 owner.AtIdSupl.AddItem("@" + ft.FilterName);
                 if (cnt != owner.AtIdSupl.ItemCount)
                 {
-                    owner.ModifySettingAtId = true;
+                    owner.MarkSettingAtIdModified();
                 }
                 ft.UseNameField = true;
                 bdy = MSG1.Text;
@@ -581,7 +667,7 @@ namespace OpenTween
             else
             {
                 ft.FilterBody = bdy.Split(' ', ' ')
-                    .Where(x => !string.IsNullOrEmpty(x))
+                    .Where(x => !MyCommon.IsNullOrEmpty(x))
                     .ToArray();
             }
 
@@ -613,7 +699,7 @@ namespace OpenTween
             else
             {
                 ft.ExFilterBody = bdy.Split(' ', ' ')
-                    .Where(x => !string.IsNullOrEmpty(x))
+                    .Where(x => !MyCommon.IsNullOrEmpty(x))
                     .ToArray();
             }
 
@@ -625,15 +711,11 @@ namespace OpenTween
 
             if (_mode == EDITMODE.AddNew)
             {
-                if (!_sts.Tabs[ListTabs.SelectedItem.ToString()].AddFilter(ft))
+                if (!tab.AddFilter(ft))
                     MessageBox.Show(Properties.Resources.ButtonOK_ClickText4, Properties.Resources.ButtonOK_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Error);
             }
-            else
-            {
-                _sts.Tabs[ListTabs.SelectedItem.ToString()].EditFilter((PostFilterRule)ListFilters.SelectedItem, ft);
-            }
 
-            SetFilters(ListTabs.SelectedItem.ToString());
+            SetFilters(tab);
             ListFilters.SelectedIndex = -1;
             if (_mode == EDITMODE.AddNew)
             {
@@ -652,16 +734,13 @@ namespace OpenTween
         }
 
         private bool IsValidLambdaExp(string text)
-        {
-            return false;
-            // TODO DynamicQuery相当のGPLv3互換なライブラリで置換する
-        }
+            => false; // TODO DynamicQuery相当のGPLv3互換なライブラリで置換する
 
         private bool IsValidRegexp(string text)
         {
             try
             {
-                Regex rgx = new Regex(text);
+                new Regex(text);
             }
             catch (Exception ex)
             {
@@ -676,7 +755,7 @@ namespace OpenTween
             isBlank = false;
             if (RadioAND.Checked)
             {
-                if (string.IsNullOrEmpty(UID.Text) && string.IsNullOrEmpty(MSG1.Text) && string.IsNullOrEmpty(TextSource.Text) && CheckRetweet.Checked == false)
+                if (MyCommon.IsNullOrEmpty(UID.Text) && MyCommon.IsNullOrEmpty(MSG1.Text) && MyCommon.IsNullOrEmpty(TextSource.Text) && CheckRetweet.Checked == false)
                 {
                     isBlank = true;
                     return true;
@@ -706,7 +785,7 @@ namespace OpenTween
             }
             else
             {
-                if (string.IsNullOrEmpty(MSG2.Text) && string.IsNullOrEmpty(TextSource.Text) && CheckRetweet.Checked == false)
+                if (MyCommon.IsNullOrEmpty(MSG2.Text) && MyCommon.IsNullOrEmpty(TextSource.Text) && CheckRetweet.Checked == false)
                 {
                     isBlank = true;
                     return true;
@@ -733,7 +812,7 @@ namespace OpenTween
             isBlank = false;
             if (RadioExAnd.Checked)
             {
-                if (string.IsNullOrEmpty(ExUID.Text) && string.IsNullOrEmpty(ExMSG1.Text) && string.IsNullOrEmpty(TextExSource.Text) && CheckExRetweet.Checked == false)
+                if (MyCommon.IsNullOrEmpty(ExUID.Text) && MyCommon.IsNullOrEmpty(ExMSG1.Text) && MyCommon.IsNullOrEmpty(TextExSource.Text) && CheckExRetweet.Checked == false)
                 {
                     isBlank = true;
                     return true;
@@ -763,7 +842,7 @@ namespace OpenTween
             }
             else
             {
-                if (string.IsNullOrEmpty(ExMSG2.Text) && string.IsNullOrEmpty(TextExSource.Text) && CheckExRetweet.Checked == false)
+                if (MyCommon.IsNullOrEmpty(ExMSG2.Text) && MyCommon.IsNullOrEmpty(TextExSource.Text) && CheckExRetweet.Checked == false)
                 {
                     isBlank = true;
                     return true;
@@ -788,72 +867,73 @@ namespace OpenTween
 
         private void ListFilters_SelectedIndexChanged(object sender, EventArgs e)
         {
+            if (_multiSelState != MultiSelectionState.None)  //複数選択処理中は無視する
+                return;
+
             ShowDetail();
+
+            var selectedCount = this.ListFilters.SelectedIndices.Count;
+            if (selectedCount == 0)
+            {
+                this.RuleEnableButtonMode = EnableButtonMode.NotSelected;
+            }
+            else
+            {
+                if (selectedCount == 1 ||
+                    this.RuleEnableButtonMode == EnableButtonMode.NotSelected)
+                {
+                    var topItem = (PostFilterRule)this.ListFilters.SelectedItem;
+                    this.RuleEnableButtonMode = topItem.Enabled ? EnableButtonMode.Disable : EnableButtonMode.Enable;
+                }
+            }
         }
 
         private void ButtonClose_Click(object sender, EventArgs e)
-        {
-            this.Close();
-        }
+            => this.Close();
 
         private void FilterDialog_FormClosed(object sender, FormClosedEventArgs e)
-        {
-            _directAdd = false;
-        }
+            => this._directAdd = false;
 
         private void FilterDialog_KeyDown(object sender, KeyEventArgs e)
         {
             if (e.KeyCode == Keys.Escape)
             {
                 if (EditFilterGroup.Enabled)
-                    ButtonCancel_Click(null, null);
+                    ButtonCancel_Click(this.ButtonCancel, EventArgs.Empty);
                 else
-                    ButtonClose_Click(null, null);
+                    ButtonClose_Click(this.ButtonClose, EventArgs.Empty);
             }
         }
 
         private void ListFilters_DoubleClick(object sender, EventArgs e)
         {
-            if (ListFilters.SelectedItem == null)
-            {
-                return;
-            }
+            var idx = ListFilters.SelectedIndex;
+            if (idx == -1) return;
 
-            if (ListFilters.IndexFromPoint(ListFilters.PointToClient(Control.MousePosition)) == ListBox.NoMatches)
-            {
-                return;
-            }
+            var midx = ListFilters.IndexFromPoint(ListFilters.PointToClient(Control.MousePosition));
+            if (midx == ListBox.NoMatches || idx != midx) return;
 
-            if (ListFilters.Items[ListFilters.IndexFromPoint(ListFilters.PointToClient(Control.MousePosition))] == null)
-            {
-                return;
-            }
             ButtonEdit_Click(sender, e);
         }
 
         private void FilterDialog_Shown(object sender, EventArgs e)
         {
-            _sts = TabInformations.GetInstance();
-            ListTabs.Items.Clear();
-            foreach (string key in _sts.Tabs.Keys)
-            {
-                ListTabs.Items.Add(key);
-            }
+            ListTabs.DisplayMember = nameof(TabModel.TabName);
 
             ComboSound.Items.Clear();
             ComboSound.Items.Add("");
-            DirectoryInfo oDir = new DirectoryInfo(Application.StartupPath + Path.DirectorySeparatorChar);
+            var oDir = new DirectoryInfo(Application.StartupPath + Path.DirectorySeparatorChar);
             if (Directory.Exists(Path.Combine(Application.StartupPath, "Sounds")))
             {
                 oDir = oDir.GetDirectories("Sounds")[0];
             }
-            foreach (FileInfo oFile in oDir.GetFiles("*.wav"))
+            foreach (var oFile in oDir.GetFiles("*.wav"))
             {
                 ComboSound.Items.Add(oFile.Name);
             }
 
             idlist.Clear();
-            foreach (string tmp in ((TweenMain)this.Owner).AtIdSupl.GetItemList())
+            foreach (var tmp in ((TweenMain)this.Owner).AtIdSupl.GetItemList())
             {
                 idlist.Add(tmp.Remove(0, 1));  // @文字削除
             }
@@ -864,85 +944,104 @@ namespace OpenTween
             ExUID.AutoCompleteCustomSource.AddRange(idlist.ToArray());
 
             //選択タブ変更
-            if (ListTabs.Items.Count > 0)
-            {
-                if (_cur.Length > 0)
-                {
-                    for (int i = 0; i < ListTabs.Items.Count; i++)
-                    {
-                        if (_cur == ListTabs.Items[i].ToString())
-                        {
-                            ListTabs.SelectedIndex = i;
-                            //tabdialog.TabList.Items.Remove(_cur);
-                            break;
-                        }
-                    }
-                }
-            }
+            this.ListTabs.SelectedIndex = this.selectedTabIndex;
         }
 
         private void ListTabs_SelectedIndexChanged(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1)
-                SetFilters(ListTabs.SelectedItem.ToString());
+            this.selectedTabIndex = this.ListTabs.SelectedIndex;
+
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
+                SetFilters(selectedTab);
             else
                 ListFilters.Items.Clear();
         }
 
-        private void ButtonAddTab_Click(object sender, EventArgs e)
+        private async void ButtonAddTab_Click(object sender, EventArgs e)
         {
-            string tabName = null;
+            string? tabName = null;
             MyCommon.TabUsageType tabType;
-            using (InputTabName inputName = new InputTabName())
+            using (var inputName = new InputTabName())
             {
-                inputName.TabName = _sts.GetUniqueTabName();
+                inputName.TabName = _sts.MakeTabName("MyTab");
                 inputName.IsShowUsage = true;
                 inputName.ShowDialog();
                 if (inputName.DialogResult == DialogResult.Cancel) return;
                 tabName = inputName.TabName;
                 tabType = inputName.Usage;
             }
-            if (!string.IsNullOrEmpty(tabName))
+            if (!MyCommon.IsNullOrEmpty(tabName))
             {
                 //List対応
-                ListElement list = null;
+                ListElement? list = null;
                 if (tabType == MyCommon.TabUsageType.Lists)
                 {
-                    string rslt = ((TweenMain)this.Owner).TwitterInstance.GetListsApi();
-                    if (!string.IsNullOrEmpty(rslt))
+                    try
                     {
-                        MessageBox.Show("Failed to get lists. (" + rslt + ")");
+                        using var dialog = new WaitingDialog(Properties.Resources.ListsGetting);
+                        var cancellationToken = dialog.EnableCancellation();
+
+                        var task = ((TweenMain)this.Owner).TwitterInstance.GetListsApi();
+                        await dialog.WaitForAsync(this, task);
+
+                        cancellationToken.ThrowIfCancellationRequested();
                     }
-                    using (ListAvailable listAvail = new ListAvailable())
+                    catch (OperationCanceledException) { return; }
+                    catch (WebApiException ex)
                     {
-                        if (listAvail.ShowDialog(this) == DialogResult.Cancel) return;
-                        if (listAvail.SelectedList == null) return;
-                        list = listAvail.SelectedList;
+                        MessageBox.Show("Failed to get lists. (" + ex.Message + ")");
                     }
+
+                    using var listAvail = new ListAvailable();
+
+                    if (listAvail.ShowDialog(this) == DialogResult.Cancel)
+                        return;
+                    if (listAvail.SelectedList == null)
+                        return;
+                    list = listAvail.SelectedList;
                 }
-                if (!_sts.AddTab(tabName, tabType, list) || !((TweenMain)this.Owner).AddNewTab(tabName, false, tabType, list))
+
+                TabModel tab;
+                switch (tabType)
                 {
-                    string tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
+                    case MyCommon.TabUsageType.UserDefined:
+                        tab = new FilterTabModel(tabName);
+                        break;
+                    case MyCommon.TabUsageType.PublicSearch:
+                        tab = new PublicSearchTabModel(tabName);
+                        break;
+                    case MyCommon.TabUsageType.Lists:
+                        tab = new ListTimelineTabModel(tabName, list!);
+                        break;
+                    default:
+                        return;
+                }
+
+                if (!_sts.AddTab(tab) || !((TweenMain)this.Owner).AddNewTab(tab, startup: false))
+                {
+                    var tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
                     MessageBox.Show(tmp, Properties.Resources.AddTabMenuItem_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                     return;
                 }
                 else
                 {
-                    //成功
-                    ListTabs.Items.Add(tabName);
+                    // タブ作成成功
+                    this.RefreshListTabs();
                 }
             }
         }
 
         private void ButtonDeleteTab_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
-                string tb = ListTabs.SelectedItem.ToString();
-                int idx = ListTabs.SelectedIndex;
+                var tb = selectedTab.TabName;
+                var idx = ListTabs.SelectedIndex;
                 if (((TweenMain)this.Owner).RemoveSpecifiedTab(tb, true))
                 {
-                    ListTabs.Items.RemoveAt(idx);
+                    this.RefreshListTabs();
                     idx -= 1;
                     if (idx < 0) idx = 0;
                     ListTabs.SelectedIndex = idx;
@@ -952,172 +1051,243 @@ namespace OpenTween
 
         private void ButtonRenameTab_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
-                string tb = ListTabs.SelectedItem.ToString();
-                int idx = ListTabs.SelectedIndex;
-                if (((TweenMain)this.Owner).TabRename(ref tb))
-                {
-                    ListTabs.Items.RemoveAt(idx);
-                    ListTabs.Items.Insert(idx, tb);
-                    ListTabs.SelectedIndex = idx;
-                }
+                var origTabName = selectedTab.TabName;
+                if (((TweenMain)this.Owner).TabRename(origTabName, out _))
+                    this.RefreshListTabs();
             }
         }
 
         private void CheckManageRead_CheckedChanged(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
                 ((TweenMain)this.Owner).ChangeTabUnreadManage(
-                    ListTabs.SelectedItem.ToString(),
+                    selectedTab.TabName,
                     CheckManageRead.Checked);
             }
         }
 
         private void ButtonUp_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > 0 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
-            {
-                string selName = ListTabs.SelectedItem.ToString();
-                string tgtName = ListTabs.Items[ListTabs.SelectedIndex - 1].ToString();
-                ((TweenMain)this.Owner).ReOrderTab(
-                    selName,
-                    tgtName,
-                    true);
-                int idx = ListTabs.SelectedIndex;
-                ListTabs.Items.RemoveAt(idx - 1);
-                ListTabs.Items.Insert(idx, tgtName);
-            }
+            var selectedIndex = this.ListTabs.SelectedIndex;
+
+            if (selectedIndex == -1 || selectedIndex == 0)
+                return;
+
+            var selectedTab = this.tabs[selectedIndex];
+            var selectedTabName = selectedTab.TabName;
+
+            var targetTab = this.tabs[selectedIndex - 1];
+            var targetTabName = targetTab.TabName;
+
+            // ミュートタブは移動禁止
+            if (selectedTab.TabType == MyCommon.TabUsageType.Mute || targetTab.TabType == MyCommon.TabUsageType.Mute)
+                return;
+
+            var tweenMain = (TweenMain)this.Owner;
+            tweenMain.ReOrderTab(selectedTabName, targetTabName, true);
+
+            this.RefreshListTabs();
         }
 
         private void ButtonDown_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && ListTabs.SelectedIndex < ListTabs.Items.Count - 1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
-            {
-                string selName = ListTabs.SelectedItem.ToString();
-                string tgtName = ListTabs.Items[ListTabs.SelectedIndex + 1].ToString();
-                ((TweenMain)this.Owner).ReOrderTab(
-                    selName,
-                    tgtName,
-                    false);
-                int idx = ListTabs.SelectedIndex;
-                ListTabs.Items.RemoveAt(idx + 1);
-                ListTabs.Items.Insert(idx, tgtName);
-            }
+            var selectedIndex = this.ListTabs.SelectedIndex;
+
+            if (selectedIndex == -1 || selectedIndex == this.ListTabs.Items.Count - 1)
+                return;
+
+            var selectedTab = this.tabs[selectedIndex];
+            var selectedTabName = selectedTab.TabName;
+
+            var targetTab = this.tabs[selectedIndex + 1];
+            var targetTabName = targetTab.TabName;
+
+            // ミュートタブは移動禁止
+            if (selectedTab.TabType == MyCommon.TabUsageType.Mute || targetTab.TabType == MyCommon.TabUsageType.Mute)
+                return;
+
+            var tweenMain = (TweenMain)this.Owner;
+            tweenMain.ReOrderTab(selectedTabName, targetTabName, false);
+
+            this.RefreshListTabs();
         }
 
         private void CheckLocked_CheckedChanged(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
-                _sts.Tabs[ListTabs.SelectedItem.ToString()].Protected = CheckProtected.Checked;
+                selectedTab.Protected = CheckProtected.Checked;
                 ButtonDeleteTab.Enabled = !CheckProtected.Checked;
             }
         }
 
         private void CheckNotifyNew_CheckedChanged(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && !string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
-                _sts.Tabs[ListTabs.SelectedItem.ToString()].Notify = CheckNotifyNew.Checked;
+                selectedTab.Notify = CheckNotifyNew.Checked;
             }
         }
 
         private void ComboSound_SelectedIndexChanged(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && string.IsNullOrEmpty(ListTabs.SelectedItem.ToString()))
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null)
             {
-                string filename = "";
+                var filename = "";
                 if (ComboSound.SelectedIndex > -1) filename = ComboSound.SelectedItem.ToString();
-                _sts.Tabs[ListTabs.SelectedItem.ToString()].SoundFile = filename;
+                selectedTab.SoundFile = filename;
             }
         }
 
         private void RadioExAnd_CheckedChanged(object sender, EventArgs e)
         {
-            bool flg = RadioExAnd.Checked;
+            var flg = RadioExAnd.Checked;
             ExUID.Enabled = flg;
             ExMSG1.Enabled = flg;
             ExMSG2.Enabled = !flg;
         }
 
         private void OptMove_CheckedChanged(object sender, EventArgs e)
-        {
-            CheckMark.Enabled = !OptMove.Checked;
-        }
+            => this.CheckMark.Enabled = !OptMove.Checked;
 
         private void ButtonRuleUp_Click(object sender, EventArgs e)
+            => this.MoveSelectedRules(up: true);
+
+        private void ButtonRuleDown_Click(object sender, EventArgs e)
+            => this.MoveSelectedRules(up: false);
+
+        private void MoveSelectedRules(bool up)
         {
-            if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null && ListFilters.SelectedIndex > 0)
+            var selectedTab = this.SelectedTab;
+            if (selectedTab == null || ListFilters.SelectedIndices.Count == 0)
+                return;
+
+            var indices = ListFilters.SelectedIndices.Cast<int>().ToArray();
+
+            int diff;
+            if (up)
+            {
+                if (indices[0] <= 0) return;
+                diff = -1;
+            }
+            else
             {
-                string tabname = ListTabs.SelectedItem.ToString();
-                PostFilterRule selected = _sts.Tabs[tabname].Filters[ListFilters.SelectedIndex];
-                PostFilterRule target = _sts.Tabs[tabname].Filters[ListFilters.SelectedIndex - 1];
-                int idx = ListFilters.SelectedIndex;
-                ListFilters.Items.RemoveAt(idx - 1);
-                ListFilters.Items.Insert(idx, target);
-                _sts.Tabs[tabname].Filters.RemoveAt(idx - 1);
-                _sts.Tabs[tabname].Filters.Insert(idx, target);
+                if (indices[indices.Length - 1] >= ListFilters.Items.Count - 1) return;
+                diff = +1;
+                Array.Reverse(indices);  // 逆順にして、下にある要素から処理する
+            }
+
+            var lastSelIdx = indices[0] + diff;
+            var tab = (FilterTabModel)selectedTab;
+
+            try
+            {
+                _multiSelState |= MultiSelectionState.MoveSelected;
+
+                using (ControlTransaction.Update(ListFilters))
+                {
+                    ListFilters.SelectedIndices.Clear();
+
+                    foreach (var idx in indices)
+                    {
+                        var tidx = idx + diff;
+                        var target = (PostFilterRule)ListFilters.Items[tidx];
+
+                        // 移動先にある要素と位置を入れ替える
+                        ListFilters.Items.RemoveAt(tidx);
+                        ListFilters.Items.Insert(idx, target);
+
+                        // 移動方向の先頭要素以外なら選択する
+                        if (tidx != lastSelIdx)
+                            ListFilters.SelectedIndex = tidx;
+                    }
+
+                    tab.FilterArray = ListFilters.Items.Cast<PostFilterRule>().ToArray();
+
+                    // 移動方向の先頭要素は最後に選択する
+                    // ※移動方向への自動スクロール目的
+                    ListFilters.SelectedIndex = lastSelIdx;
+                }
+            }
+            finally
+            {
+                _multiSelState &= ~MultiSelectionState.MoveSelected;
             }
         }
 
-        private void ButtonRuleDown_Click(object sender, EventArgs e)
+        private void buttonRuleToggleEnabled_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null && ListFilters.SelectedIndex < ListFilters.Items.Count - 1)
+            if (this.RuleEnableButtonMode == EnableButtonMode.NotSelected)
+                return;
+
+            var enabled = this.RuleEnableButtonMode == EnableButtonMode.Enable;
+
+            foreach (var idx in this.ListFilters.SelectedIndices.Cast<int>())
             {
-                string tabname = ListTabs.SelectedItem.ToString();
-                PostFilterRule selected = _sts.Tabs[tabname].Filters[ListFilters.SelectedIndex];
-                PostFilterRule target = _sts.Tabs[tabname].Filters[ListFilters.SelectedIndex + 1];
-                int idx = ListFilters.SelectedIndex;
-                ListFilters.Items.RemoveAt(idx + 1);
-                ListFilters.Items.Insert(idx, target);
-                _sts.Tabs[tabname].Filters.RemoveAt(idx + 1);
-                _sts.Tabs[tabname].Filters.Insert(idx, target);
+                var filter = (PostFilterRule)this.ListFilters.Items[idx];
+                if (filter.Enabled != enabled)
+                {
+                    filter.Enabled = enabled;
+
+                    var itemRect = this.ListFilters.GetItemRectangle(idx);
+                    this.ListFilters.Invalidate(itemRect);
+                }
             }
+
+            this.RuleEnableButtonMode = enabled ? EnableButtonMode.Disable : EnableButtonMode.Enable;
         }
 
         private void ButtonRuleCopy_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null)
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null && ListFilters.SelectedItem != null)
             {
-                TabClass[] selectedTabs;
-                using (TabsDialog dialog = new TabsDialog(_sts))
+                TabModel[] destinationTabs;
+                using (var dialog = new TabsDialog(_sts))
                 {
                     dialog.MultiSelect = true;
                     dialog.Text = Properties.Resources.ButtonRuleCopy_ClickText1;
 
                     if (dialog.ShowDialog(this) == DialogResult.Cancel) return;
 
-                    selectedTabs = dialog.SelectedTabs;
+                    destinationTabs = dialog.SelectedTabs;
                 }
 
-                string tabname = ListTabs.SelectedItem.ToString();
-                List<PostFilterRule> filters = new List<PostFilterRule>();
+                var currentTab = (FilterTabModel)selectedTab;
+                var filters = new List<PostFilterRule>();
 
                 foreach (int idx in ListFilters.SelectedIndices)
                 {
-                    filters.Add(_sts.Tabs[tabname].Filters[idx].Clone());
+                    filters.Add(currentTab.FilterArray[idx].Clone());
                 }
-                foreach (var tb in selectedTabs)
+                foreach (var tb in destinationTabs.Cast<FilterTabModel>())
                 {
-                    if (tb.TabName == tabname) continue;
+                    if (tb.TabName == currentTab.TabName) continue;
 
-                    foreach (PostFilterRule flt in filters)
+                    foreach (var flt in filters)
                     {
-                        if (!tb.Filters.Contains(flt))
+                        if (!tb.FilterArray.Contains(flt))
                             tb.AddFilter(flt.Clone());
                     }
                 }
-                SetFilters(tabname);
+                SetFilters(selectedTab);
             }
         }
 
         private void ButtonRuleMove_Click(object sender, EventArgs e)
         {
-            if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null)
+            var selectedTab = this.SelectedTab;
+            if (selectedTab != null && ListFilters.SelectedItem != null)
             {
-                TabClass[] selectedTabs;
+                TabModel[] destinationTabs;
                 using (var dialog = new TabsDialog(_sts))
                 {
                     dialog.MultiSelect = true;
@@ -1125,35 +1295,35 @@ namespace OpenTween
 
                     if (dialog.ShowDialog(this) == DialogResult.Cancel) return;
 
-                    selectedTabs = dialog.SelectedTabs;
+                    destinationTabs = dialog.SelectedTabs;
                 }
-                string tabname = ListTabs.SelectedItem.ToString();
-                List<PostFilterRule> filters = new List<PostFilterRule>();
+                var currentTab = (FilterTabModel)selectedTab;
+                var filters = new List<PostFilterRule>();
 
                 foreach (int idx in ListFilters.SelectedIndices)
                 {
-                    filters.Add(_sts.Tabs[tabname].Filters[idx].Clone());
+                    filters.Add(currentTab.FilterArray[idx].Clone());
                 }
-                if (selectedTabs.Length == 1 && selectedTabs[0].TabName == tabname) return;
-                foreach (var tb in selectedTabs)
+                if (destinationTabs.Length == 1 && destinationTabs[0].TabName == currentTab.TabName) return;
+                foreach (var tb in destinationTabs.Cast<FilterTabModel>())
                 {
-                    if (tb.TabName == tabname) continue;
+                    if (tb.TabName == currentTab.TabName) continue;
 
-                    foreach (PostFilterRule flt in filters)
+                    foreach (var flt in filters)
                     {
-                        if (!tb.Filters.Contains(flt))
+                        if (!tb.FilterArray.Contains(flt))
                             tb.AddFilter(flt.Clone());
                     }
                 }
-                for (int idx = ListFilters.Items.Count - 1; idx >= 0; idx--)
+                for (var idx = ListFilters.Items.Count - 1; idx >= 0; idx--)
                 {
                     if (ListFilters.GetSelected(idx))
                     {
-                        _sts.Tabs[ListTabs.SelectedItem.ToString()].RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
+                        currentTab.RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
                         ListFilters.Items.RemoveAt(idx);
                     }
                 }
-                SetFilters(tabname);
+                SetFilters(selectedTab);
             }
         }
 
@@ -1161,19 +1331,19 @@ namespace OpenTween
         {
             if (e.KeyCode == Keys.Space && e.Modifiers == (Keys.Shift | Keys.Control))
             {
-                TweenMain main = (TweenMain)this.Owner;
-                TextBox tbox = (TextBox)sender;
+                var main = (TweenMain)this.Owner;
+                var tbox = (TextBox)sender;
                 if (tbox.SelectionStart > 0)
                 {
-                    int endidx = tbox.SelectionStart - 1;
-                    string startstr = "";
-                    for (int i = tbox.SelectionStart - 1; i >= 0; i--)
+                    var endidx = tbox.SelectionStart - 1;
+                    for (var i = tbox.SelectionStart - 1; i >= 0; i--)
                     {
-                        char c = tbox.Text[i];
-                        if (Char.IsLetterOrDigit(c) || c == '_')
+                        var c = tbox.Text[i];
+                        if (char.IsLetterOrDigit(c) || c == '_')
                         {
                             continue;
                         }
+                        string startstr;
                         if (c == '@')
                         {
                             startstr = tbox.Text.Substring(i + 1, endidx - i);
@@ -1196,21 +1366,79 @@ namespace OpenTween
 
         private void FilterTextBox_KeyPress(object sender, KeyPressEventArgs e)
         {
-            TweenMain main = (TweenMain)this.Owner;
-            TextBox tbox = (TextBox)sender;
+            var main = (TweenMain)this.Owner;
+            var tbox = (TextBox)sender;
             if (e.KeyChar == '@')
             {
-                //if (!SettingDialog.UseAtIdSupplement) return;
                 //@マーク
                 main.ShowSuplDialog(tbox, main.AtIdSupl);
                 e.Handled = true;
             }
             else if (e.KeyChar == '#')
             {
-                //if (!SettingDialog.UseHashSupplement) return;
                 main.ShowSuplDialog(tbox, main.HashSupl);
                 e.Handled = true;
             }
         }
+
+        private void ListFilters_DrawItem(object sender, DrawItemEventArgs e)
+        {
+            e.DrawBackground();
+
+            if (e.Index != -1)
+            {
+                var filter = (PostFilterRule)this.ListFilters.Items[e.Index];
+                var isSelected = e.State.HasFlag(DrawItemState.Selected);
+
+                Brush textBrush;
+                if (isSelected)
+                    textBrush = SystemBrushes.HighlightText;
+                else if (filter.Enabled)
+                    textBrush = SystemBrushes.WindowText;
+                else
+                    textBrush = SystemBrushes.GrayText;
+
+                e.Graphics.DrawString(filter.ToString(), e.Font, textBrush, e.Bounds);
+            }
+
+            e.DrawFocusRectangle();
+        }
+
+        private void ListFilters_KeyDown(object sender, KeyEventArgs e)
+        {
+            if (e.Control && e.KeyCode == Keys.A)
+            {
+                var itemCount = this.ListFilters.Items.Count;
+                if (itemCount == 0) return;
+
+                using (ControlTransaction.Update(this.ListFilters))
+                {
+                    if (itemCount > 1)
+                    {
+                        try
+                        {
+                            _multiSelState |= MultiSelectionState.SelectAll;
+
+                            for (var i = 1; i < itemCount; i++)
+                            {
+                                this.ListFilters.SetSelected(i, true);
+                            }
+                        }
+                        finally
+                        {
+                            _multiSelState &= ~MultiSelectionState.SelectAll;
+                        }
+                    }
+
+                    this.ListFilters.SetSelected(0, true);
+                }
+            }
+        }
+
+        protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
+        {
+            base.ScaleControl(factor, specified);
+            this.ListFilters.ItemHeight = this.ListFilters.Font.Height;
+        }
     }
 }