OSDN Git Service

TabClassをタブの種類別のクラスに分離
authorKimura Youichi <kim.upsilon@bucyou.net>
Fri, 13 May 2016 19:58:26 +0000 (04:58 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Fri, 13 May 2016 20:02:19 +0000 (05:02 +0900)
25 files changed:
OpenTween.Tests/Models/TabInformationTest.cs
OpenTween.Tests/Models/TabModelTest.cs [moved from OpenTween.Tests/Models/TabClassTest.cs with 72% similarity]
OpenTween.Tests/OpenTween.Tests.csproj
OpenTween.Tests/TabsDialogTest.cs
OpenTween/FilterDialog.cs
OpenTween/Models/DirectMessagesTabModel.cs [new file with mode: 0644]
OpenTween/Models/FavoritesTabModel.cs [new file with mode: 0644]
OpenTween/Models/FilterTabModel.cs [new file with mode: 0644]
OpenTween/Models/HomeTabModel.cs [new file with mode: 0644]
OpenTween/Models/InternalStorageTabModel.cs [new file with mode: 0644]
OpenTween/Models/ListTimelineTabModel.cs [new file with mode: 0644]
OpenTween/Models/LocalSearchTabModel.cs [new file with mode: 0644]
OpenTween/Models/MentionsTabModel.cs [new file with mode: 0644]
OpenTween/Models/MuteTabModel.cs [new file with mode: 0644]
OpenTween/Models/PublicSearchTabModel.cs [new file with mode: 0644]
OpenTween/Models/RelatedPostsTabModel.cs [new file with mode: 0644]
OpenTween/Models/TabClass.cs [deleted file]
OpenTween/Models/TabInformations.cs
OpenTween/Models/TabModel.cs [new file with mode: 0644]
OpenTween/Models/UserTimelineTabModel.cs [new file with mode: 0644]
OpenTween/MyLists.cs
OpenTween/OpenTween.csproj
OpenTween/TabsDialog.cs
OpenTween/Tween.cs
OpenTween/Twitter.cs

index 6636a96..a3f198a 100644 (file)
@@ -43,30 +43,26 @@ namespace OpenTween.Models
             field.SetValue(null, this.tabinfo);
 
             // 標準のタブを追加
-            this.tabinfo.AddTab("Recent", MyCommon.TabUsageType.Home, null);
-            this.tabinfo.AddTab("Reply", MyCommon.TabUsageType.Mentions, null);
-            this.tabinfo.AddTab("DM", MyCommon.TabUsageType.DirectMessage, null);
-            this.tabinfo.AddTab("Favorites", MyCommon.TabUsageType.Favorites, null);
+            this.tabinfo.AddTab(new HomeTabModel("Recent"));
+            this.tabinfo.AddTab(new MentionsTabModel("Reply"));
+            this.tabinfo.AddTab(new DirectMessagesTabModel("DM"));
+            this.tabinfo.AddTab(new FavoritesTabModel("Favorites"));
         }
 
         [Fact]
         public void AddTab_Test()
         {
-            var list = new ListElement { };
-            var ret = this.tabinfo.AddTab("MyTab", MyCommon.TabUsageType.Lists, list);
+            var tab = new FilterTabModel("MyTab");
+            var ret = this.tabinfo.AddTab(tab);
 
             Assert.True(ret);
-
-            var lastTab = this.tabinfo.Tabs.Values.Last();
-            Assert.Equal("MyTab", lastTab.TabName);
-            Assert.Equal(MyCommon.TabUsageType.Lists, lastTab.TabType);
-            Assert.Same(list, lastTab.ListInfo);
+            Assert.Same(tab, this.tabinfo.Tabs.Values.Last());
         }
 
         [Fact]
         public void AddTab_DuplicateTest()
         {
-            var ret = this.tabinfo.AddTab("Recent", MyCommon.TabUsageType.UserDefined, null);
+            var ret = this.tabinfo.AddTab(new FilterTabModel("Recent"));
 
             Assert.False(ret);
         }
@@ -81,8 +77,8 @@ namespace OpenTween.Models
         [Fact]
         public void MakeTabName_RetryTest()
         {
-            this.tabinfo.AddTab("NewTab", MyCommon.TabUsageType.UserDefined, null);
-            this.tabinfo.AddTab("NewTab2", MyCommon.TabUsageType.UserDefined, null);
+            this.tabinfo.AddTab(new FilterTabModel("NewTab"));
+            this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
 
             var baseTabName = "NewTab";
             Assert.Equal("NewTab3", this.tabinfo.MakeTabName(baseTabName, 5));
@@ -91,11 +87,11 @@ namespace OpenTween.Models
         [Fact]
         public void MakeTabName_RetryErrorTest()
         {
-            this.tabinfo.AddTab("NewTab", MyCommon.TabUsageType.UserDefined, null);
-            this.tabinfo.AddTab("NewTab2", MyCommon.TabUsageType.UserDefined, null);
-            this.tabinfo.AddTab("NewTab3", MyCommon.TabUsageType.UserDefined, null);
-            this.tabinfo.AddTab("NewTab4", MyCommon.TabUsageType.UserDefined, null);
-            this.tabinfo.AddTab("NewTab5", MyCommon.TabUsageType.UserDefined, null);
+            this.tabinfo.AddTab(new FilterTabModel("NewTab"));
+            this.tabinfo.AddTab(new FilterTabModel("NewTab2"));
+            this.tabinfo.AddTab(new FilterTabModel("NewTab3"));
+            this.tabinfo.AddTab(new FilterTabModel("NewTab4"));
+            this.tabinfo.AddTab(new FilterTabModel("NewTab5"));
 
             var baseTabName = "NewTab";
             Assert.Throws<TabException>(() => this.tabinfo.MakeTabName(baseTabName, 5));
@@ -189,7 +185,7 @@ namespace OpenTween.Models
         {
             this.tabinfo.MuteUserIds = new HashSet<long> { };
 
-            var muteTab = new TabClass("Mute", MyCommon.TabUsageType.Mute);
+            var muteTab = new MuteTabModel();
             muteTab.AddFilter(new PostFilterRule
             {
                 FilterName = "foo",
@@ -211,7 +207,7 @@ namespace OpenTween.Models
         {
             this.tabinfo.MuteUserIds = new HashSet<long> { };
 
-            var muteTab = new TabClass("Mute", MyCommon.TabUsageType.Mute);
+            var muteTab = new MuteTabModel();
             muteTab.AddFilter(new PostFilterRule
             {
                 FilterName = "foo",
@@ -233,23 +229,23 @@ namespace OpenTween.Models
         [Fact]
         public void SetReadAllTab_MarkAsReadTest()
         {
-            var tab1 = new TabClass("search1", MyCommon.TabUsageType.PublicSearch);
-            var tab2 = new TabClass("search2", MyCommon.TabUsageType.PublicSearch);
+            var tab1 = new PublicSearchTabModel("search1");
+            var tab2 = new PublicSearchTabModel("search2");
 
             this.tabinfo.AddTab(tab1);
             this.tabinfo.AddTab(tab2);
 
             // search1 に追加するツイート (StatusId: 100, 150, 200; すべて未読)
             tab1.UnreadManage = true;
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 150L, IsRead = false });
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = false });
+            tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
+            tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
+            tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
 
             // search2 に追加するツイート (StatusId: 150, 200, 250; すべて未読)
             tab2.UnreadManage = true;
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 150L, IsRead = false });
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = false });
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 250L, IsRead = false });
+            tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = false });
+            tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
+            tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = false });
 
             this.tabinfo.DistributePosts();
             this.tabinfo.SubmitUpdate();
@@ -274,23 +270,23 @@ namespace OpenTween.Models
         [Fact]
         public void SetReadAllTab_MarkAsUnreadTest()
         {
-            var tab1 = new TabClass("search1", MyCommon.TabUsageType.PublicSearch);
-            var tab2 = new TabClass("search2", MyCommon.TabUsageType.PublicSearch);
+            var tab1 = new PublicSearchTabModel("search1");
+            var tab2 = new PublicSearchTabModel("search2");
 
             this.tabinfo.AddTab(tab1);
             this.tabinfo.AddTab(tab2);
 
             // search1 に追加するツイート (StatusId: 100, 150, 200; すべて既読)
             tab1.UnreadManage = true;
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = true });
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 150L, IsRead = true });
-            tab1.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = true });
+            tab1.AddPostQueue(new PostClass { StatusId = 100L, IsRead = true });
+            tab1.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
+            tab1.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
 
             // search2 に追加するツイート (StatusId: 150, 200, 250; すべて既読)
             tab2.UnreadManage = true;
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 150L, IsRead = true });
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = true });
-            tab2.AddPostToInnerStorage(new PostClass { StatusId = 250L, IsRead = true });
+            tab2.AddPostQueue(new PostClass { StatusId = 150L, IsRead = true });
+            tab2.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
+            tab2.AddPostQueue(new PostClass { StatusId = 250L, IsRead = true });
 
             this.tabinfo.DistributePosts();
             this.tabinfo.SubmitUpdate();
@@ -372,10 +368,10 @@ namespace OpenTween.Models
             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsRead = false });
 
             // StatusId: 150 だけ FilterTab の振り分けルールにヒットする (PostClass.FilterHit が true になる)
-            this.tabinfo.AddTab("FilterTab", MyCommon.TabUsageType.UserDefined, null);
-            var filterTab = this.tabinfo.Tabs["FilterTab"];
+            var filterTab = new FilterTabModel("FilterTab");
             filterTab.AddFilter(TestPostFilterRule.Create(x =>
                 x.StatusId == 150L ? MyCommon.HITRESULT.Copy : MyCommon.HITRESULT.None));
+            this.tabinfo.AddTab(filterTab);
 
             this.tabinfo.DistributePosts();
             this.tabinfo.SubmitUpdate();
@@ -439,7 +435,7 @@ namespace OpenTween.Models
             this.tabinfo.AddPost(new PostClass { StatusId = 200L, IsReply = true, IsRead = false });
 
             // DM
-            dmTab.AddPostToInnerStorage(new PostClass { StatusId = 300L, IsDm = true, IsRead = false });
+            dmTab.AddPostQueue(new PostClass { StatusId = 300L, IsDm = true, IsRead = false });
 
             this.tabinfo.DistributePosts();
 
@@ -491,7 +487,7 @@ namespace OpenTween.Models
         {
             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
 
-            var myTab1 = new TabClass("MyTab1", MyCommon.TabUsageType.UserDefined);
+            var myTab1 = new FilterTabModel("MyTab1");
             this.tabinfo.AddTab(myTab1);
 
             var filter = new PostFilterRule
@@ -512,8 +508,8 @@ namespace OpenTween.Models
             this.tabinfo.SubmitUpdate();
 
             // この時点での振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 100L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L }, myTab1.StatusIds);
 
             // フィルタを変更する
             filter.FilterName = "bbb";
@@ -528,8 +524,8 @@ namespace OpenTween.Models
             //   [statusId: 200] は Recent から MyTab1 にコピーされる
 
             // 変更後の振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 200L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 200L }, myTab1.StatusIds);
         }
 
         [Fact]
@@ -537,7 +533,7 @@ namespace OpenTween.Models
         {
             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
 
-            var myTab1 = new TabClass("MyTab1", MyCommon.TabUsageType.UserDefined);
+            var myTab1 = new FilterTabModel("MyTab1");
             this.tabinfo.AddTab(myTab1);
 
             var filter = new PostFilterRule
@@ -558,8 +554,8 @@ namespace OpenTween.Models
             this.tabinfo.SubmitUpdate();
 
             // この時点での振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 100L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L }, myTab1.StatusIds);
 
             // フィルタを変更する
             filter.FilterName = "bbb";
@@ -574,8 +570,8 @@ namespace OpenTween.Models
             //   [statusId: 200] は Recent から MyTab1 にコピーされ、マークが付与される
 
             // 変更後の振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 200L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 200L }, myTab1.StatusIds);
 
             // [statusId: 200] は IsMark が true の状態になる
             Assert.True(this.tabinfo[200L].IsMark);
@@ -586,7 +582,7 @@ namespace OpenTween.Models
         {
             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
 
-            var myTab1 = new TabClass("MyTab1", MyCommon.TabUsageType.UserDefined);
+            var myTab1 = new FilterTabModel("MyTab1");
             this.tabinfo.AddTab(myTab1);
 
             var filter = new PostFilterRule
@@ -606,8 +602,8 @@ namespace OpenTween.Models
             this.tabinfo.SubmitUpdate();
 
             // この時点での振り分け状態
-            Assert.Equal(new[] { 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 100L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L }, myTab1.StatusIds);
 
             // フィルタを変更する
             filter.FilterName = "bbb";
@@ -622,8 +618,8 @@ namespace OpenTween.Models
             //   [statusId: 200] は Recent から MyTab1 に移動される
 
             // 変更後の振り分け状態
-            Assert.Equal(new[] { 100L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 200L }, myTab1.BackupIds);
+            Assert.Equal(new[] { 100L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 200L }, myTab1.StatusIds);
         }
 
         [Fact]
@@ -631,8 +627,8 @@ namespace OpenTween.Models
         {
             var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
 
-            var myTab1 = new TabClass("MyTab1", MyCommon.TabUsageType.UserDefined);
-            var myTab2 = new TabClass("MyTab2", MyCommon.TabUsageType.UserDefined);
+            var myTab1 = new FilterTabModel("MyTab1");
+            var myTab2 = new FilterTabModel("MyTab2");
             this.tabinfo.AddTab(myTab1);
             this.tabinfo.AddTab(myTab2);
 
@@ -663,9 +659,9 @@ namespace OpenTween.Models
             this.tabinfo.SubmitUpdate();
 
             // この時点での振り分け状態
-            Assert.Equal(new[] { 300L }, homeTab.BackupIds);
-            Assert.Equal(new[] { 100L }, myTab1.BackupIds);
-            Assert.Equal(new[] { 200L }, myTab2.BackupIds);
+            Assert.Equal(new[] { 300L }, homeTab.StatusIds);
+            Assert.Equal(new[] { 100L }, myTab1.StatusIds);
+            Assert.Equal(new[] { 200L }, myTab2.StatusIds);
 
             // MyTab1 のフィルタを変更する
             filter1.FilterName = "bbb";
@@ -685,16 +681,16 @@ namespace OpenTween.Models
             //   [statusId: 300] は Recent から MyTab2 に移動される
 
             // 変更後の振り分け状態
-            Assert.Equal(new[] { 100L }, homeTab.BackupIds);
-            Assert.Equal(new[] { 200L }, myTab1.BackupIds);
-            Assert.Equal(new[] { 300L }, myTab2.BackupIds);
+            Assert.Equal(new[] { 100L }, homeTab.StatusIds);
+            Assert.Equal(new[] { 200L }, myTab1.StatusIds);
+            Assert.Equal(new[] { 300L }, myTab2.StatusIds);
         }
 
         [Fact]
         public void FilterAll_ExcludeReplyFilterTest()
         {
-            var homeTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Home);
-            var replyTab = this.tabinfo.GetTabByType(MyCommon.TabUsageType.Mentions);
+            var homeTab = this.tabinfo.GetTabByType<HomeTabModel>();
+            var replyTab = this.tabinfo.GetTabByType<MentionsTabModel>();
 
             var filter = new PostFilterRule
             {
@@ -711,8 +707,8 @@ namespace OpenTween.Models
             this.tabinfo.SubmitUpdate();
 
             // この時点での振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 200L, 300L }, replyTab.BackupIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 200L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
 
             // [statusId: 100] は IsExcludeReply が true の状態になっている
             Assert.True(this.tabinfo[100L].IsExcludeReply);
@@ -730,8 +726,8 @@ namespace OpenTween.Models
             //   [statusId: 200] は Reply から取り除かれ、IsExcludeReply が true になる
 
             // 変更後の振り分け状態
-            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.BackupIds, AnyOrderComparer<long>.Instance);
-            Assert.Equal(new[] { 100L, 300L }, replyTab.BackupIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L, 200L, 300L }, homeTab.StatusIds, AnyOrderComparer<long>.Instance);
+            Assert.Equal(new[] { 100L, 300L }, replyTab.StatusIds, AnyOrderComparer<long>.Instance);
 
             // [statusId: 100] は IsExcludeReply が false の状態になる
             Assert.False(this.tabinfo[100L].IsExcludeReply);
similarity index 72%
rename from OpenTween.Tests/Models/TabClassTest.cs
rename to OpenTween.Tests/Models/TabModelTest.cs
index 84f46e2..cd33324 100644 (file)
@@ -30,20 +30,19 @@ using Xunit.Extensions;
 
 namespace OpenTween.Models
 {
-    public class TabClassTest
+    public class TabModelTest
     {
         [Fact]
         public void EnqueueRemovePost_Test()
         {
-            var tab = new TabClass
+            var tab = new PublicSearchTabModel("search")
             {
-                TabType = MyCommon.TabUsageType.PublicSearch,
                 UnreadManage = true,
             };
 
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 110L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 120L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 110L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 120L, IsRead = false });
 
             tab.AddSubmit();
 
@@ -60,21 +59,20 @@ namespace OpenTween.Models
 
             Assert.Equal(2, tab.AllCount);
             Assert.Equal(2, tab.UnreadCount);
-            Assert.Equal(new[] { 110L, 120L }, tab.BackupIds);
+            Assert.Equal(new[] { 110L, 120L }, tab.StatusIds);
             Assert.Equal(new[] { 100L }, removedIds.AsEnumerable());
         }
 
         [Fact]
         public void EnqueueRemovePost_SetIsDeletedTest()
         {
-            var tab = new TabClass
+            var tab = new PublicSearchTabModel("search")
             {
-                TabType = MyCommon.TabUsageType.PublicSearch,
                 UnreadManage = true,
             };
 
             var post = new PostClass { StatusId = 100L, IsRead = false };
-            tab.AddPostToInnerStorage(post);
+            tab.AddPostQueue(post);
             tab.AddSubmit();
 
             Assert.Equal(1, tab.AllCount);
@@ -97,13 +95,12 @@ namespace OpenTween.Models
         [Fact]
         public void EnqueueRemovePost_UnknownIdTest()
         {
-            var tab = new TabClass
+            var tab = new PublicSearchTabModel("search")
             {
-                TabType = MyCommon.TabUsageType.PublicSearch,
                 UnreadManage = true,
             };
 
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
             tab.AddSubmit();
 
             Assert.Equal(1, tab.AllCount);
@@ -125,14 +122,14 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_Test()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
             // 未読なし
             Assert.Equal(-1L, tab.NextUnreadId);
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -141,7 +138,7 @@ namespace OpenTween.Models
 
             Assert.Equal(100L, tab.NextUnreadId);
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 50L,
                 IsRead = true, // 既読
@@ -154,12 +151,12 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_DisabledTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             // 未読表示無効
             tab.UnreadManage = false;
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -172,7 +169,7 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_SortByIdAscTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
@@ -180,9 +177,9 @@ namespace OpenTween.Models
             tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending);
 
             // 画面には上から 100 → 200 → 300 の順に並ぶ
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 300L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 300L, IsRead = false });
             tab.AddSubmit();
 
             // 昇順/降順に関わらず、ID の小さい順に未読の ID を返す
@@ -192,7 +189,7 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_SortByIdDescTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
@@ -200,9 +197,9 @@ namespace OpenTween.Models
             tab.SetSortMode(ComparerMode.Id, SortOrder.Descending);
 
             // 画面には上から 300 → 200 → 100 の順に並ぶ
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 300L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 200L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 300L, IsRead = false });
             tab.AddSubmit();
 
             // 昇順/降順に関わらず、ID の小さい順に未読の ID を返す
@@ -212,7 +209,7 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_SortByScreenNameAscTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
@@ -220,9 +217,9 @@ namespace OpenTween.Models
             tab.SetSortMode(ComparerMode.Name, SortOrder.Ascending);
 
             // 画面には上から 200 → 100 → 300 の順に並ぶ
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, ScreenName = "bbb", IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, ScreenName = "aaa", IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 300L, ScreenName = "ccc", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, ScreenName = "bbb", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 200L, ScreenName = "aaa", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 300L, ScreenName = "ccc", IsRead = false });
             tab.AddSubmit();
 
             // 昇順/降順に関わらず、ScreenName の辞書順で小さい順に未読の ID を返す
@@ -232,7 +229,7 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadId_SortByScreenNameDescTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
@@ -240,9 +237,9 @@ namespace OpenTween.Models
             tab.SetSortMode(ComparerMode.Name, SortOrder.Descending);
 
             // 画面には上から 300 → 100 → 200 の順に並ぶ
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, ScreenName = "bbb", IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, ScreenName = "aaa", IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 300L, ScreenName = "ccc", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, ScreenName = "bbb", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 200L, ScreenName = "aaa", IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 300L, ScreenName = "ccc", IsRead = false });
             tab.AddSubmit();
 
             // 昇順/降順に関わらず、ScreenName の辞書順で小さい順に未読の ID を返す
@@ -252,14 +249,14 @@ namespace OpenTween.Models
         [Fact]
         public void UnreadCount_Test()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
             // 未読なし
             Assert.Equal(0, tab.UnreadCount);
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -268,7 +265,7 @@ namespace OpenTween.Models
 
             Assert.Equal(1, tab.UnreadCount);
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 50L,
                 IsRead = true, // 既読
@@ -281,12 +278,12 @@ namespace OpenTween.Models
         [Fact]
         public void UnreadCount_DisabledTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             // 未読表示無効
             tab.UnreadManage = false;
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -299,7 +296,7 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadIndex_Test()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
             tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending);
 
             tab.UnreadManage = true;
@@ -307,17 +304,17 @@ namespace OpenTween.Models
             // 未読なし
             Assert.Equal(-1, tab.NextUnreadIndex);
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 50L,
                 IsRead = true, // 既読
             });
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
             });
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 150L,
                 IsRead = false, // 未読
@@ -330,13 +327,13 @@ namespace OpenTween.Models
         [Fact]
         public void NextUnreadIndex_DisabledTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
             tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending);
 
             // 未読表示無効
             tab.UnreadManage = false;
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -349,13 +346,13 @@ namespace OpenTween.Models
         [Fact]
         public void GetUnreadIds_Test()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
             tab.UnreadManage = true;
 
             Assert.Empty(tab.GetUnreadIds());
 
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false });
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = true });
+            tab.AddPostQueue(new PostClass { StatusId = 100L, IsRead = false });
+            tab.AddPostQueue(new PostClass { StatusId = 200L, IsRead = true });
             tab.AddSubmit();
 
             Assert.Equal(new[] { 100L }, tab.GetUnreadIds());
@@ -368,11 +365,11 @@ namespace OpenTween.Models
         [Fact]
         public void SetReadState_MarkAsReadTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = false, // 未読
@@ -389,11 +386,11 @@ namespace OpenTween.Models
         [Fact]
         public void SetReadState_MarkAsUnreadTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline };
+            var tab = new PublicSearchTabModel("search");
 
             tab.UnreadManage = true;
 
-            tab.AddPostToInnerStorage(new PostClass
+            tab.AddPostQueue(new PostClass
             {
                 StatusId = 100L,
                 IsRead = true, // 既読
@@ -410,7 +407,7 @@ namespace OpenTween.Models
         [Fact]
         public void FilterArraySetter_Test()
         {
-            var tab = new TabClass();
+            var tab = new FilterTabModel("MyTab");
 
             var filter = new PostFilterRule();
             tab.FilterArray = new[] { filter };
@@ -422,7 +419,7 @@ namespace OpenTween.Models
         [Fact]
         public void AddFilter_Test()
         {
-            var tab = new TabClass();
+            var tab = new FilterTabModel("MyTab");
 
             var filter = new PostFilterRule();
             tab.AddFilter(filter);
@@ -434,7 +431,7 @@ namespace OpenTween.Models
         [Fact]
         public void RemoveFilter_Test()
         {
-            var tab = new TabClass();
+            var tab = new FilterTabModel("MyTab");
 
             var filter = new PostFilterRule();
             tab.FilterArray = new[] { filter };
@@ -449,7 +446,7 @@ namespace OpenTween.Models
         [Fact]
         public void OnFilterModified_Test()
         {
-            var tab = new TabClass();
+            var tab = new FilterTabModel("MyTab");
 
             var filter = new PostFilterRule();
             tab.FilterArray = new[] { filter };
@@ -464,7 +461,7 @@ namespace OpenTween.Models
         [Fact]
         public void OnFilterModified_DetachedTest()
         {
-            var tab = new TabClass();
+            var tab = new FilterTabModel("MyTab");
 
             var filter = new PostFilterRule();
             tab.FilterArray = new[] { filter };
@@ -481,13 +478,13 @@ namespace OpenTween.Models
         [Fact]
         public void SearchPostsAll_Test()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.PublicSearch };
+            var tab = new PublicSearchTabModel("search");
 
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, TextFromApi = "abcd", ScreenName = "", Nickname = "" }); // 0
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 110L, TextFromApi = "efgh", ScreenName = "", Nickname = "" }); // 1
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 120L, TextFromApi = "ijkl", ScreenName = "", Nickname = "" }); // 2
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 130L, TextFromApi = "abc", ScreenName = "", Nickname = "" });  // 3
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 140L, TextFromApi = "def", ScreenName = "", Nickname = "" });  // 4
+            tab.AddPostQueue(new PostClass { StatusId = 100L, TextFromApi = "abcd", ScreenName = "", Nickname = "" }); // 0
+            tab.AddPostQueue(new PostClass { StatusId = 110L, TextFromApi = "efgh", ScreenName = "", Nickname = "" }); // 1
+            tab.AddPostQueue(new PostClass { StatusId = 120L, TextFromApi = "ijkl", ScreenName = "", Nickname = "" }); // 2
+            tab.AddPostQueue(new PostClass { StatusId = 130L, TextFromApi = "abc", ScreenName = "", Nickname = "" });  // 3
+            tab.AddPostQueue(new PostClass { StatusId = 140L, TextFromApi = "def", ScreenName = "", Nickname = "" });  // 4
 
             tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending);
             tab.AddSubmit();
@@ -504,13 +501,13 @@ namespace OpenTween.Models
         [Fact]
         public void SearchPostsAll_ReverseOrderTest()
         {
-            var tab = new TabClass { TabType = MyCommon.TabUsageType.PublicSearch };
+            var tab = new PublicSearchTabModel("search");
 
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, TextFromApi = "abcd", ScreenName = "", Nickname = "" }); // 0
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 110L, TextFromApi = "efgh", ScreenName = "", Nickname = "" }); // 1
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 120L, TextFromApi = "ijkl", ScreenName = "", Nickname = "" }); // 2
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 130L, TextFromApi = "abc", ScreenName = "", Nickname = "" });  // 3
-            tab.AddPostToInnerStorage(new PostClass { StatusId = 140L, TextFromApi = "def", ScreenName = "", Nickname = "" });  // 4
+            tab.AddPostQueue(new PostClass { StatusId = 100L, TextFromApi = "abcd", ScreenName = "", Nickname = "" }); // 0
+            tab.AddPostQueue(new PostClass { StatusId = 110L, TextFromApi = "efgh", ScreenName = "", Nickname = "" }); // 1
+            tab.AddPostQueue(new PostClass { StatusId = 120L, TextFromApi = "ijkl", ScreenName = "", Nickname = "" }); // 2
+            tab.AddPostQueue(new PostClass { StatusId = 130L, TextFromApi = "abc", ScreenName = "", Nickname = "" });  // 3
+            tab.AddPostQueue(new PostClass { StatusId = 140L, TextFromApi = "def", ScreenName = "", Nickname = "" });  // 4
 
             tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending);
             tab.AddSubmit();
index 533119f..5ce1e26 100644 (file)
     <Compile Include="RegexAsyncTest.cs" />
     <Compile Include="ShortcutCommandTest.cs" />
     <Compile Include="ShortUrlTest.cs" />
-    <Compile Include="Models\TabClassTest.cs" />
+    <Compile Include="Models\TabModelTest.cs" />
     <Compile Include="Models\TabInformationTest.cs" />
     <Compile Include="TabsDialogTest.cs" />
     <Compile Include="TestUtils.cs" />
index 7f3e71d..1b7d4c8 100644 (file)
@@ -40,11 +40,11 @@ namespace OpenTween
             this.tabinfo = Activator.CreateInstance(typeof(TabInformations), true) as TabInformations;
 
             // タブを追加
-            this.tabinfo.AddTab("Recent", MyCommon.TabUsageType.Home, null);
-            this.tabinfo.AddTab("Reply", MyCommon.TabUsageType.Mentions, null);
-            this.tabinfo.AddTab("DM", MyCommon.TabUsageType.DirectMessage, null);
-            this.tabinfo.AddTab("Favorites", MyCommon.TabUsageType.Favorites, null);
-            this.tabinfo.AddTab("MyTab1", MyCommon.TabUsageType.UserDefined, null);
+            this.tabinfo.AddTab(new HomeTabModel("Recent"));
+            this.tabinfo.AddTab(new MentionsTabModel("Reply"));
+            this.tabinfo.AddTab(new DirectMessagesTabModel("DM"));
+            this.tabinfo.AddTab(new FavoritesTabModel("Favorites"));
+            this.tabinfo.AddTab(new FilterTabModel("MyTab1"));
 
             // 一応 TabInformation.GetInstance() でも取得できるようにする
             var field = typeof(TabInformations).GetField("_instance",
index fa22958..636d7b8 100644 (file)
@@ -95,9 +95,12 @@ namespace OpenTween
         {
             if (ListTabs.Items.Count == 0) return;
 
-            var tab = _sts.Tabs[tabName];
-
             ListFilters.Items.Clear();
+
+            var tab = _sts.Tabs[tabName] as FilterTabModel;
+            if (tab == null)
+                return;
+
             ListFilters.Items.AddRange(tab.GetFilters());
 
             if (ListFilters.Items.Count > 0)
@@ -406,7 +409,7 @@ namespace OpenTween
             if (rslt == DialogResult.Cancel) return;
 
             var indices = ListFilters.SelectedIndices.Cast<int>().Reverse().ToArray();  // 後ろの要素から削除
-            var tab = _sts.Tabs[ListTabs.SelectedItem.ToString()];
+            var tab = (FilterTabModel)_sts.Tabs[ListTabs.SelectedItem.ToString()];
 
             using (ControlTransaction.Update(ListFilters))
             {
@@ -615,7 +618,7 @@ namespace OpenTween
                 return;
             }
 
-            var tab = this._sts.Tabs[(string)this.ListTabs.SelectedItem];
+            var tab = (FilterTabModel)this._sts.Tabs[(string)this.ListTabs.SelectedItem];
             int i = ListFilters.SelectedIndex;
 
             PostFilterRule ft;
@@ -1028,7 +1031,24 @@ namespace OpenTween
                         list = listAvail.SelectedList;
                     }
                 }
-                if (!_sts.AddTab(tabName, tabType, list) || !((TweenMain)this.Owner).AddNewTab(tabName, false, tabType, list))
+
+                TabModel tab;
+                switch (tabType)
+                {
+                    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))
                 {
                     string tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
                     MessageBox.Show(tmp, Properties.Resources.AddTabMenuItem_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
@@ -1216,7 +1236,7 @@ namespace OpenTween
             }
 
             var lastSelIdx = indices[0] + diff;
-            var tab = _sts.Tabs[ListTabs.Items[tabIdx].ToString()];
+            var tab = (FilterTabModel)_sts.Tabs[ListTabs.Items[tabIdx].ToString()];
 
             try
             {
@@ -1279,7 +1299,7 @@ namespace OpenTween
         {
             if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null)
             {
-                TabClass[] selectedTabs;
+                TabModel[] selectedTabs;
                 using (TabsDialog dialog = new TabsDialog(_sts))
                 {
                     dialog.MultiSelect = true;
@@ -1295,9 +1315,10 @@ namespace OpenTween
 
                 foreach (int idx in ListFilters.SelectedIndices)
                 {
-                    filters.Add(_sts.Tabs[tabname].FilterArray[idx].Clone());
+                    var tab = (FilterTabModel)_sts.Tabs[tabname];
+                    filters.Add(tab.FilterArray[idx].Clone());
                 }
-                foreach (var tb in selectedTabs)
+                foreach (var tb in selectedTabs.Cast<FilterTabModel>())
                 {
                     if (tb.TabName == tabname) continue;
 
@@ -1315,7 +1336,7 @@ namespace OpenTween
         {
             if (ListTabs.SelectedIndex > -1 && ListFilters.SelectedItem != null)
             {
-                TabClass[] selectedTabs;
+                TabModel[] selectedTabs;
                 using (var dialog = new TabsDialog(_sts))
                 {
                     dialog.MultiSelect = true;
@@ -1330,10 +1351,11 @@ namespace OpenTween
 
                 foreach (int idx in ListFilters.SelectedIndices)
                 {
-                    filters.Add(_sts.Tabs[tabname].FilterArray[idx].Clone());
+                    var tab = (FilterTabModel)_sts.Tabs[tabname];
+                    filters.Add(tab.FilterArray[idx].Clone());
                 }
                 if (selectedTabs.Length == 1 && selectedTabs[0].TabName == tabname) return;
-                foreach (var tb in selectedTabs)
+                foreach (var tb in selectedTabs.Cast<FilterTabModel>())
                 {
                     if (tb.TabName == tabname) continue;
 
@@ -1347,7 +1369,8 @@ namespace OpenTween
                 {
                     if (ListFilters.GetSelected(idx))
                     {
-                        _sts.Tabs[ListTabs.SelectedItem.ToString()].RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
+                        var tab = (FilterTabModel)_sts.Tabs[ListTabs.SelectedItem.ToString()];
+                        tab.RemoveFilter((PostFilterRule)ListFilters.Items[idx]);
                         ListFilters.Items.RemoveAt(idx);
                     }
                 }
diff --git a/OpenTween/Models/DirectMessagesTabModel.cs b/OpenTween/Models/DirectMessagesTabModel.cs
new file mode 100644 (file)
index 0000000..f6f8e6f
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class DirectMessagesTabModel : InternalStorageTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.DirectMessage;
+
+        public DirectMessagesTabModel() : this(MyCommon.DEFAULTTAB.DM)
+        {
+        }
+
+        public DirectMessagesTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/FavoritesTabModel.cs b/OpenTween/Models/FavoritesTabModel.cs
new file mode 100644 (file)
index 0000000..900a2c8
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class FavoritesTabModel : TabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Favorites;
+
+        public FavoritesTabModel() : this(MyCommon.DEFAULTTAB.FAV)
+        {
+        }
+
+        public FavoritesTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/FilterTabModel.cs b/OpenTween/Models/FilterTabModel.cs
new file mode 100644 (file)
index 0000000..d71a3d0
--- /dev/null
@@ -0,0 +1,173 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class FilterTabModel : TabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.UserDefined;
+
+        public bool FilterModified { get; set; }
+
+        private List<PostFilterRule> _filters = new List<PostFilterRule>();
+        private object lockObjFilters = new object();
+
+        public FilterTabModel(string tabName) : base(tabName)
+        {
+        }
+
+        //フィルタに合致したら追加
+        public MyCommon.HITRESULT AddFiltered(PostClass post, bool immediately = false)
+        {
+            if (this.IsInnerStorageTabType)
+                return MyCommon.HITRESULT.None;
+
+            var rslt = MyCommon.HITRESULT.None;
+
+            //全フィルタ評価(優先順位あり)
+            lock (this.lockObjFilters)
+            {
+                foreach (var ft in _filters)
+                {
+                    try
+                    {
+                        switch (ft.ExecFilter(post))   //フィルタクラスでヒット判定
+                        {
+                            case MyCommon.HITRESULT.None:
+                                break;
+                            case MyCommon.HITRESULT.Copy:
+                                if (rslt != MyCommon.HITRESULT.CopyAndMark) rslt = MyCommon.HITRESULT.Copy;
+                                break;
+                            case MyCommon.HITRESULT.CopyAndMark:
+                                rslt = MyCommon.HITRESULT.CopyAndMark;
+                                break;
+                            case MyCommon.HITRESULT.Move:
+                                rslt = MyCommon.HITRESULT.Move;
+                                break;
+                            case MyCommon.HITRESULT.Exclude:
+                                rslt = MyCommon.HITRESULT.Exclude;
+                                goto exit_for;
+                        }
+                    }
+                    catch (NullReferenceException)
+                    {
+                        // ExecFilterでNullRef出る場合あり。暫定対応
+                        MyCommon.TraceOut("ExecFilterでNullRef: " + ft.ToString());
+                        rslt = MyCommon.HITRESULT.None;
+                    }
+                }
+                exit_for:
+                ;
+            }
+
+            if (this.TabType != MyCommon.TabUsageType.Mute &&
+                rslt != MyCommon.HITRESULT.None && rslt != MyCommon.HITRESULT.Exclude)
+            {
+                if (immediately)
+                    this.AddPostImmediately(post.StatusId, post.IsRead);
+                else
+                    this.AddPostQueue(post);
+            }
+
+            return rslt; //マーク付けは呼び出し元で行うこと
+        }
+
+
+        public PostFilterRule[] GetFilters()
+        {
+            lock (this.lockObjFilters)
+            {
+                return _filters.ToArray();
+            }
+        }
+
+        public void RemoveFilter(PostFilterRule filter)
+        {
+            lock (this.lockObjFilters)
+            {
+                _filters.Remove(filter);
+                filter.PropertyChanged -= this.OnFilterModified;
+                this.FilterModified = true;
+            }
+        }
+
+        public bool AddFilter(PostFilterRule filter)
+        {
+            lock (this.lockObjFilters)
+            {
+                if (_filters.Contains(filter)) return false;
+                filter.PropertyChanged += this.OnFilterModified;
+                _filters.Add(filter);
+                this.FilterModified = true;
+                return true;
+            }
+        }
+
+        private void OnFilterModified(object sender, PropertyChangedEventArgs e)
+        {
+            this.FilterModified = true;
+        }
+
+        public PostFilterRule[] FilterArray
+        {
+            get
+            {
+                lock (this.lockObjFilters)
+                {
+                    return _filters.ToArray();
+                }
+            }
+            set
+            {
+                lock (this.lockObjFilters)
+                {
+                    foreach (var oldFilter in this._filters)
+                    {
+                        oldFilter.PropertyChanged -= this.OnFilterModified;
+                    }
+
+                    this._filters.Clear();
+                    this.FilterModified = true;
+
+                    foreach (var newFilter in value)
+                    {
+                        _filters.Add(newFilter);
+                        newFilter.PropertyChanged += this.OnFilterModified;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/OpenTween/Models/HomeTabModel.cs b/OpenTween/Models/HomeTabModel.cs
new file mode 100644 (file)
index 0000000..fe44419
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class HomeTabModel : TabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Home;
+
+        public HomeTabModel() : this(MyCommon.DEFAULTTAB.RECENT)
+        {
+        }
+
+        public HomeTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/InternalStorageTabModel.cs b/OpenTween/Models/InternalStorageTabModel.cs
new file mode 100644 (file)
index 0000000..9cd6886
--- /dev/null
@@ -0,0 +1,88 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public abstract class InternalStorageTabModel : TabModel
+    {
+        protected readonly ConcurrentDictionary<long, PostClass> internalPosts = new ConcurrentDictionary<long, PostClass>();
+
+        public override ConcurrentDictionary<long, PostClass> Posts
+            => this.internalPosts;
+
+        public InternalStorageTabModel(string tabName) : base(tabName)
+        {
+        }
+
+        public override void AddPostQueue(PostClass post)
+        {
+            if (TabInformations.GetInstance().IsMuted(post, isHomeTimeline: false))
+                return;
+
+            base.AddPostQueue(post);
+
+            if (!this.internalPosts.TryAdd(post.StatusId, post))
+                return;
+        }
+
+        public override void EnqueueRemovePost(long statusId, bool setIsDeleted)
+        {
+            base.EnqueueRemovePost(statusId, setIsDeleted);
+
+            if (setIsDeleted)
+            {
+                PostClass post;
+                if (this.internalPosts.TryGetValue(statusId, out post))
+                    post.IsDeleted = true;
+            }
+        }
+
+        public override bool RemovePostImmediately(long statusId)
+        {
+            if (!base.RemovePostImmediately(statusId))
+                return false;
+
+            PostClass removedPost;
+            this.internalPosts.TryRemove(statusId, out removedPost);
+
+            return true;
+        }
+
+        public override void ClearIDs()
+        {
+            base.ClearIDs();
+            this.internalPosts.Clear();
+        }
+    }
+}
diff --git a/OpenTween/Models/ListTimelineTabModel.cs b/OpenTween/Models/ListTimelineTabModel.cs
new file mode 100644 (file)
index 0000000..97f558f
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class ListTimelineTabModel : InternalStorageTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Lists;
+
+        public ListElement ListInfo { get; set; }
+
+        public ListTimelineTabModel(string tabName, ListElement list)
+            : base(tabName)
+        {
+            this.ListInfo = list;
+        }
+    }
+}
diff --git a/OpenTween/Models/LocalSearchTabModel.cs b/OpenTween/Models/LocalSearchTabModel.cs
new file mode 100644 (file)
index 0000000..d682ad7
--- /dev/null
@@ -0,0 +1,39 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class LocalSearchTabModel : InternalStorageTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.SearchResults;
+
+        public LocalSearchTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/MentionsTabModel.cs b/OpenTween/Models/MentionsTabModel.cs
new file mode 100644 (file)
index 0000000..6ae3116
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class MentionsTabModel : FilterTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Mentions;
+
+        public MentionsTabModel() : this(MyCommon.DEFAULTTAB.REPLY)
+        {
+        }
+
+        public MentionsTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/MuteTabModel.cs b/OpenTween/Models/MuteTabModel.cs
new file mode 100644 (file)
index 0000000..e039094
--- /dev/null
@@ -0,0 +1,47 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2016 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class MuteTabModel : FilterTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Mute;
+
+        public MuteTabModel() : this(MyCommon.DEFAULTTAB.MUTE)
+        {
+        }
+
+        public MuteTabModel(string tabName) : base(tabName)
+        {
+        }
+
+        public override void AddPostQueue(PostClass post)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/PublicSearchTabModel.cs b/OpenTween/Models/PublicSearchTabModel.cs
new file mode 100644 (file)
index 0000000..1bee750
--- /dev/null
@@ -0,0 +1,68 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class PublicSearchTabModel : InternalStorageTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.PublicSearch;
+
+        public string SearchWords
+        {
+            get { return this._searchWords; }
+            set
+            {
+                this._searchWords = value;
+                this.ResetFetchIds();
+            }
+        }
+
+        public string SearchLang
+        {
+            get { return this._searchLang; }
+            set
+            {
+                this._searchLang = value;
+                this.ResetFetchIds();
+            }
+        }
+
+        private string _searchWords = "";
+        private string _searchLang = "";
+
+        public PublicSearchTabModel(string tabName) : base(tabName)
+        {
+        }
+    }
+}
diff --git a/OpenTween/Models/RelatedPostsTabModel.cs b/OpenTween/Models/RelatedPostsTabModel.cs
new file mode 100644 (file)
index 0000000..f18d2cf
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class RelatedPostsTabModel : TabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.Related;
+
+        public PostClass TargetPost { get; }
+
+        public RelatedPostsTabModel(string tabName, PostClass targetPost)
+            : base(tabName)
+        {
+            this.TargetPost = targetPost;
+        }
+    }
+}
diff --git a/OpenTween/Models/TabClass.cs b/OpenTween/Models/TabClass.cs
deleted file mode 100644 (file)
index 41f85db..0000000
+++ /dev/null
@@ -1,738 +0,0 @@
-// OpenTween - Client of Twitter
-// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
-//           (c) 2008-2011 Moz (@syo68k)
-//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
-//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
-//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
-//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
-//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
-// All rights reserved.
-//
-// This file is part of OpenTween.
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by the Free
-// Software Foundation; either version 3 of the License, or (at your option)
-// any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-// for more details.
-//
-// You should have received a copy of the GNU General Public License along
-// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
-// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
-// Boston, MA 02110-1301, USA.
-
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace OpenTween.Models
-{
-    public sealed class TabClass
-    {
-        private List<PostFilterRule> _filters;
-        private IndexedSortedSet<long> _ids;
-        private ConcurrentQueue<TemporaryId> addQueue = new ConcurrentQueue<TemporaryId>();
-        private ConcurrentQueue<long> removeQueue = new ConcurrentQueue<long>();
-        private SortedSet<long> unreadIds = new SortedSet<long>();
-        private MyCommon.TabUsageType _tabType = MyCommon.TabUsageType.Undefined;
-
-        private readonly object _lockObj = new object();
-
-        public string User { get; set; }
-
-        #region "検索"
-        //Search query
-        private string _searchLang = "";
-        private string _searchWords = "";
-
-        public string SearchLang
-        {
-            get
-            {
-                return _searchLang;
-            }
-            set
-            {
-                _searchLang = value;
-                this.ResetFetchIds();
-            }
-        }
-        public string SearchWords
-        {
-            get
-            {
-                return _searchWords;
-            }
-            set
-            {
-                _searchWords = value.Trim();
-                this.ResetFetchIds();
-            }
-        }
-
-        private Dictionary<string, string> _beforeQuery = new Dictionary<string, string>();
-
-        public bool IsSearchQueryChanged
-        {
-            get
-            {
-                var qry = new Dictionary<string, string>();
-                if (!string.IsNullOrEmpty(_searchWords))
-                {
-                    qry.Add("q", _searchWords);
-                    if (!string.IsNullOrEmpty(_searchLang)) qry.Add("lang", _searchLang);
-                }
-                if (qry.Count != _beforeQuery.Count)
-                {
-                    _beforeQuery = qry;
-                    return true;
-                }
-
-                foreach (var kvp in qry)
-                {
-                    string value;
-                    if (!_beforeQuery.TryGetValue(kvp.Key, out value) || value != kvp.Value)
-                    {
-                        _beforeQuery = qry;
-                        return true;
-                    }
-                }
-                return false;
-            }
-        }
-        #endregion
-
-        #region "リスト"
-        private ListElement _listInfo;
-        public ListElement ListInfo
-        {
-            get
-            {
-                return _listInfo;
-            }
-            set
-            {
-                _listInfo = value;
-            }
-        }
-        #endregion
-
-        public PostClass RelationTargetPost { get; set; }
-
-        public long OldestId = long.MaxValue;
-
-        public long SinceId { get; set; }
-
-        public ConcurrentDictionary<long, PostClass> Posts { get; private set; }
-
-        private ConcurrentDictionary<long, PostClass> _innerPosts;
-
-        private struct TemporaryId
-        {
-            public long Id;
-            public bool Read;
-
-            public TemporaryId(long argId, bool argRead)
-            {
-                Id = argId;
-                Read = argRead;
-            }
-        }
-
-        public TabClass()
-        {
-            _innerPosts = new ConcurrentDictionary<long, PostClass>();
-            Posts = _innerPosts;
-            SoundFile = "";
-            TabName = "";
-            _filters = new List<PostFilterRule>();
-            Protected = false;
-            Notify = true;
-            SoundFile = "";
-            UnreadManage = true;
-            _ids = new IndexedSortedSet<long>();
-            _tabType = MyCommon.TabUsageType.Undefined;
-            _listInfo = null;
-        }
-
-        public TabClass(string name, MyCommon.TabUsageType type) : this(name, type, list: null)
-        {
-        }
-
-        public TabClass(string TabName, MyCommon.TabUsageType TabType, ListElement list) : this()
-        {
-            this.TabName = TabName;
-            this.TabType = TabType;
-            this.ListInfo = list;
-        }
-
-        /// <summary>
-        /// タブ更新時に使用する SinceId, OldestId をリセットする
-        /// </summary>
-        public void ResetFetchIds()
-        {
-            this.SinceId = 0L;
-            this.OldestId = long.MaxValue;
-        }
-
-        /// <summary>
-        /// ソート対象のフィールドとソート順を設定し、ソートを実行します
-        /// </summary>
-        public void SetSortMode(ComparerMode mode, SortOrder sortOrder)
-        {
-            this.SortMode = mode;
-            this.SortOrder = sortOrder;
-
-            this.ApplySortMode();
-        }
-
-        private void ApplySortMode()
-        {
-            var sign = this.SortOrder == SortOrder.Ascending ? 1 : -1;
-
-            Comparison<long> comparison;
-            if (this.SortMode == ComparerMode.Id)
-            {
-                comparison = (x, y) => sign * x.CompareTo(y);
-            }
-            else
-            {
-                Comparison<PostClass> postComparison;
-                switch (this.SortMode)
-                {
-                    default:
-                    case ComparerMode.Data:
-                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.TextFromApi, y?.TextFromApi);
-                        break;
-                    case ComparerMode.Name:
-                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.ScreenName, y?.ScreenName);
-                        break;
-                    case ComparerMode.Nickname:
-                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.Nickname, y?.Nickname);
-                        break;
-                    case ComparerMode.Source:
-                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.Source, y?.Source);
-                        break;
-                }
-
-                comparison = (x, y) =>
-                {
-                    PostClass xPost, yPost;
-                    this.Posts.TryGetValue(x, out xPost);
-                    this.Posts.TryGetValue(y, out yPost);
-
-                    var compare = sign * postComparison(xPost, yPost);
-                    if (compare != 0)
-                        return compare;
-
-                    // 同値であれば status_id で比較する
-                    return sign * x.CompareTo(y);
-                };
-            }
-
-            var comparer = Comparer<long>.Create(comparison);
-
-            this._ids = new IndexedSortedSet<long>(this._ids, comparer);
-            this.unreadIds = new SortedSet<long>(this.unreadIds, comparer);
-        }
-
-        public ComparerMode SortMode { get; private set; }
-        public SortOrder SortOrder { get; private set; }
-
-        public void AddPostQueue(long statusId, bool read)
-        {
-            this.addQueue.Enqueue(new TemporaryId(statusId, read));
-        }
-
-        //無条件に追加
-        public void AddPostImmediately(long ID, bool Read)
-        {
-            if (this._ids.Contains(ID)) return;
-
-            this._ids.Add(ID);
-
-            if (!Read)
-                this.unreadIds.Add(ID);
-        }
-
-        //フィルタに合致したら追加
-        public MyCommon.HITRESULT AddFiltered(PostClass post, bool immediately = false)
-        {
-            if (this.IsInnerStorageTabType) return MyCommon.HITRESULT.None;
-
-            var rslt = MyCommon.HITRESULT.None;
-            //全フィルタ評価(優先順位あり)
-            lock (this._lockObj)
-            {
-                foreach (var ft in _filters)
-                {
-                    try
-                    {
-                        switch (ft.ExecFilter(post))   //フィルタクラスでヒット判定
-                        {
-                            case MyCommon.HITRESULT.None:
-                                break;
-                            case MyCommon.HITRESULT.Copy:
-                                if (rslt != MyCommon.HITRESULT.CopyAndMark) rslt = MyCommon.HITRESULT.Copy;
-                                break;
-                            case MyCommon.HITRESULT.CopyAndMark:
-                                rslt = MyCommon.HITRESULT.CopyAndMark;
-                                break;
-                            case MyCommon.HITRESULT.Move:
-                                rslt = MyCommon.HITRESULT.Move;
-                                break;
-                            case MyCommon.HITRESULT.Exclude:
-                                rslt = MyCommon.HITRESULT.Exclude;
-                                goto exit_for;
-                        }
-                    }
-                    catch (NullReferenceException)
-                    {
-                        // ExecFilterでNullRef出る場合あり。暫定対応
-                        MyCommon.TraceOut("ExecFilterでNullRef: " + ft.ToString());
-                        rslt = MyCommon.HITRESULT.None;
-                    }
-                }
-                exit_for:
-                ;
-            }
-
-            if (this.TabType != MyCommon.TabUsageType.Mute &&
-                rslt != MyCommon.HITRESULT.None && rslt != MyCommon.HITRESULT.Exclude)
-            {
-                if (immediately)
-                    this.AddPostImmediately(post.StatusId, post.IsRead);
-                else
-                    this.AddPostQueue(post.StatusId, post.IsRead);
-            }
-
-            return rslt; //マーク付けは呼び出し元で行うこと
-        }
-
-        //検索結果の追加
-        public void AddPostToInnerStorage(PostClass Post)
-        {
-            if (_innerPosts.ContainsKey(Post.StatusId))
-                return;
-
-            if (TabInformations.GetInstance().IsMuted(Post, isHomeTimeline: false))
-                return;
-
-            _innerPosts.TryAdd(Post.StatusId, Post);
-            this.AddPostQueue(Post.StatusId, Post.IsRead);
-        }
-
-        public IList<long> AddSubmit()
-        {
-            var addedIds = new List<long>();
-
-            TemporaryId tId;
-            while (this.addQueue.TryDequeue(out tId))
-            {
-                this.AddPostImmediately(tId.Id, tId.Read);
-                addedIds.Add(tId.Id);
-            }
-
-            return addedIds;
-        }
-
-        public void EnqueueRemovePost(long statusId, bool setIsDeleted)
-        {
-            this.removeQueue.Enqueue(statusId);
-
-            if (setIsDeleted && this.IsInnerStorageTabType)
-            {
-                PostClass post;
-                if (this._innerPosts.TryGetValue(statusId, out post))
-                    post.IsDeleted = true;
-            }
-        }
-
-        public bool RemovePostImmediately(long statusId)
-        {
-            if (!this._ids.Remove(statusId))
-                return false;
-
-            this.unreadIds.Remove(statusId);
-
-            if (this.IsInnerStorageTabType)
-            {
-                PostClass removedPost;
-                this._innerPosts.TryRemove(statusId, out removedPost);
-            }
-
-            return true;
-        }
-
-        public IReadOnlyList<long> RemoveSubmit()
-        {
-            var removedIds = new List<long>();
-
-            long statusId;
-            while (this.removeQueue.TryDequeue(out statusId))
-            {
-                if (this.RemovePostImmediately(statusId))
-                    removedIds.Add(statusId);
-            }
-
-            return removedIds;
-        }
-
-        public bool UnreadManage { get; set; }
-        public bool Protected { get; set; }
-        public bool Notify { get; set; }
-        public string SoundFile { get; set; }
-
-        /// <summary>
-        /// 次に表示する未読ツイートのIDを返します。
-        /// ただし、未読がない場合または UnreadManage が false の場合は -1 を返します
-        /// </summary>
-        public long NextUnreadId
-        {
-            get
-            {
-                if (!this.UnreadManage || !SettingCommon.Instance.UnreadManage)
-                    return -1L;
-
-                if (this.unreadIds.Count == 0)
-                    return -1L;
-
-                // unreadIds はリストのインデックス番号順に並んでいるため、
-                // 例えば ID 順の整列であれば昇順なら上から、降順なら下から順に返せば過去→現在の順になる
-                return this.SortOrder == SortOrder.Ascending ? this.unreadIds.Min : this.unreadIds.Max;
-            }
-        }
-
-        /// <summary>
-        /// 次に表示する未読ツイートのインデックス番号を返します。
-        /// ただし、未読がない場合または UnreadManage が false の場合は -1 を返します
-        /// </summary>
-        public int NextUnreadIndex
-        {
-            get
-            {
-                var unreadId = this.NextUnreadId;
-                return unreadId != -1 ? this.IndexOf(unreadId) : -1;
-            }
-        }
-
-        /// <summary>
-        /// 未読ツイートの件数を返します。
-        /// ただし、未読がない場合または UnreadManage が false の場合は 0 を返します
-        /// </summary>
-        public int UnreadCount
-        {
-            get
-            {
-                if (!this.UnreadManage || !SettingCommon.Instance.UnreadManage)
-                    return 0;
-
-                return this.unreadIds.Count;
-            }
-        }
-
-        public int AllCount
-        {
-            get
-            {
-                return this._ids.Count;
-            }
-        }
-
-        /// <summary>
-        /// 未読ツイートの ID を配列で返します
-        /// </summary>
-        public long[] GetUnreadIds()
-        {
-            lock (this._lockObj)
-            {
-                return this.unreadIds.ToArray();
-            }
-        }
-
-        /// <summary>
-        /// タブ内の既読状態を変更します
-        /// </summary>
-        /// <remarks>
-        /// 全タブを横断して既読状態を変える TabInformation.SetReadAllTab() の内部で呼び出されるメソッドです
-        /// </remarks>
-        /// <param name="statusId">変更するツイートのID</param>
-        /// <param name="read">既読状態</param>
-        /// <returns>既読状態に変化があれば true、変化がなければ false</returns>
-        internal bool SetReadState(long statusId, bool read)
-        {
-            if (!this._ids.Contains(statusId))
-                throw new ArgumentOutOfRangeException(nameof(statusId));
-
-            if (this.IsInnerStorageTabType)
-                this.Posts[statusId].IsRead = read;
-
-            if (read)
-                return this.unreadIds.Remove(statusId);
-            else
-                return this.unreadIds.Add(statusId);
-        }
-
-        public PostFilterRule[] GetFilters()
-        {
-            lock (this._lockObj)
-            {
-                return _filters.ToArray();
-            }
-        }
-
-        public void RemoveFilter(PostFilterRule filter)
-        {
-            lock (this._lockObj)
-            {
-                _filters.Remove(filter);
-                filter.PropertyChanged -= this.OnFilterModified;
-                this.FilterModified = true;
-            }
-        }
-
-        public bool AddFilter(PostFilterRule filter)
-        {
-            lock (this._lockObj)
-            {
-                if (_filters.Contains(filter)) return false;
-                filter.PropertyChanged += this.OnFilterModified;
-                _filters.Add(filter);
-                this.FilterModified = true;
-                return true;
-            }
-        }
-
-        private void OnFilterModified(object sender, PropertyChangedEventArgs e)
-        {
-            this.FilterModified = true;
-        }
-
-        public PostFilterRule[] FilterArray
-        {
-            get
-            {
-                lock (this._lockObj)
-                {
-                    return _filters.ToArray();
-                }
-            }
-            set
-            {
-                lock (this._lockObj)
-                {
-                    foreach (var oldFilter in this._filters)
-                    {
-                        oldFilter.PropertyChanged -= this.OnFilterModified;
-                    }
-
-                    this._filters.Clear();
-                    this.FilterModified = true;
-
-                    foreach (var newFilter in value)
-                    {
-                        _filters.Add(newFilter);
-                        newFilter.PropertyChanged += this.OnFilterModified;
-                    }
-                }
-            }
-        }
-        public bool Contains(long ID)
-        {
-            return _ids.Contains(ID);
-        }
-
-        public void ClearIDs()
-        {
-            _ids.Clear();
-            this.unreadIds.Clear();
-            _innerPosts.Clear();
-
-            Interlocked.Exchange(ref this.addQueue, new ConcurrentQueue<TemporaryId>());
-        }
-
-        public PostClass this[int Index]
-        {
-            get
-            {
-                var id = GetId(Index);
-                if (id < 0) throw new ArgumentException("Index can't find. Index=" + Index.ToString() + "/TabName=" + TabName, nameof(Index));
-                return Posts[id];
-            }
-        }
-
-        public PostClass[] this[int StartIndex, int EndIndex]
-        {
-            get
-            {
-                var length = EndIndex - StartIndex + 1;
-                var posts = new PostClass[length];
-                for (int i = 0; i < length; i++)
-                {
-                    posts[i] = Posts[GetId(StartIndex + i)];
-                }
-                return posts;
-            }
-        }
-
-        public long[] GetId(ListView.SelectedIndexCollection IndexCollection)
-        {
-            if (IndexCollection.Count == 0) return null;
-
-            var Ids = new long[IndexCollection.Count];
-            for (int i = 0; i < Ids.Length; i++)
-            {
-                Ids[i] = GetId(IndexCollection[i]);
-            }
-            return Ids;
-        }
-
-        public long GetId(int Index)
-        {
-            return Index < _ids.Count ? _ids[Index] : -1;
-        }
-
-        public int[] IndexOf(long[] Ids)
-        {
-            if (Ids == null) return null;
-            var idx = new int[Ids.Length];
-            for (int i = 0; i < Ids.Length; i++)
-            {
-                idx[i] = IndexOf(Ids[i]);
-            }
-            return idx;
-        }
-
-        public int IndexOf(long ID)
-        {
-            return _ids.IndexOf(ID);
-        }
-
-        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer)
-            => this.SearchPostsAll(stringComparer, reverse: false);
-
-        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, int startIndex)
-            => this.SearchPostsAll(stringComparer, startIndex, reverse: false);
-
-        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, bool reverse)
-        {
-            var startIndex = reverse ? this.AllCount - 1 : 0;
-
-            return this.SearchPostsAll(stringComparer, startIndex, reverse: false);
-        }
-
-        /// <summary>
-        /// タブ内の発言を指定された条件で検索します
-        /// </summary>
-        /// <param name="stringComparer">発言内容、スクリーン名、名前と比較する条件。マッチしたら true を返す</param>
-        /// <param name="startIndex">検索を開始する位置</param>
-        /// <param name="reverse">インデックスの昇順に検索する場合は false、降順の場合は true</param>
-        /// <returns></returns>
-        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, int startIndex, bool reverse)
-        {
-            if (this.AllCount == 0)
-                yield break;
-
-            var searchIndices = Enumerable.Empty<int>();
-
-            if (!reverse)
-            {
-                // startindex ...末尾
-                if (startIndex != this.AllCount - 1)
-                    searchIndices = MyCommon.CountUp(startIndex, this.AllCount - 1);
-
-                // 先頭 ... (startIndex - 1)
-                if (startIndex != 0)
-                    searchIndices = searchIndices.Concat(MyCommon.CountUp(0, startIndex - 1));
-            }
-            else
-            {
-                // startIndex ... 先頭
-                if (startIndex != 0)
-                    searchIndices = MyCommon.CountDown(startIndex, 0);
-
-                // 末尾 ... (startIndex - 1)
-                if (startIndex != this.AllCount - 1)
-                    searchIndices = searchIndices.Concat(MyCommon.CountDown(this.AllCount - 1, startIndex - 1));
-            }
-
-            foreach (var index in searchIndices)
-            {
-                PostClass post;
-                if (!this.Posts.TryGetValue(this.GetId(index), out post))
-                    continue;
-
-                if (stringComparer(post.Nickname) || stringComparer(post.TextFromApi) || stringComparer(post.ScreenName))
-                {
-                    yield return index;
-                }
-            }
-        }
-
-        public bool FilterModified { get; set; }
-
-        public long[] BackupIds
-        {
-            get
-            {
-                return _ids.ToArray();
-            }
-        }
-
-        public string TabName { get; set; }
-
-        public MyCommon.TabUsageType TabType
-        {
-            get
-            {
-                return _tabType;
-            }
-            set
-            {
-                _tabType = value;
-                if (this.IsInnerStorageTabType)
-                {
-                    Posts = _innerPosts;
-                }
-                else
-                {
-                    Posts = TabInformations.GetInstance().Posts;
-                }
-            }
-        }
-
-        public bool IsDefaultTabType
-        {
-            get
-            {
-                return _tabType.IsDefault();
-            }
-        }
-
-        public bool IsDistributableTabType
-        {
-            get
-            {
-                return _tabType.IsDistributable();
-            }
-        }
-
-        public bool IsInnerStorageTabType
-        {
-            get
-            {
-                return _tabType.IsInnerStorage();
-            }
-        }
-    }
-}
index 6ac8ee8..0a58c5c 100644 (file)
@@ -39,11 +39,11 @@ namespace OpenTween.Models
     public sealed class TabInformations
     {
         //個別タブの情報をDictionaryで保持
-        private Dictionary<string, TabClass> _tabs = new Dictionary<string, TabClass>();
+        private Dictionary<string, TabModel> _tabs = new Dictionary<string, TabModel>();
         private ConcurrentDictionary<long, PostClass> _statuses = new ConcurrentDictionary<long, PostClass>();
         private Dictionary<long, PostClass> _retweets = new Dictionary<long, PostClass>();
         private Dictionary<long, PostClass> _quotes = new Dictionary<long, PostClass>();
-        private Stack<TabClass> _removedTab = new Stack<TabClass>();
+        private Stack<TabModel> _removedTab = new Stack<TabModel>();
 
         public ISet<long> BlockIds = new HashSet<long>();
         public ISet<long> MuteUserIds = new HashSet<long>();
@@ -90,7 +90,7 @@ namespace OpenTween.Models
             {
                 if (value != null && value.Count > 0)
                 {
-                    foreach (var tb in this.GetTabsByType(MyCommon.TabUsageType.Lists))
+                    foreach (var tb in this.GetTabsByType<ListTimelineTabModel>())
                     {
                         foreach (var list in value)
                         {
@@ -106,10 +106,7 @@ namespace OpenTween.Models
             }
         }
 
-        public bool AddTab(string TabName, MyCommon.TabUsageType TabType, ListElement List)
-            => this.AddTab(new TabClass(TabName, TabType, List));
-
-        public bool AddTab(TabClass tab)
+        public bool AddTab(TabModel tab)
         {
             if (this._tabs.ContainsKey(tab.TabName))
                 return false;
@@ -140,7 +137,7 @@ namespace OpenTween.Models
                     for (int idx = 0; idx < tb.AllCount; ++idx)
                     {
                         var exist = false;
-                        var Id = tb.GetId(idx);
+                        var Id = tb.GetStatusIdAt(idx);
                         if (Id < 0) continue;
                         foreach (var tab in _tabs.Values)
                         {
@@ -161,7 +158,7 @@ namespace OpenTween.Models
             }
         }
 
-        public Stack<TabClass> RemovedTab
+        public Stack<TabModel> RemovedTab
         {
             get { return _removedTab; }
         }
@@ -171,7 +168,7 @@ namespace OpenTween.Models
             return _tabs.ContainsKey(TabText);
         }
 
-        public bool ContainsTab(TabClass ts)
+        public bool ContainsTab(TabModel ts)
         {
             return _tabs.ContainsValue(ts);
         }
@@ -212,7 +209,7 @@ namespace OpenTween.Models
             throw new TabException(message);
         }
 
-        public Dictionary<string, TabClass> Tabs
+        public Dictionary<string, TabModel> Tabs
         {
             get
             {
@@ -224,7 +221,7 @@ namespace OpenTween.Models
             }
         }
 
-        public Dictionary<string, TabClass>.KeyCollection KeysTab
+        public Dictionary<string, TabModel>.KeyCollection KeysTab
         {
             get
             {
@@ -428,7 +425,7 @@ namespace OpenTween.Models
                 var replyTab = this.GetTabByType(MyCommon.TabUsageType.Mentions);
                 var favTab = this.GetTabByType(MyCommon.TabUsageType.Favorites);
 
-                var distributableTabs = this._tabs.Values.Where(x => x.IsDistributableTabType)
+                var distributableTabs = this.GetTabsByType<FilterTabModel>()
                     .ToArray();
 
                 var adddedCount = 0;
@@ -476,15 +473,15 @@ namespace OpenTween.Models
 
                     // 移動されなかったらRecentに追加
                     if (!moved)
-                        homeTab.AddPostQueue(post.StatusId, post.IsRead);
+                        homeTab.AddPostQueue(post);
 
                     // 除外ルール適用のないReplyならReplyタブに追加
                     if (post.IsReply && !excludedReply)
-                        replyTab.AddPostQueue(post.StatusId, post.IsRead);
+                        replyTab.AddPostQueue(post);
 
                     // Fav済み発言だったらFavoritesタブに追加
                     if (post.IsFav)
-                        favTab.AddPostQueue(post.StatusId, post.IsRead);
+                        favTab.AddPostQueue(post);
 
                     adddedCount++;
                 }
@@ -550,7 +547,7 @@ namespace OpenTween.Models
 
         public bool IsMuted(PostClass post, bool isHomeTimeline)
         {
-            var muteTab = this.GetTabByType(MyCommon.TabUsageType.Mute);
+            var muteTab = this.GetTabByType<MuteTabModel>();
             if (muteTab != null && muteTab.AddFiltered(post) == MyCommon.HITRESULT.Move)
                 return true;
 
@@ -700,11 +697,8 @@ namespace OpenTween.Models
                 var homeTab = GetTabByType(MyCommon.TabUsageType.Home);
                 var detachedIdsAll = Enumerable.Empty<long>();
 
-                foreach (var tab in _tabs.Values.ToArray())
+                foreach (var tab in _tabs.Values.OfType<FilterTabModel>().ToArray())
                 {
-                    if (!tab.IsDistributableTabType)
-                        continue;
-
                     if (tab.TabType == MyCommon.TabUsageType.Mute)
                         continue;
 
@@ -715,7 +709,7 @@ namespace OpenTween.Models
                     tab.FilterModified = false;
 
                     // フィルタ実行前の時点でタブに含まれていたstatusIdを記憶する
-                    var orgIds = tab.BackupIds;
+                    var orgIds = tab.StatusIds;
                     tab.ClearIDs();
 
                     foreach (var post in _statuses.Values)
@@ -763,7 +757,7 @@ namespace OpenTween.Models
                     }
 
                     // フィルタの更新によってタブから取り除かれたツイートのID
-                    var detachedIds = orgIds.Except(tab.BackupIds).ToArray();
+                    var detachedIds = orgIds.Except(tab.StatusIds).ToArray();
 
                     detachedIdsAll = detachedIdsAll.Concat(detachedIds);
                 }
@@ -802,7 +796,7 @@ namespace OpenTween.Models
                 var tb = _tabs[TabName];
                 if (!tb.IsInnerStorageTabType)
                 {
-                    foreach (var Id in tb.BackupIds)
+                    foreach (var Id in tb.StatusIds)
                     {
                         var Hit = false;
                         foreach (var tab in _tabs.Values)
@@ -855,7 +849,7 @@ namespace OpenTween.Models
             }
         }
 
-        public TabClass GetTabByType(MyCommon.TabUsageType tabType)
+        public TabModel GetTabByType(MyCommon.TabUsageType tabType)
         {
             //Home,Mentions,DM,Favは1つに制限する
             //その他のタイプを指定されたら、最初に合致したものを返す
@@ -867,7 +861,13 @@ namespace OpenTween.Models
             }
         }
 
-        public TabClass[] GetTabsByType(MyCommon.TabUsageType tabType)
+        public T GetTabByType<T>() where T : TabModel
+        {
+            lock (this.LockObj)
+                return this._tabs.Values.OfType<T>().FirstOrDefault();
+        }
+
+        public TabModel[] GetTabsByType(MyCommon.TabUsageType tabType)
         {
             lock (LockObj)
             {
@@ -877,7 +877,13 @@ namespace OpenTween.Models
             }
         }
 
-        public TabClass[] GetTabsInnerStorageType()
+        public T[] GetTabsByType<T>() where T : TabModel
+        {
+            lock (this.LockObj)
+                return this._tabs.Values.OfType<T>().ToArray();
+        }
+
+        public TabModel[] GetTabsInnerStorageType()
         {
             lock (LockObj)
             {
@@ -887,11 +893,11 @@ namespace OpenTween.Models
             }
         }
 
-        public TabClass GetTabByName(string tabName)
+        public TabModel GetTabByName(string tabName)
         {
             lock (LockObj)
             {
-                TabClass tab;
+                TabModel tab;
                 return _tabs.TryGetValue(tabName, out tab)
                     ? tab
                     : null;
diff --git a/OpenTween/Models/TabModel.cs b/OpenTween/Models/TabModel.cs
new file mode 100644 (file)
index 0000000..20f57c9
--- /dev/null
@@ -0,0 +1,417 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace OpenTween.Models
+{
+    public abstract class TabModel
+    {
+        public string TabName { get; set; }
+
+        public bool UnreadManage { get; set; } = true;
+        public bool Protected { get; set; }
+        public bool Notify { get; set; } = true;
+        public string SoundFile { get; set; } = "";
+
+        public ComparerMode SortMode { get; private set; }
+        public SortOrder SortOrder { get; private set; }
+
+        public long OldestId { get; set; } = long.MaxValue;
+        public long SinceId { get; set; }
+
+        public abstract MyCommon.TabUsageType TabType { get; }
+
+        public virtual ConcurrentDictionary<long, PostClass> Posts
+            => TabInformations.GetInstance().Posts;
+
+        public int AllCount => this._ids.Count;
+        public long[] StatusIds => this._ids.ToArray();
+
+        public bool IsDefaultTabType => this.TabType.IsDefault();
+        public bool IsDistributableTabType => this.TabType.IsDistributable();
+        public bool IsInnerStorageTabType => this.TabType.IsInnerStorage();
+
+        private IndexedSortedSet<long> _ids = new IndexedSortedSet<long>();
+        private ConcurrentQueue<TemporaryId> addQueue = new ConcurrentQueue<TemporaryId>();
+        private ConcurrentQueue<long> removeQueue = new ConcurrentQueue<long>();
+        private SortedSet<long> unreadIds = new SortedSet<long>();
+
+        private readonly object _lockObj = new object();
+
+        public TabModel(string tabName)
+        {
+            this.TabName = tabName;
+        }
+
+        private struct TemporaryId
+        {
+            public long StatusId { get; }
+            public bool Read { get; }
+
+            public TemporaryId(long statusId, bool read)
+            {
+                this.StatusId = statusId;
+                this.Read = read;
+            }
+        }
+
+        public virtual void AddPostQueue(PostClass post)
+        {
+            this.addQueue.Enqueue(new TemporaryId(post.StatusId, post.IsRead));
+        }
+
+        //無条件に追加
+        internal void AddPostImmediately(long statusId, bool read)
+        {
+            if (this._ids.Contains(statusId)) return;
+
+            this._ids.Add(statusId);
+
+            if (!read)
+                this.unreadIds.Add(statusId);
+        }
+
+        public IList<long> AddSubmit()
+        {
+            var addedIds = new List<long>();
+
+            TemporaryId tId;
+            while (this.addQueue.TryDequeue(out tId))
+            {
+                this.AddPostImmediately(tId.StatusId, tId.Read);
+                addedIds.Add(tId.StatusId);
+            }
+
+            return addedIds;
+        }
+
+        public virtual void EnqueueRemovePost(long statusId, bool setIsDeleted)
+        {
+            this.removeQueue.Enqueue(statusId);
+        }
+
+        public virtual bool RemovePostImmediately(long statusId)
+        {
+            if (!this._ids.Remove(statusId))
+                return false;
+
+            this.unreadIds.Remove(statusId);
+            return true;
+        }
+
+        public IReadOnlyList<long> RemoveSubmit()
+        {
+            var removedIds = new List<long>();
+
+            long statusId;
+            while (this.removeQueue.TryDequeue(out statusId))
+            {
+                if (this.RemovePostImmediately(statusId))
+                    removedIds.Add(statusId);
+            }
+
+            return removedIds;
+        }
+
+        public virtual void ClearIDs()
+        {
+            this._ids.Clear();
+            this.unreadIds.Clear();
+
+            Interlocked.Exchange(ref this.addQueue, new ConcurrentQueue<TemporaryId>());
+        }
+
+        /// <summary>
+        /// タブ更新時に使用する SinceId, OldestId をリセットする
+        /// </summary>
+        public void ResetFetchIds()
+        {
+            this.SinceId = 0L;
+            this.OldestId = long.MaxValue;
+        }
+
+        /// <summary>
+        /// ソート対象のフィールドとソート順を設定し、ソートを実行します
+        /// </summary>
+        public void SetSortMode(ComparerMode mode, SortOrder sortOrder)
+        {
+            this.SortMode = mode;
+            this.SortOrder = sortOrder;
+
+            this.ApplySortMode();
+        }
+
+        private void ApplySortMode()
+        {
+            var sign = this.SortOrder == SortOrder.Ascending ? 1 : -1;
+
+            Comparison<long> comparison;
+            if (this.SortMode == ComparerMode.Id)
+            {
+                comparison = (x, y) => sign * x.CompareTo(y);
+            }
+            else
+            {
+                Comparison<PostClass> postComparison;
+                switch (this.SortMode)
+                {
+                    default:
+                    case ComparerMode.Data:
+                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.TextFromApi, y?.TextFromApi);
+                        break;
+                    case ComparerMode.Name:
+                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.ScreenName, y?.ScreenName);
+                        break;
+                    case ComparerMode.Nickname:
+                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.Nickname, y?.Nickname);
+                        break;
+                    case ComparerMode.Source:
+                        postComparison = (x, y) => Comparer<string>.Default.Compare(x?.Source, y?.Source);
+                        break;
+                }
+
+                comparison = (x, y) =>
+                {
+                    PostClass xPost, yPost;
+                    this.Posts.TryGetValue(x, out xPost);
+                    this.Posts.TryGetValue(y, out yPost);
+
+                    var compare = sign * postComparison(xPost, yPost);
+                    if (compare != 0)
+                        return compare;
+
+                    // 同値であれば status_id で比較する
+                    return sign * x.CompareTo(y);
+                };
+            }
+
+            var comparer = Comparer<long>.Create(comparison);
+
+            this._ids = new IndexedSortedSet<long>(this._ids, comparer);
+            this.unreadIds = new SortedSet<long>(this.unreadIds, comparer);
+        }
+
+        /// <summary>
+        /// 次に表示する未読ツイートのIDを返します。
+        /// ただし、未読がない場合または UnreadManage が false の場合は -1 を返します
+        /// </summary>
+        public long NextUnreadId
+        {
+            get
+            {
+                if (!this.UnreadManage || !SettingCommon.Instance.UnreadManage)
+                    return -1L;
+
+                if (this.unreadIds.Count == 0)
+                    return -1L;
+
+                // unreadIds はリストのインデックス番号順に並んでいるため、
+                // 例えば ID 順の整列であれば昇順なら上から、降順なら下から順に返せば過去→現在の順になる
+                return this.SortOrder == SortOrder.Ascending ? this.unreadIds.Min : this.unreadIds.Max;
+            }
+        }
+
+        /// <summary>
+        /// 次に表示する未読ツイートのインデックス番号を返します。
+        /// ただし、未読がない場合または UnreadManage が false の場合は -1 を返します
+        /// </summary>
+        public int NextUnreadIndex
+        {
+            get
+            {
+                var unreadId = this.NextUnreadId;
+                return unreadId != -1 ? this.IndexOf(unreadId) : -1;
+            }
+        }
+
+        /// <summary>
+        /// 未読ツイートの件数を返します。
+        /// ただし、未読がない場合または UnreadManage が false の場合は 0 を返します
+        /// </summary>
+        public int UnreadCount
+        {
+            get
+            {
+                if (!this.UnreadManage || !SettingCommon.Instance.UnreadManage)
+                    return 0;
+
+                return this.unreadIds.Count;
+            }
+        }
+
+        /// <summary>
+        /// 未読ツイートの ID を配列で返します
+        /// </summary>
+        public long[] GetUnreadIds()
+        {
+            lock (this._lockObj)
+                return this.unreadIds.ToArray();
+        }
+
+        /// <summary>
+        /// タブ内の既読状態を変更します
+        /// </summary>
+        /// <remarks>
+        /// 全タブを横断して既読状態を変える TabInformation.SetReadAllTab() の内部で呼び出されるメソッドです
+        /// </remarks>
+        /// <param name="statusId">変更するツイートのID</param>
+        /// <param name="read">既読状態</param>
+        /// <returns>既読状態に変化があれば true、変化がなければ false</returns>
+        internal bool SetReadState(long statusId, bool read)
+        {
+            if (!this._ids.Contains(statusId))
+                throw new ArgumentOutOfRangeException(nameof(statusId));
+
+            if (this.IsInnerStorageTabType)
+                this.Posts[statusId].IsRead = read;
+
+            if (read)
+                return this.unreadIds.Remove(statusId);
+            else
+                return this.unreadIds.Add(statusId);
+        }
+
+        public bool Contains(long statusId)
+            => this._ids.Contains(statusId);
+
+        public PostClass this[int index]
+        {
+            get
+            {
+                PostClass post;
+                if (!this.Posts.TryGetValue(this.GetStatusIdAt(index), out post))
+                    throw new ArgumentException("Post not exists", nameof(index));
+
+                return post;
+            }
+        }
+
+        public PostClass[] this[int startIndex, int endIndex]
+        {
+            get
+            {
+                var length = endIndex - startIndex + 1;
+                var posts = new PostClass[length];
+
+                var i = 0;
+                foreach (var idx in Enumerable.Range(startIndex, length))
+                {
+                    var statusId = this.GetStatusIdAt(idx);
+                    this.Posts.TryGetValue(statusId, out posts[i++]);
+                }
+
+                return posts;
+            }
+        }
+
+        public long[] GetStatusIdAt(IEnumerable<int> indexes)
+            => indexes.Select(x => this.GetStatusIdAt(x)).ToArray();
+
+        public long GetStatusIdAt(int index)
+            => this._ids[index];
+
+        public int[] IndexOf(long[] statusIds)
+        {
+            if (statusIds == null)
+                throw new ArgumentNullException(nameof(statusIds));
+
+            return statusIds.Select(x => this.IndexOf(x)).ToArray();
+        }
+
+        public int IndexOf(long statusId)
+            => this._ids.IndexOf(statusId);
+
+        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer)
+            => this.SearchPostsAll(stringComparer, reverse: false);
+
+        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, int startIndex)
+            => this.SearchPostsAll(stringComparer, startIndex, reverse: false);
+
+        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, bool reverse)
+        {
+            var startIndex = reverse ? this.AllCount - 1 : 0;
+
+            return this.SearchPostsAll(stringComparer, startIndex, reverse: false);
+        }
+
+        /// <summary>
+        /// タブ内の発言を指定された条件で検索します
+        /// </summary>
+        /// <param name="stringComparer">発言内容、スクリーン名、名前と比較する条件。マッチしたら true を返す</param>
+        /// <param name="startIndex">検索を開始する位置</param>
+        /// <param name="reverse">インデックスの昇順に検索する場合は false、降順の場合は true</param>
+        /// <returns></returns>
+        public IEnumerable<int> SearchPostsAll(Func<string, bool> stringComparer, int startIndex, bool reverse)
+        {
+            if (this.AllCount == 0)
+                yield break;
+
+            var searchIndices = Enumerable.Empty<int>();
+
+            if (!reverse)
+            {
+                // startindex ...末尾
+                if (startIndex != this.AllCount - 1)
+                    searchIndices = MyCommon.CountUp(startIndex, this.AllCount - 1);
+
+                // 先頭 ... (startIndex - 1)
+                if (startIndex != 0)
+                    searchIndices = searchIndices.Concat(MyCommon.CountUp(0, startIndex - 1));
+            }
+            else
+            {
+                // startIndex ... 先頭
+                if (startIndex != 0)
+                    searchIndices = MyCommon.CountDown(startIndex, 0);
+
+                // 末尾 ... (startIndex - 1)
+                if (startIndex != this.AllCount - 1)
+                    searchIndices = searchIndices.Concat(MyCommon.CountDown(this.AllCount - 1, startIndex - 1));
+            }
+
+            foreach (var index in searchIndices)
+            {
+                PostClass post;
+                if (!this.Posts.TryGetValue(this.GetStatusIdAt(index), out post))
+                    continue;
+
+                if (stringComparer(post.Nickname) || stringComparer(post.TextFromApi) || stringComparer(post.ScreenName))
+                {
+                    yield return index;
+                }
+            }
+        }
+    }
+}
diff --git a/OpenTween/Models/UserTimelineTabModel.cs b/OpenTween/Models/UserTimelineTabModel.cs
new file mode 100644 (file)
index 0000000..6dafdea
--- /dev/null
@@ -0,0 +1,49 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+//           (c) 2008-2011 Moz (@syo68k)
+//           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+//           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+//           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+//           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+//           (c) 2012      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace OpenTween.Models
+{
+    public class UserTimelineTabModel : InternalStorageTabModel
+    {
+        public override MyCommon.TabUsageType TabType
+            => MyCommon.TabUsageType.UserTimeline;
+
+        public string ScreenName { get; }
+
+        public UserTimelineTabModel(string tabName, string screenName)
+            : base(tabName)
+        {
+            this.ScreenName = screenName;
+        }
+    }
+}
index 7eadb94..db0e390 100644 (file)
@@ -69,15 +69,12 @@ namespace OpenTween
                 List<PostClass> listPost = new List<PostClass>();
                 List<PostClass> otherPost = new List<PostClass>();
 
-                foreach (TabClass tab in TabInformations.GetInstance().Tabs.Values)
+                foreach (var tab in TabInformations.GetInstance().GetTabsByType<ListTimelineTabModel>())
                 {
-                    if (tab.TabType == MyCommon.TabUsageType.Lists)
-                    {
-                        if (listItem.Id == tab.ListInfo.Id)
-                            listPost.AddRange(tab.Posts.Values);
-                        else
-                            otherPost.AddRange(tab.Posts.Values);
-                    }
+                    if (listItem.Id == tab.ListInfo.Id)
+                        listPost.AddRange(tab.Posts.Values);
+                    else
+                        otherPost.AddRange(tab.Posts.Values);
                 }
 
                 //リストが空の場合は推定不能
index 5bf5602..cf78750 100644 (file)
     </Compile>
     <Compile Include="IndexedSortedSet.cs" />
     <Compile Include="MediaItem.cs" />
+    <Compile Include="Models\ComparerMode.cs" />
+    <Compile Include="Models\DirectMessagesTabModel.cs" />
+    <Compile Include="Models\FavoritesTabModel.cs" />
+    <Compile Include="Models\FilterTabModel.cs" />
+    <Compile Include="Models\HomeTabModel.cs" />
+    <Compile Include="Models\InternalStorageTabModel.cs" />
+    <Compile Include="Models\ListTimelineTabModel.cs" />
+    <Compile Include="Models\LocalSearchTabModel.cs" />
     <Compile Include="Models\MediaInfo.cs" />
+    <Compile Include="Models\MentionsTabModel.cs" />
+    <Compile Include="Models\MuteTabModel.cs" />
     <Compile Include="Models\PostClass.cs" />
-    <Compile Include="Models\TabClass.cs" />
+    <Compile Include="Models\PublicSearchTabModel.cs" />
+    <Compile Include="Models\RelatedPostsTabModel.cs" />
+    <Compile Include="Models\TabModel.cs" />
     <Compile Include="Models\TabInformations.cs" />
+    <Compile Include="Models\TabUsageTypeExt.cs" />
+    <Compile Include="Models\UserTimelineTabModel.cs" />
     <Compile Include="MouseWheelMessageFilter.cs" />
     <Compile Include="NotifyPropertyChangedBase.cs" />
     <Compile Include="SendErrorReportForm.cs">
index 8b0683a..b0216d5 100644 (file)
@@ -44,7 +44,7 @@ namespace OpenTween
 
         protected internal class TabListItem
         {
-            public TabClass Tab { get; set; }
+            public TabModel Tab { get; set; }
             public string Label { get; set; }
 
             public override string ToString()
@@ -108,7 +108,7 @@ namespace OpenTween
                 this.OK_Button.Enabled = true;
         }
 
-        public TabClass SelectedTab
+        public TabModel SelectedTab
         {
             get
             {
@@ -119,7 +119,7 @@ namespace OpenTween
             }
         }
 
-        public TabClass[] SelectedTabs
+        public TabModel[] SelectedTabs
         {
             get
             {
index 631219d..238cdf9 100644 (file)
@@ -1055,66 +1055,20 @@ namespace OpenTween
             SetTabAlignment();
 
             //デフォルトタブの存在チェック、ない場合には追加
-            if (_statuses.GetTabByType(MyCommon.TabUsageType.Home) == null)
-            {
-                TabClass tab;
-                if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.RECENT, out tab))
-                {
-                    _statuses.AddTab(MyCommon.DEFAULTTAB.RECENT, MyCommon.TabUsageType.Home, null);
-                }
-                else
-                {
-                    tab.TabType = MyCommon.TabUsageType.Home;
-                }
-            }
-            if (_statuses.GetTabByType(MyCommon.TabUsageType.Mentions) == null)
-            {
-                TabClass tab;
-                if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.REPLY, out tab))
-                {
-                    _statuses.AddTab(MyCommon.DEFAULTTAB.REPLY, MyCommon.TabUsageType.Mentions, null);
-                }
-                else
-                {
-                    tab.TabType = MyCommon.TabUsageType.Mentions;
-                }
-            }
-            if (_statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage) == null)
-            {
-                TabClass tab;
-                if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.DM, out tab))
-                {
-                    _statuses.AddTab(MyCommon.DEFAULTTAB.DM, MyCommon.TabUsageType.DirectMessage, null);
-                }
-                else
-                {
-                    tab.TabType = MyCommon.TabUsageType.DirectMessage;
-                }
-            }
-            if (_statuses.GetTabByType(MyCommon.TabUsageType.Favorites) == null)
-            {
-                TabClass tab;
-                if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.FAV, out tab))
-                {
-                    _statuses.AddTab(MyCommon.DEFAULTTAB.FAV, MyCommon.TabUsageType.Favorites, null);
-                }
-                else
-                {
-                    tab.TabType = MyCommon.TabUsageType.Favorites;
-                }
-            }
-            if (_statuses.GetTabByType(MyCommon.TabUsageType.Mute) == null)
-            {
-                TabClass tab;
-                if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.MUTE, out tab))
-                {
-                    _statuses.AddTab(MyCommon.DEFAULTTAB.MUTE, MyCommon.TabUsageType.Mute, null);
-                }
-                else
-                {
-                    tab.TabType = MyCommon.TabUsageType.Mute;
-                }
-            }
+            if (this._statuses.GetTabByType<HomeTabModel>() == null)
+                this._statuses.AddTab(new HomeTabModel());
+
+            if (this._statuses.GetTabByType<MentionsTabModel>() == null)
+                this._statuses.AddTab(new MentionsTabModel());
+
+            if (this._statuses.GetTabByType<DirectMessagesTabModel>() == null)
+                this._statuses.AddTab(new DirectMessagesTabModel());
+
+            if (this._statuses.GetTabByType<FavoritesTabModel>() == null)
+                this._statuses.AddTab(new FavoritesTabModel());
+
+            if (this._statuses.GetTabByType<MuteTabModel>() == null)
+                this._statuses.AddTab(new MuteTabModel());
 
             foreach (var tab in _statuses.Tabs.Values)
             {
@@ -1122,11 +1076,7 @@ namespace OpenTween
                 if (tab.TabType == MyCommon.TabUsageType.Mute)
                     continue;
 
-                if (tab.TabType == MyCommon.TabUsageType.Undefined)
-                {
-                    tab.TabType = MyCommon.TabUsageType.UserDefined;
-                }
-                if (!AddNewTab(tab.TabName, true, tab.TabType, tab.ListInfo))
+                if (!AddNewTab(tab, startup: true))
                     throw new TabException(Properties.Resources.TweenMain_LoadText1);
             }
 
@@ -1286,18 +1236,55 @@ namespace OpenTween
             var tabsSetting = SettingTabs.Load().Tabs;
             foreach (var tabSetting in tabsSetting)
             {
-                var tab = new TabClass(tabSetting.TabName, tabSetting.TabType, tabSetting.ListInfo)
-                {
-                    UnreadManage = tabSetting.UnreadManage,
-                    Protected = tabSetting.Protected,
-                    Notify = tabSetting.Notify,
-                    SoundFile = tabSetting.SoundFile,
-                    FilterArray = tabSetting.FilterArray,
-                    FilterModified = false,
-                    User = tabSetting.User,
-                    SearchWords = tabSetting.SearchWords,
-                    SearchLang = tabSetting.SearchLang,
-                };
+                TabModel tab;
+                switch (tabSetting.TabType)
+                {
+                    case MyCommon.TabUsageType.Home:
+                        tab = new HomeTabModel(tabSetting.TabName);
+                        break;
+                    case MyCommon.TabUsageType.Mentions:
+                        tab = new MentionsTabModel(tabSetting.TabName);
+                        break;
+                    case MyCommon.TabUsageType.DirectMessage:
+                        tab = new DirectMessagesTabModel(tabSetting.TabName);
+                        break;
+                    case MyCommon.TabUsageType.Favorites:
+                        tab = new FavoritesTabModel(tabSetting.TabName);
+                        break;
+                    case MyCommon.TabUsageType.UserDefined:
+                        tab = new FilterTabModel(tabSetting.TabName);
+                        break;
+                    case MyCommon.TabUsageType.UserTimeline:
+                        tab = new UserTimelineTabModel(tabSetting.TabName, tabSetting.User);
+                        break;
+                    case MyCommon.TabUsageType.PublicSearch:
+                        tab = new PublicSearchTabModel(tabSetting.TabName)
+                        {
+                            SearchWords = tabSetting.SearchWords,
+                            SearchLang = tabSetting.SearchLang,
+                        };
+                        break;
+                    case MyCommon.TabUsageType.Lists:
+                        tab = new ListTimelineTabModel(tabSetting.TabName, tabSetting.ListInfo);
+                        break;
+                    case MyCommon.TabUsageType.Mute:
+                        tab = new MuteTabModel(tabSetting.TabName);
+                        break;
+                    default:
+                        continue;
+                }
+
+                tab.UnreadManage = tabSetting.UnreadManage;
+                tab.Protected = tabSetting.Protected;
+                tab.Notify = tabSetting.Notify;
+                tab.SoundFile = tabSetting.SoundFile;
+
+                if (tab.IsDistributableTabType)
+                {
+                    var filterTab = (FilterTabModel)tab;
+                    filterTab.FilterArray = tabSetting.FilterArray;
+                    filterTab.FilterModified = false;
+                }
 
                 if (this._statuses.ContainsTab(tab.TabName))
                     tab.TabName = this._statuses.MakeTabName("MyTab");
@@ -1306,10 +1293,10 @@ namespace OpenTween
             }
             if (_statuses.Tabs.Count == 0)
             {
-                _statuses.AddTab(MyCommon.DEFAULTTAB.RECENT, MyCommon.TabUsageType.Home, null);
-                _statuses.AddTab(MyCommon.DEFAULTTAB.REPLY, MyCommon.TabUsageType.Mentions, null);
-                _statuses.AddTab(MyCommon.DEFAULTTAB.DM, MyCommon.TabUsageType.DirectMessage, null);
-                _statuses.AddTab(MyCommon.DEFAULTTAB.FAV, MyCommon.TabUsageType.Favorites, null);
+                _statuses.AddTab(new HomeTabModel());
+                _statuses.AddTab(new MentionsTabModel());
+                _statuses.AddTab(new DirectMessagesTabModel());
+                _statuses.AddTab(new FavoritesTabModel());
             }
         }
 
@@ -1452,7 +1439,7 @@ namespace OpenTween
                 foreach (TabPage tab in ListTab.TabPages)
                 {
                     DetailsListView lst = (DetailsListView)tab.Tag;
-                    TabClass tabInfo = _statuses.Tabs[tab.Text];
+                    TabModel tabInfo = _statuses.Tabs[tab.Text];
                     if (isDelete || lst.VirtualListSize != tabInfo.AllCount)
                     {
                         using (ControlTransaction.Update(lst))
@@ -1523,7 +1510,7 @@ namespace OpenTween
         /// <summary>
         /// <see cref="ListView"/> のスクロール位置に関する情報を <see cref="ListViewScroll"/> として返します
         /// </summary>
-        private ListViewScroll SaveListViewScroll(DetailsListView listView, TabClass tab)
+        private ListViewScroll SaveListViewScroll(DetailsListView listView, TabModel tab)
         {
             var listScroll = new ListViewScroll
             {
@@ -1534,7 +1521,7 @@ namespace OpenTween
             {
                 var topItem = listView.TopItem;
                 if (topItem != null)
-                    listScroll.TopItemStatusId = tab.GetId(topItem.Index);
+                    listScroll.TopItemStatusId = tab.GetStatusIdAt(topItem.Index);
             }
 
             return listScroll;
@@ -1624,33 +1611,33 @@ namespace OpenTween
             return listsDict;
         }
 
-        private long[] GetSelectedStatusIds(DetailsListView listView, TabClass tab)
+        private long[] GetSelectedStatusIds(DetailsListView listView, TabModel tab)
         {
             var selectedIndices = listView.SelectedIndices;
             if (selectedIndices.Count > 0 && selectedIndices.Count < 61)
-                return tab.GetId(selectedIndices);
+                return tab.GetStatusIdAt(selectedIndices.Cast<int>());
             else
                 return null;
         }
 
-        private long? GetFocusedStatusId(DetailsListView listView, TabClass tab)
+        private long? GetFocusedStatusId(DetailsListView listView, TabModel tab)
         {
             var focusedItem = listView.FocusedItem;
 
-            return focusedItem != null ? tab.GetId(focusedItem.Index) : (long?)null;
+            return focusedItem != null ? tab.GetStatusIdAt(focusedItem.Index) : (long?)null;
         }
 
-        private long? GetSelectionMarkStatusId(DetailsListView listView, TabClass tab)
+        private long? GetSelectionMarkStatusId(DetailsListView listView, TabModel tab)
         {
             var selectionMarkIndex = listView.SelectionMark;
 
-            return selectionMarkIndex != -1 ? tab.GetId(selectionMarkIndex) : (long?)null;
+            return selectionMarkIndex != -1 ? tab.GetStatusIdAt(selectionMarkIndex) : (long?)null;
         }
 
         /// <summary>
         /// <see cref="SaveListViewScroll"/> によって保存されたスクロール位置を復元します
         /// </summary>
-        private void RestoreListViewScroll(DetailsListView listView, TabClass tab, ListViewScroll listScroll)
+        private void RestoreListViewScroll(DetailsListView listView, TabModel tab, ListViewScroll listScroll)
         {
             if (listView.VirtualListSize == 0)
                 return;
@@ -1677,7 +1664,7 @@ namespace OpenTween
         /// <summary>
         /// <see cref="SaveListViewStatus"/> によって保存された選択状態を復元します
         /// </summary>
-        private void RestoreListViewSelection(DetailsListView listView, TabClass tab, ListViewSelection listSelection)
+        private void RestoreListViewSelection(DetailsListView listView, TabModel tab, ListViewSelection listSelection)
         {
             // status_id から ListView 上のインデックスに変換
             int[] selectedIndices = null;
@@ -2516,22 +2503,22 @@ namespace OpenTween
 
         private Task GetPublicSearchAllAsync()
         {
-            var tabs = this._statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch);
+            var tabs = this._statuses.GetTabsByType<PublicSearchTabModel>();
 
             return this.GetPublicSearchAsync(tabs, loadMore: false);
         }
 
-        private Task GetPublicSearchAsync(TabClass tab)
+        private Task GetPublicSearchAsync(PublicSearchTabModel tab)
         {
             return this.GetPublicSearchAsync(tab, loadMore: false);
         }
 
-        private Task GetPublicSearchAsync(TabClass tab, bool loadMore)
+        private Task GetPublicSearchAsync(PublicSearchTabModel tab, bool loadMore)
         {
             return this.GetPublicSearchAsync(new[] { tab }, loadMore);
         }
 
-        private async Task GetPublicSearchAsync(IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetPublicSearchAsync(IEnumerable<PublicSearchTabModel> tabs, bool loadMore)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2552,7 +2539,7 @@ namespace OpenTween
             }
         }
 
-        private async Task GetPublicSearchAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetPublicSearchAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<PublicSearchTabModel> tabs, bool loadMore)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -2608,22 +2595,22 @@ namespace OpenTween
 
         private Task GetUserTimelineAllAsync()
         {
-            var tabs = this._statuses.GetTabsByType(MyCommon.TabUsageType.UserTimeline);
+            var tabs = this._statuses.GetTabsByType<UserTimelineTabModel>();
 
             return this.GetUserTimelineAsync(tabs, loadMore: false);
         }
 
-        private Task GetUserTimelineAsync(TabClass tab)
+        private Task GetUserTimelineAsync(UserTimelineTabModel tab)
         {
             return this.GetUserTimelineAsync(tab, loadMore: false);
         }
 
-        private Task GetUserTimelineAsync(TabClass tab, bool loadMore)
+        private Task GetUserTimelineAsync(UserTimelineTabModel tab, bool loadMore)
         {
             return this.GetUserTimelineAsync(new[] { tab }, loadMore);
         }
 
-        private async Task GetUserTimelineAsync(IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetUserTimelineAsync(IEnumerable<UserTimelineTabModel> tabs, bool loadMore)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2644,7 +2631,7 @@ namespace OpenTween
             }
         }
 
-        private async Task GetUserTimelineAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetUserTimelineAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<UserTimelineTabModel> tabs, bool loadMore)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -2668,10 +2655,10 @@ namespace OpenTween
                 {
                     try
                     {
-                        if (string.IsNullOrEmpty(tab.User))
+                        if (string.IsNullOrEmpty(tab.ScreenName))
                             continue;
 
-                        await this.tw.GetUserTimelineApi(read, tab.User, tab, loadMore)
+                        await this.tw.GetUserTimelineApi(read, tab.ScreenName, tab, loadMore)
                             .ConfigureAwait(false);
                     }
                     catch (WebApiException ex)
@@ -2696,22 +2683,22 @@ namespace OpenTween
 
         private Task GetListTimelineAllAsync()
         {
-            var tabs = this._statuses.GetTabsByType(MyCommon.TabUsageType.Lists);
+            var tabs = this._statuses.GetTabsByType<ListTimelineTabModel>();
 
             return this.GetListTimelineAsync(tabs, loadMore: false);
         }
 
-        private Task GetListTimelineAsync(TabClass tab)
+        private Task GetListTimelineAsync(ListTimelineTabModel tab)
         {
             return this.GetListTimelineAsync(tab, loadMore: false);
         }
 
-        private Task GetListTimelineAsync(TabClass tab, bool loadMore)
+        private Task GetListTimelineAsync(ListTimelineTabModel tab, bool loadMore)
         {
             return this.GetListTimelineAsync(new[] { tab }, loadMore);
         }
 
-        private async Task GetListTimelineAsync(IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetListTimelineAsync(IEnumerable<ListTimelineTabModel> tabs, bool loadMore)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2732,7 +2719,7 @@ namespace OpenTween
             }
         }
 
-        private async Task GetListTimelineAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<TabClass> tabs, bool loadMore)
+        private async Task GetListTimelineAsyncInternal(IProgress<string> p, CancellationToken ct, IEnumerable<ListTimelineTabModel> tabs, bool loadMore)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -2782,7 +2769,7 @@ namespace OpenTween
             this.RefreshTimeline();
         }
 
-        private async Task GetRelatedTweetsAsync(TabClass tab)
+        private async Task GetRelatedTweetsAsync(RelatedPostsTabModel tab)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2803,7 +2790,7 @@ namespace OpenTween
             }
         }
 
-        private async Task GetRelatedTweetsAsyncInternal(IProgress<string> p, CancellationToken ct, TabClass tab)
+        private async Task GetRelatedTweetsAsyncInternal(IProgress<string> p, CancellationToken ct, RelatedPostsTabModel tab)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -2842,7 +2829,7 @@ namespace OpenTween
                 // TODO: 非同期更新中にタブが閉じられている場合を厳密に考慮したい
 
                 var listView = (DetailsListView)tabPage.Tag;
-                var index = tab.IndexOf(tab.RelationTargetPost.RetweetedId ?? tab.RelationTargetPost.StatusId);
+                var index = tab.IndexOf(tab.TargetPost.RetweetedId ?? tab.TargetPost.StatusId);
 
                 if (index != -1 && index < listView.Items.Count)
                 {
@@ -2852,7 +2839,7 @@ namespace OpenTween
             }
         }
 
-        private async Task FavAddAsync(long statusId, TabClass tab)
+        private async Task FavAddAsync(long statusId, TabModel tab)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2873,7 +2860,7 @@ namespace OpenTween
             }
         }
 
-        private async Task FavAddAsyncInternal(IProgress<string> p, CancellationToken ct, long statusId, TabClass tab)
+        private async Task FavAddAsyncInternal(IProgress<string> p, CancellationToken ct, long statusId, TabModel tab)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -2916,7 +2903,7 @@ namespace OpenTween
                         postTl.IsFav = true;
 
                         var favTab = this._statuses.GetTabByType(MyCommon.TabUsageType.Favorites);
-                        favTab.AddPostQueue(statusId, postTl.IsRead);
+                        favTab.AddPostQueue(postTl);
                     }
 
                     // 検索,リスト,UserTimeline,Relatedの各タブに反映
@@ -2964,7 +2951,7 @@ namespace OpenTween
             }
         }
 
-        private async Task FavRemoveAsync(IReadOnlyList<long> statusIds, TabClass tab)
+        private async Task FavRemoveAsync(IReadOnlyList<long> statusIds, TabModel tab)
         {
             await this.workerSemaphore.WaitAsync();
 
@@ -2985,7 +2972,7 @@ namespace OpenTween
             }
         }
 
-        private async Task FavRemoveAsyncInternal(IProgress<string> p, CancellationToken ct, IReadOnlyList<long> statusIds, TabClass tab)
+        private async Task FavRemoveAsyncInternal(IProgress<string> p, CancellationToken ct, IReadOnlyList<long> statusIds, TabModel tab)
         {
             if (ct.IsCancellationRequested)
                 return;
@@ -3450,7 +3437,7 @@ namespace OpenTween
 
         private async Task FavoriteChange(bool FavAdd, bool multiFavoriteChangeDialogEnable = true)
         {
-            TabClass tab;
+            TabModel tab;
             if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out tab))
                 return;
 
@@ -3984,7 +3971,7 @@ namespace OpenTween
         {
             if (_curTab != null)
             {
-                TabClass tab;
+                TabModel tab;
                 if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out tab))
                     return;
 
@@ -4002,17 +3989,17 @@ namespace OpenTween
                     //case MyCommon.TabUsageType.Profile:
                         //// TODO
                     case MyCommon.TabUsageType.PublicSearch:
-                        //// TODO
-                        if (string.IsNullOrEmpty(tab.SearchWords)) return;
-                        await this.GetPublicSearchAsync(tab);
+                        var searchTab = (PublicSearchTabModel)tab;
+                        if (string.IsNullOrEmpty(searchTab.SearchWords)) return;
+                        await this.GetPublicSearchAsync(searchTab);
                         break;
                     case MyCommon.TabUsageType.UserTimeline:
-                        await this.GetUserTimelineAsync(tab);
+                        await this.GetUserTimelineAsync((UserTimelineTabModel)tab);
                         break;
                     case MyCommon.TabUsageType.Lists:
-                        //// TODO
-                        if (tab.ListInfo == null || tab.ListInfo.Id == 0) return;
-                        await this.GetListTimelineAsync(tab);
+                        var listTab = (ListTimelineTabModel)tab;
+                        if (listTab.ListInfo == null || listTab.ListInfo.Id == 0) return;
+                        await this.GetListTimelineAsync(listTab);
                         break;
                     default:
                         await this.GetHomeTimelineAsync();
@@ -4030,7 +4017,7 @@ namespace OpenTween
             //ページ指定をマイナス1に
             if (_curTab != null)
             {
-                TabClass tab;
+                TabModel tab;
                 if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out tab))
                     return;
 
@@ -4049,17 +4036,17 @@ namespace OpenTween
                         //// TODO
                         break;
                     case MyCommon.TabUsageType.PublicSearch:
-                        // TODO
-                        if (string.IsNullOrEmpty(tab.SearchWords)) return;
-                        await this.GetPublicSearchAsync(tab, loadMore: true);
+                        var searchTab = (PublicSearchTabModel)tab;
+                        if (string.IsNullOrEmpty(searchTab.SearchWords)) return;
+                        await this.GetPublicSearchAsync(searchTab, loadMore: true);
                         break;
                     case MyCommon.TabUsageType.UserTimeline:
-                        await this.GetUserTimelineAsync(tab, loadMore: true);
+                        await this.GetUserTimelineAsync((UserTimelineTabModel)tab, loadMore: true);
                         break;
                     case MyCommon.TabUsageType.Lists:
-                        //// TODO
-                        if (tab.ListInfo == null || tab.ListInfo.Id == 0) return;
-                        await this.GetListTimelineAsync(tab, loadMore: true);
+                        var listTab = (ListTimelineTabModel)tab;
+                        if (listTab.ListInfo == null || listTab.ListInfo.Id == 0) return;
+                        await this.GetListTimelineAsync(listTab, loadMore: true);
                         break;
                     default:
                         await this.GetHomeTimelineAsync(loadMore: true);
@@ -4368,7 +4355,7 @@ namespace OpenTween
             foreach (TabPage tab in ListTab.TabPages)
             {
                 DetailsListView lst = (DetailsListView)tab.Tag;
-                TabClass tabInfo = _statuses.Tabs[tab.Text];
+                TabModel tabInfo = _statuses.Tabs[tab.Text];
                 using (ControlTransaction.Update(lst))
                 {
                     // 選択位置などを復元
@@ -4465,7 +4452,7 @@ namespace OpenTween
         public void AddNewTabForSearch(string searchWord)
         {
             //同一検索条件のタブが既に存在すれば、そのタブアクティブにして終了
-            foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch))
+            foreach (var tb in _statuses.GetTabsByType<PublicSearchTabModel>())
             {
                 if (tb.SearchWords == searchWord && string.IsNullOrEmpty(tb.SearchLang))
                 {
@@ -4489,8 +4476,9 @@ namespace OpenTween
                     break;
             }
             //タブ追加
-            _statuses.AddTab(tabName, MyCommon.TabUsageType.PublicSearch, null);
-            AddNewTab(tabName, false, MyCommon.TabUsageType.PublicSearch);
+            var tab = new PublicSearchTabModel(tabName);
+            _statuses.AddTab(tab);
+            AddNewTab(tab, startup: false);
             //追加したタブをアクティブに
             ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
             //検索条件の設定
@@ -4522,9 +4510,9 @@ namespace OpenTween
         public void AddNewTabForUserTimeline(string user)
         {
             //同一検索条件のタブが既に存在すれば、そのタブアクティブにして終了
-            foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.UserTimeline))
+            foreach (var tb in _statuses.GetTabsByType<UserTimelineTabModel>())
             {
-                if (tb.User == user)
+                if (tb.ScreenName == user)
                 {
                     foreach (TabPage tp in ListTab.TabPages)
                     {
@@ -4543,10 +4531,9 @@ namespace OpenTween
                 tabName += "_";
             }
             //タブ追加
-            var tab = new TabClass(tabName, MyCommon.TabUsageType.UserTimeline);
+            var tab = new UserTimelineTabModel(tabName, user);
             this._statuses.AddTab(tab);
-            tab.User = user;
-            AddNewTab(tabName, false, MyCommon.TabUsageType.UserTimeline);
+            this.AddNewTab(tab, startup: false);
             //追加したタブをアクティブに
             ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
             SaveConfigsTabs();
@@ -4554,27 +4541,23 @@ namespace OpenTween
             this.GetUserTimelineAsync(tab);
         }
 
-        public bool AddNewTab(string tabName, bool startup, MyCommon.TabUsageType tabType, ListElement listInfo = null)
+        public bool AddNewTab(TabModel tab, bool startup)
         {
             //重複チェック
             foreach (TabPage tb in ListTab.TabPages)
             {
-                if (tb.Text == tabName) return false;
+                if (tb.Text == tab.TabName) return false;
             }
 
             //新規タブ名チェック
-            if (tabName == Properties.Resources.AddNewTabText1) return false;
+            if (tab.TabName == Properties.Resources.AddNewTabText1) return false;
 
             //タブタイプ重複チェック
             if (!startup)
             {
-                if (tabType == MyCommon.TabUsageType.DirectMessage ||
-                   tabType == MyCommon.TabUsageType.Favorites ||
-                   tabType == MyCommon.TabUsageType.Home ||
-                   tabType == MyCommon.TabUsageType.Mentions ||
-                   tabType == MyCommon.TabUsageType.Related)
+                if (tab.IsDefaultTabType || tab.TabType == MyCommon.TabUsageType.Related)
                 {
-                    if (_statuses.GetTabByType(tabType) != null) return false;
+                    if (_statuses.GetTabByType(tab.TabType) != null) return false;
                 }
             }
 
@@ -4596,19 +4579,24 @@ namespace OpenTween
                 _tabPage.Controls.Add(_listCustom);
 
                 /// UserTimeline関連
-                if (tabType == MyCommon.TabUsageType.UserTimeline || tabType == MyCommon.TabUsageType.Lists)
+                var userTab = tab as UserTimelineTabModel;
+                var listTab = tab as ListTimelineTabModel;
+                var searchTab = tab as PublicSearchTabModel;
+
+                if (userTab != null || listTab != null)
                 {
                     var label = new Label();
                     label.Dock = DockStyle.Top;
                     label.Name = "labelUser";
                     label.TabIndex = 0;
-                    if (tabType == MyCommon.TabUsageType.Lists)
+
+                    if (listTab != null)
                     {
-                        label.Text = listInfo.ToString();
+                        label.Text = listTab.ListInfo.ToString();
                     }
-                    else
+                    else if (userTab != null)
                     {
-                        label.Text = _statuses.Tabs[tabName].User + "'s Timeline";
+                        label.Text = userTab.ScreenName + "'s Timeline";
                     }
                     label.TextAlign = ContentAlignment.MiddleLeft;
                     using (ComboBox tmpComboBox = new ComboBox())
@@ -4618,7 +4606,7 @@ namespace OpenTween
                     _tabPage.Controls.Add(label);
                 }
                 /// 検索関連の準備
-                else if (tabType == MyCommon.TabUsageType.PublicSearch)
+                else if (searchTab != null)
                 {
                     var pnl = new Panel();
 
@@ -4696,18 +4684,14 @@ namespace OpenTween
                         btn.TabIndex = 3;
                         btn.Click += SearchButton_Click;
 
-                        TabClass tab;
-                        if (_statuses.Tabs.TryGetValue(tabName, out tab))
+                        if (!string.IsNullOrEmpty(searchTab.SearchWords))
                         {
-                            if (!string.IsNullOrEmpty(tab.SearchWords))
-                            {
-                                cmb.Items.Add(tab.SearchWords);
-                                cmb.Text = tab.SearchWords;
-                            }
-
-                            cmbLang.Text = tab.SearchLang;
+                            cmb.Items.Add(searchTab.SearchWords);
+                            cmb.Text = searchTab.SearchWords;
                         }
 
+                        cmbLang.Text = searchTab.SearchLang;
+
                         _tabPage.Controls.Add(pnl);
                     }
                 }
@@ -4719,7 +4703,7 @@ namespace OpenTween
                 _tabPage.Name = "CTab" + cnt.ToString();
                 _tabPage.Size = new Size(380, 260);
                 _tabPage.TabIndex = 2 + cnt;
-                _tabPage.Text = tabName;
+                _tabPage.Text = tab.TabName;
                 _tabPage.UseVisualStyleBackColor = true;
                 _tabPage.AccessibleRole = AccessibleRole.PageTab;
 
@@ -5811,8 +5795,8 @@ namespace OpenTween
                         MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                     }
 
-                    var resultTab = new TabClass(tabName, MyCommon.TabUsageType.SearchResults);
-                    this.AddNewTab(tabName, false, MyCommon.TabUsageType.SearchResults);
+                    var resultTab = new LocalSearchTabModel(tabName);
+                    this.AddNewTab(resultTab, startup: false);
                     this._statuses.AddTab(resultTab);
 
                     var targetTab = this._statuses.Tabs[this._curTab.Text];
@@ -5838,7 +5822,7 @@ namespace OpenTween
                     var foundPosts = foundIndices.Select(x => targetTab[x]);
                     foreach (var post in foundPosts)
                     {
-                        resultTab.AddPostToInnerStorage(post);
+                        resultTab.AddPostQueue(post);
                     }
 
                     this._statuses.DistributePosts();
@@ -7477,7 +7461,7 @@ namespace OpenTween
         {
             if (_curPost == null) return;
 
-            TabClass curTabClass = _statuses.Tabs[_curTab.Text];
+            TabModel curTabClass = _statuses.Tabs[_curTab.Text];
 
             if (curTabClass.TabType == MyCommon.TabUsageType.PublicSearch && _curPost.InReplyToStatusId == null && _curPost.TextFromApi.Contains("@"))
             {
@@ -7569,7 +7553,7 @@ namespace OpenTween
         {
             if (_curPost == null) return;
 
-            TabClass curTabClass = _statuses.Tabs[_curTab.Text];
+            TabModel curTabClass = _statuses.Tabs[_curTab.Text];
             //Dictionary<long, PostClass> curTabPosts = curTabClass.Posts;
 
             if (parallel)
@@ -7978,12 +7962,27 @@ namespace OpenTween
                     Protected = tab.Protected,
                     Notify = tab.Notify,
                     SoundFile = tab.SoundFile,
-                    FilterArray = tab.FilterArray,
-                    User = tab.User,
-                    SearchWords = tab.SearchWords,
-                    SearchLang = tab.SearchLang,
-                    ListInfo = tab.ListInfo,
                 };
+
+                var filterTab = tab as FilterTabModel;
+                if (filterTab != null)
+                    tabSetting.FilterArray = filterTab.FilterArray;
+
+                var userTab = tab as UserTimelineTabModel;
+                if (userTab != null)
+                    tabSetting.User = userTab.ScreenName;
+
+                var searchTab = tab as PublicSearchTabModel;
+                if (searchTab != null)
+                {
+                    tabSetting.SearchWords = searchTab.SearchWords;
+                    tabSetting.SearchLang = searchTab.SearchLang;
+                }
+
+                var listTab = tab as ListTimelineTabModel;
+                if (listTab != null)
+                    tabSetting.ListInfo = listTab.ListInfo;
+
                 tabsSetting.Tabs.Add(tabSetting);
             }
 
@@ -8610,7 +8609,7 @@ namespace OpenTween
                 return;
             }
 
-            TabClass tb = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
+            TabModel tb = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
             if (this._cfgCommon.ReplyIconState != MyCommon.REPLY_ICONSTATE.None && tb != null && tb.UnreadCount > 0)
             {
                 if (blinkCnt > 0) return;
@@ -8666,7 +8665,7 @@ namespace OpenTween
             if (_statuses == null) return;
             if (_statuses.Tabs == null) return;
 
-            TabClass tb = _statuses.Tabs[_rclickTabName];
+            TabModel tb = _statuses.Tabs[_rclickTabName];
             if (tb == null) return;
 
             NotifyDispMenuItem.Checked = tb.Notify;
@@ -8859,7 +8858,24 @@ namespace OpenTween
                         list = listAvail.SelectedList;
                     }
                 }
-                if (!_statuses.AddTab(tabName, tabUsage, list) || !AddNewTab(tabName, false, tabUsage, list))
+
+                TabModel tab;
+                switch (tabUsage)
+                {
+                    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 (!_statuses.AddTab(tab) || !AddNewTab(tab, startup: false))
                 {
                     string tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
                     MessageBox.Show(tmp, Properties.Resources.AddTabMenuItem_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
@@ -8876,8 +8892,8 @@ namespace OpenTween
                     if (tabUsage == MyCommon.TabUsageType.Lists)
                     {
                         ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
-                        var tab = this._statuses.Tabs[this._curTab.Text];
-                        this.GetListTimelineAsync(tab);
+                        var listTab = (ListTimelineTabModel)this._statuses.Tabs[this._curTab.Text];
+                        this.GetListTimelineAsync(listTab);
                     }
                 }
             }
@@ -9018,7 +9034,7 @@ namespace OpenTween
             //タブ選択(or追加)
             if (!SelectTab(out tabName)) return;
 
-            var tab = this._statuses.Tabs[tabName];
+            var tab = (FilterTabModel)this._statuses.Tabs[tabName];
 
             bool mv;
             bool mk;
@@ -9084,7 +9100,7 @@ namespace OpenTween
                 return;
 
             var currentTab = this._statuses.Tabs[this._curTab.Text];
-            var filterTab = this._statuses.Tabs[tabName];
+            var filterTab = (FilterTabModel)this._statuses.Tabs[tabName];
 
             bool mv;
             bool mk;
@@ -9155,7 +9171,8 @@ namespace OpenTween
                     this.TopMost = this._cfgCommon.AlwaysTop;
                     if (!string.IsNullOrEmpty(tabName))
                     {
-                        if (!_statuses.AddTab(tabName, MyCommon.TabUsageType.UserDefined, null) || !AddNewTab(tabName, false, MyCommon.TabUsageType.UserDefined))
+                        var tab = new FilterTabModel(tabName);
+                        if (!_statuses.AddTab(tab) || !AddNewTab(tab, startup: false))
                         {
                             string tmp = string.Format(Properties.Resources.IDRuleMenuItem_ClickText2, tabName);
                             MessageBox.Show(tmp, Properties.Resources.IDRuleMenuItem_ClickText3, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
@@ -9444,8 +9461,8 @@ namespace OpenTween
             //ステータス欄にカウント表示
             //タブ未読数/タブ発言数 全未読数/総発言数 (未読@+未読DM数)
             if (_statuses == null) return "";
-            TabClass tbRep = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
-            TabClass tbDm = _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage);
+            TabModel tbRep = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
+            TabModel tbDm = _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage);
             if (tbRep == null || tbDm == null) return "";
             int urat = tbRep.UnreadCount + tbDm.UnreadCount;
             int ur = 0;
@@ -9832,7 +9849,7 @@ namespace OpenTween
                 }
                 else
                 {
-                    foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.PublicSearch))
+                    foreach (TabModel tb in _statuses.GetTabsByType(MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.PublicSearch))
                     {
                         if (tb == null || !tb.Contains(_curPost.InReplyToStatusId.Value)) break;
                         PostClass repPost = _statuses[_curPost.InReplyToStatusId.Value];
@@ -11710,7 +11727,7 @@ namespace OpenTween
             Control pnl = ((Control)sender).Parent;
             if (pnl == null) return;
             string tbName = pnl.Parent.Text;
-            TabClass tb = _statuses.Tabs[tbName];
+            var tb = (PublicSearchTabModel)_statuses.Tabs[tbName];
             ComboBox cmb = (ComboBox)pnl.Controls["comboSearch"];
             ComboBox cmbLang = (ComboBox)pnl.Controls["comboLang"];
             cmb.Text = cmb.Text.Trim();
@@ -11744,6 +11761,8 @@ namespace OpenTween
 
             var listView = (DetailsListView)pnl.Parent.Tag;
 
+            var queryChanged = tb.SearchWords != cmb.Text || tb.SearchLang != cmbLang.Text;
+
             tb.SearchWords = cmb.Text;
             tb.SearchLang = cmbLang.Text;
             if (string.IsNullOrEmpty(cmb.Text))
@@ -11752,7 +11771,7 @@ namespace OpenTween
                 SaveConfigsTabs();
                 return;
             }
-            if (tb.IsSearchQueryChanged)
+            if (queryChanged)
             {
                 int idx = cmb.Items.IndexOf(tb.SearchWords);
                 if (idx > -1) cmb.Items.RemoveAt(idx);
@@ -11806,7 +11825,7 @@ namespace OpenTween
             {
                 DetailsListView listView = null;
 
-                TabClass tb = _statuses.RemovedTab.Pop();
+                TabModel tb = _statuses.RemovedTab.Pop();
                 if (tb.TabType == MyCommon.TabUsageType.Related)
                 {
                     var relatedTab = _statuses.GetTabByType(MyCommon.TabUsageType.Related);
@@ -11838,8 +11857,9 @@ namespace OpenTween
                             renamed = TabName + i.ToString();
                         }
                         tb.TabName = renamed;
-                        AddNewTab(renamed, false, tb.TabType, tb.ListInfo);
-                        _statuses.Tabs.Add(renamed, tb);  // 後に
+
+                        _statuses.AddTab(tb);
+                        AddNewTab(tb, startup: false);
 
                         var tabPage = ListTab.TabPages[ListTab.TabPages.Count - 1];
                         listView = (DetailsListView)tabPage.Tag;
@@ -11855,8 +11875,9 @@ namespace OpenTween
                         renamed = tb.TabName + "(" + i.ToString() + ")";
                     }
                     tb.TabName = renamed;
-                    _statuses.Tabs.Add(renamed, tb);  // 先に
-                    AddNewTab(renamed, false, tb.TabType, tb.ListInfo);
+
+                    _statuses.AddTab(tb);
+                    AddNewTab(tb, startup: false);
 
                     var tabPage = ListTab.TabPages[ListTab.TabPages.Count - 1];
                     listView = (DetailsListView)tabPage.Tag;
@@ -11904,7 +11925,7 @@ namespace OpenTween
                 //タブ選択(or追加)
                 if (!SelectTab(out tabName)) return;
 
-                var tab = this._statuses.Tabs[tabName];
+                var tab = (FilterTabModel)this._statuses.Tabs[tabName];
 
                 bool mv;
                 bool mk;
@@ -12752,27 +12773,20 @@ namespace OpenTween
         /// <exception cref="TabException">名前の重複が多すぎてタブを作成できない場合</exception>
         private async Task OpenRelatedTab(PostClass post)
         {
-            var tabRelated = this._statuses.GetTabByType(MyCommon.TabUsageType.Related);
-            string tabName;
-
-            if (tabRelated == null)
+            var tabRelated = this._statuses.GetTabByType<RelatedPostsTabModel>();
+            if (tabRelated != null)
             {
-                tabName = this._statuses.MakeTabName("Related Tweets");
+                this.RemoveSpecifiedTab(tabRelated.TabName, confirm: false);
+            }
 
-                tabRelated = new TabClass(tabName, MyCommon.TabUsageType.Related);
-                tabRelated.UnreadManage = false;
-                tabRelated.Notify = false;
+            var tabName = this._statuses.MakeTabName("Related Tweets");
 
-                this.AddNewTab(tabName, false, MyCommon.TabUsageType.Related);
-                this._statuses.AddTab(tabRelated);
-            }
-            else
-            {
-                tabName = tabRelated.TabName;
-            }
+            tabRelated = new RelatedPostsTabModel(tabName, post);
+            tabRelated.UnreadManage = false;
+            tabRelated.Notify = false;
 
-            tabRelated.RelationTargetPost = post;
-            this.ClearTab(tabName, false);
+            this._statuses.AddTab(tabRelated);
+            this.AddNewTab(tabRelated, startup: false);
 
             for (int i = 0; i < this.ListTab.TabPages.Count; i++)
             {
index 7570dd9..5d3ecdf 100644 (file)
@@ -823,7 +823,7 @@ namespace OpenTween
             }
         }
 
-        public async Task GetUserTimelineApi(bool read, string userName, TabClass tab, bool more)
+        public async Task GetUserTimelineApi(bool read, string userName, UserTimelineTabModel tab, bool more)
         {
             this.CheckAccountState();
 
@@ -832,7 +832,7 @@ namespace OpenTween
             TwitterStatus[] statuses;
             if (string.IsNullOrEmpty(userName))
             {
-                var target = tab.User;
+                var target = tab.ScreenName;
                 if (string.IsNullOrEmpty(target)) return;
                 userName = target;
                 statuses = await this.Api.StatusesUserTimeline(userName, count)
@@ -875,14 +875,14 @@ namespace OpenTween
             return item;
         }
 
-        public async Task GetStatusApi(bool read, long id, TabClass tab)
+        public async Task GetStatusApi(bool read, long id, TabModel tab)
         {
             var post = await this.GetStatusApi(read, id)
                 .ConfigureAwait(false);
 
             //非同期アイコン取得&StatusDictionaryに追加
             if (tab != null && tab.IsInnerStorageTabType)
-                tab.AddPostToInnerStorage(post);
+                tab.AddPostQueue(post);
             else
                 TabInformations.GetInstance().AddPost(post);
         }
@@ -1044,7 +1044,7 @@ namespace OpenTween
             }
         }
 
-        private long? CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabClass tab, bool read)
+        private long? CreatePostsFromJson(TwitterStatus[] items, MyCommon.WORKERTYPE gType, TabModel tab, bool read)
         {
             long? minimumId = null;
 
@@ -1079,7 +1079,7 @@ namespace OpenTween
 
                 //非同期アイコン取得&StatusDictionaryに追加
                 if (tab != null && tab.IsInnerStorageTabType)
-                    tab.AddPostToInnerStorage(post);
+                    tab.AddPostQueue(post);
                 else
                     TabInformations.GetInstance().AddPost(post);
             }
@@ -1087,7 +1087,7 @@ namespace OpenTween
             return minimumId;
         }
 
-        private long? CreatePostsFromSearchJson(TwitterSearchResult items, TabClass tab, bool read, int count, bool more)
+        private long? CreatePostsFromSearchJson(TwitterSearchResult items, TabModel tab, bool read, int count, bool more)
         {
             long? minimumId = null;
 
@@ -1119,7 +1119,7 @@ namespace OpenTween
 
                 //非同期アイコン取得&StatusDictionaryに追加
                 if (tab != null && tab.IsInnerStorageTabType)
-                    tab.AddPostToInnerStorage(post);
+                    tab.AddPostQueue(post);
                 else
                     TabInformations.GetInstance().AddPost(post);
             }
@@ -1148,7 +1148,7 @@ namespace OpenTween
             }
         }
 
-        public async Task GetListStatus(bool read, TabClass tab, bool more, bool startup)
+        public async Task GetListStatus(bool read, ListTimelineTabModel tab, bool more, bool startup)
         {
             var count = GetApiResultCount(MyCommon.WORKERTYPE.List, more, startup);
 
@@ -1190,30 +1190,31 @@ namespace OpenTween
             return nextPost;
         }
 
-        public async Task GetRelatedResult(bool read, TabClass tab)
+        public async Task GetRelatedResult(bool read, RelatedPostsTabModel tab)
         {
+            var targetPost = tab.TargetPost;
             var relPosts = new Dictionary<Int64, PostClass>();
-            if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == null)
+            if (targetPost.TextFromApi.Contains("@") && targetPost.InReplyToStatusId == null)
             {
                 //検索結果対応
-                var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId];
+                var p = TabInformations.GetInstance()[targetPost.StatusId];
                 if (p != null && p.InReplyToStatusId != null)
                 {
-                    tab.RelationTargetPost = p;
+                    targetPost = p;
                 }
                 else
                 {
-                    p = await this.GetStatusApi(read, tab.RelationTargetPost.StatusId)
+                    p = await this.GetStatusApi(read, targetPost.StatusId)
                         .ConfigureAwait(false);
-                    tab.RelationTargetPost = p;
+                    targetPost = p;
                 }
             }
-            relPosts.Add(tab.RelationTargetPost.StatusId, tab.RelationTargetPost);
+            relPosts.Add(targetPost.StatusId, targetPost);
 
             Exception lastException = null;
 
             // in_reply_to_status_id を使用してリプライチェインを辿る
-            var nextPost = FindTopOfReplyChain(relPosts, tab.RelationTargetPost.StatusId);
+            var nextPost = FindTopOfReplyChain(relPosts, targetPost.StatusId);
             var loopCount = 1;
             while (nextPost.InReplyToStatusId != null && loopCount++ <= 20)
             {
@@ -1240,7 +1241,7 @@ namespace OpenTween
             }
 
             //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
-            var text = tab.RelationTargetPost.Text;
+            var text = targetPost.Text;
             var ma = Twitter.StatusUrlRegex.Matches(text).Cast<Match>()
                 .Concat(Twitter.ThirdPartyStatusUrlRegex.Matches(text).Cast<Match>());
             foreach (var _match in ma)
@@ -1278,14 +1279,14 @@ namespace OpenTween
                 else
                     p.IsRead = read;
 
-                tab.AddPostToInnerStorage(p);
+                tab.AddPostQueue(p);
             });
 
             if (lastException != null)
                 throw new WebApiException(lastException.Message, lastException);
         }
 
-        public async Task GetSearch(bool read, TabClass tab, bool more)
+        public async Task GetSearch(bool read, PublicSearchTabModel tab, bool more)
         {
             var count = GetApiResultCount(MyCommon.WORKERTYPE.PublicSearch, more, false);
 
@@ -1408,7 +1409,7 @@ namespace OpenTween
                 post.IsDm = true;
 
                 var dmTab = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.DirectMessage);
-                dmTab.AddPostToInnerStorage(post);
+                dmTab.AddPostQueue(post);
             }
         }