OSDN Git Service

UserTimelineタブでルールが追加できたりなんだり対応
[opentween/open-tween.git] / Tween / StatusDictionary.vb
1 ' Tween - Client of Twitter
2 ' Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 '           (c) 2008-2011 Moz (@syo68k)
4 '           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 '           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 '           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 ' All rights reserved.
8
9 ' This file is part of Tween.
10
11 ' This program is free software; you can redistribute it and/or modify it
12 ' under the terms of the GNU General Public License as published by the Free
13 ' Software Foundation; either version 3 of the License, or (at your option)
14 ' any later version.
15
16 ' This program is distributed in the hope that it will be useful, but
17 ' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 ' for more details. 
20
21 ' You should have received a copy of the GNU General Public License along
22 ' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
23 ' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
24 ' Boston, MA 02110-1301, USA.
25
26 Imports System.Collections.Generic
27 Imports System.Collections.ObjectModel
28 Imports System.Linq.Expressions
29 Imports Tween.TweenCustomControl
30 Imports System.Text.RegularExpressions
31 Imports System.Web.HttpUtility
32 Imports System.Text
33 Imports System.Linq
34 Imports System.Linq.Expressions.DynamicExpression
35
36 Public NotInheritable Class PostClass
37     Implements ICloneable
38     Private _Nick As String
39     Private _textFromApi As String
40     Private _ImageUrl As String
41     Private _screenName As String
42     Private _createdAt As Date
43     Private _statusId As Long
44     Private _IsFav As Boolean
45     Private _text As String
46     Private _IsRead As Boolean
47     Private _IsReply As Boolean
48     Private _IsExcludeReply As Boolean
49     Private _IsProtect As Boolean
50     Private _IsOWL As Boolean
51     Private _IsMark As Boolean
52     Private _InReplyToUser As String
53     Private _InReplyToStatusId As Long
54     Private _Source As String
55     Private _SourceHtml As String
56     Private _ReplyToList As New List(Of String)
57     Private _IsMe As Boolean
58     Private _IsDm As Boolean
59     Private _statuses As Statuses = Statuses.None
60     Private _UserId As Long
61     Private _FilterHit As Boolean
62     Private _RetweetedBy As String = ""
63     Private _RetweetedId As Long = 0
64     Private _SearchTabName As String = ""
65     Private _IsDeleted As Boolean = False
66     Private _InReplyToUserId As Long = 0
67
68     <FlagsAttribute()> _
69     Private Enum Statuses
70         None = 0
71         Protect = 1
72         Mark = 2
73         Read = 4
74         Reply = 8
75     End Enum
76
77     Public Sub New(ByVal Nickname As String, _
78             ByVal textFromApi As String, _
79             ByVal text As String, _
80             ByVal ImageUrl As String, _
81             ByVal screenName As String, _
82             ByVal createdAt As Date, _
83             ByVal statusId As Long, _
84             ByVal IsFav As Boolean, _
85             ByVal IsRead As Boolean, _
86             ByVal IsReply As Boolean, _
87             ByVal IsExcludeReply As Boolean, _
88             ByVal IsProtect As Boolean, _
89             ByVal IsOwl As Boolean, _
90             ByVal IsMark As Boolean, _
91             ByVal InReplyToUser As String, _
92             ByVal InReplyToStatusId As Long, _
93             ByVal Source As String, _
94             ByVal SourceHtml As String, _
95             ByVal ReplyToList As List(Of String), _
96             ByVal IsMe As Boolean, _
97             ByVal IsDm As Boolean, _
98             ByVal userId As Long, _
99             ByVal FilterHit As Boolean, _
100             ByVal RetweetedBy As String, _
101             ByVal RetweetedId As Long)
102         _Nick = Nickname
103         _textFromApi = textFromApi
104         _ImageUrl = ImageUrl
105         _screenName = screenName
106         _createdAt = createdAt
107         _statusId = statusId
108         _IsFav = IsFav
109         _text = Text
110         _IsRead = IsRead
111         _IsReply = IsReply
112         _IsExcludeReply = IsExcludeReply
113         _IsProtect = IsProtect
114         _IsOWL = IsOwl
115         _IsMark = IsMark
116         _InReplyToUser = InReplyToUser
117         _InReplyToStatusId = InReplyToStatusId
118         _Source = Source
119         _SourceHtml = SourceHtml
120         _ReplyToList = ReplyToList
121         _IsMe = IsMe
122         _IsDm = IsDm
123         _UserId = userId
124         _FilterHit = FilterHit
125         _RetweetedBy = RetweetedBy
126         _RetweetedId = RetweetedId
127     End Sub
128
129     Public Sub New()
130     End Sub
131
132     Public Property Nickname() As String
133         Get
134             Return _Nick
135         End Get
136         Set(ByVal value As String)
137             _Nick = value
138         End Set
139     End Property
140     Public Property TextFromApi() As String
141         Get
142             Return _textFromApi
143         End Get
144         Set(ByVal value As String)
145             _textFromApi = value
146         End Set
147     End Property
148     Public Property ImageUrl() As String
149         Get
150             Return _ImageUrl
151         End Get
152         Set(ByVal value As String)
153             _ImageUrl = value
154         End Set
155     End Property
156     Public Property ScreenName() As String
157         Get
158             Return _screenName
159         End Get
160         Set(ByVal value As String)
161             _screenName = value
162         End Set
163     End Property
164     Public Property CreatedAt() As Date
165         Get
166             Return _createdAt
167         End Get
168         Set(ByVal value As Date)
169             _createdAt = value
170         End Set
171     End Property
172     Public Property StatusId() As Long
173         Get
174             Return _statusId
175         End Get
176         Set(ByVal value As Long)
177             _statusId = value
178         End Set
179     End Property
180     Public Property IsFav() As Boolean
181         Get
182             If Me.RetweetedId > 0 AndAlso TabInformations.GetInstance.RetweetSource(Me.RetweetedId) IsNot Nothing Then
183                 Return TabInformations.GetInstance.RetweetSource(Me.RetweetedId).IsFav
184             Else
185                 Return _IsFav
186             End If
187         End Get
188         Set(ByVal value As Boolean)
189             _IsFav = value
190             If Me.RetweetedId > 0 AndAlso TabInformations.GetInstance.RetweetSource(Me.RetweetedId) IsNot Nothing Then
191                 TabInformations.GetInstance.RetweetSource(Me.RetweetedId).IsFav = value
192             End If
193         End Set
194     End Property
195     Public Property Text() As String
196         Get
197             Return _text
198         End Get
199         Set(ByVal value As String)
200             _text = value
201         End Set
202     End Property
203     Public Property IsRead() As Boolean
204         Get
205             Return _IsRead
206         End Get
207         Set(ByVal value As Boolean)
208             If value Then
209                 _statuses = _statuses Or Statuses.Read
210             Else
211                 _statuses = _statuses And Not Statuses.Read
212             End If
213             _IsRead = value
214         End Set
215     End Property
216     Public Property IsReply() As Boolean
217         Get
218             Return _IsReply
219         End Get
220         Set(ByVal value As Boolean)
221             _IsReply = value
222         End Set
223     End Property
224     Public Property IsExcludeReply() As Boolean
225         Get
226             Return _IsExcludeReply
227         End Get
228         Set(ByVal value As Boolean)
229             _IsExcludeReply = value
230         End Set
231     End Property
232     Public Property IsProtect() As Boolean
233         Get
234             Return _IsProtect
235         End Get
236         Set(ByVal value As Boolean)
237             If value Then
238                 _statuses = _statuses Or Statuses.Protect
239             Else
240                 _statuses = _statuses And Not Statuses.Protect
241             End If
242             _IsProtect = value
243         End Set
244     End Property
245     Public Property IsOwl() As Boolean
246         Get
247             Return _IsOWL
248         End Get
249         Set(ByVal value As Boolean)
250             _IsOWL = value
251         End Set
252     End Property
253     Public Property IsMark() As Boolean
254         Get
255             Return _IsMark
256         End Get
257         Set(ByVal value As Boolean)
258             If value Then
259                 _statuses = _statuses Or Statuses.Mark
260             Else
261                 _statuses = _statuses And Not Statuses.Mark
262             End If
263             _IsMark = value
264         End Set
265     End Property
266     Public Property InReplyToUser() As String
267         Get
268             Return _InReplyToUser
269         End Get
270         Set(ByVal value As String)
271             _InReplyToUser = value
272         End Set
273     End Property
274     Public Property InReplyToStatusId() As Long
275         Get
276             Return _InReplyToStatusId
277         End Get
278         Set(ByVal value As Long)
279             _InReplyToStatusId = value
280         End Set
281     End Property
282
283     Public Property InReplyToUserId() As Long
284         Get
285             Return _InReplyToUserId
286         End Get
287         Set(ByVal value As Long)
288             _InReplyToUserId = value
289         End Set
290     End Property
291     Public Property Source() As String
292         Get
293             Return _Source
294         End Get
295         Set(ByVal value As String)
296             _Source = value
297         End Set
298     End Property
299     Public Property SourceHtml() As String
300         Get
301             Return _SourceHtml
302         End Get
303         Set(ByVal value As String)
304             _SourceHtml = value
305         End Set
306     End Property
307     Public Property ReplyToList() As List(Of String)
308         Get
309             Return _ReplyToList
310         End Get
311         Set(ByVal value As List(Of String))
312             _ReplyToList = value
313         End Set
314     End Property
315     Public Property IsMe() As Boolean
316         Get
317             Return _IsMe
318         End Get
319         Set(ByVal value As Boolean)
320             _IsMe = value
321         End Set
322     End Property
323     Public Property IsDm() As Boolean
324         Get
325             Return _IsDm
326         End Get
327         Set(ByVal value As Boolean)
328             _IsDm = value
329         End Set
330     End Property
331     'Public ReadOnly Property StatusIndex() As Integer
332     '    Get
333     '        Return _statuses
334     '    End Get
335     'End Property
336     Public Property UserId() As Long
337         Get
338             Return _UserId
339         End Get
340         Set(ByVal value As Long)
341             _UserId = value
342         End Set
343     End Property
344     Public Property FilterHit() As Boolean
345         Get
346             Return _FilterHit
347         End Get
348         Set(ByVal value As Boolean)
349             _FilterHit = value
350         End Set
351     End Property
352     Public Property RetweetedBy() As String
353         Get
354             Return _RetweetedBy
355         End Get
356         Set(ByVal value As String)
357             _RetweetedBy = value
358         End Set
359     End Property
360     Public Property RetweetedId() As Long
361         Get
362             Return _RetweetedId
363         End Get
364         Set(ByVal value As Long)
365             _RetweetedId = value
366         End Set
367     End Property
368     Public Property RelTabName() As String
369         Get
370             Return _SearchTabName
371         End Get
372         Set(ByVal value As String)
373             _SearchTabName = value
374         End Set
375     End Property
376     Public Property IsDeleted As Boolean
377         Get
378             Return _IsDeleted
379         End Get
380         Set(ByVal value As Boolean)
381             If value Then
382                 Me.InReplyToStatusId = 0
383                 Me.InReplyToUser = ""
384                 Me.InReplyToUserId = 0
385                 Me.IsReply = False
386                 Me.ReplyToList = New List(Of String)
387             End If
388             _IsDeleted = value
389         End Set
390     End Property
391
392     Public Property FavoritedCount As Integer
393
394     Public Function Copy() As PostClass
395         Dim post As PostClass = DirectCast(Me.Clone, PostClass)
396         post.ReplyToList = New List(Of String)(Me.ReplyToList)
397         Return post
398     End Function
399
400     Public Overrides Function Equals(ByVal obj As Object) As Boolean
401         If (obj Is Nothing) OrElse Not (Me.GetType() Is obj.GetType()) Then Return False
402         Return Me.Equals(CType(obj, PostClass))
403     End Function
404
405     Public Overloads Function Equals(ByVal other As PostClass) As Boolean
406         If other Is Nothing Then Return False
407         Return (Me.Nickname = other.Nickname) AndAlso
408                 (Me.TextFromApi = other.TextFromApi) AndAlso
409                 (Me.ImageUrl = other.ImageUrl) AndAlso
410                 (Me.ScreenName = other.ScreenName) AndAlso
411                 (Me.CreatedAt = other.CreatedAt) AndAlso
412                 (Me.StatusId = other.StatusId) AndAlso
413                 (Me.IsFav = other.IsFav) AndAlso
414                 (Me.Text = other.Text) AndAlso
415                 (Me.IsRead = other.IsRead) AndAlso
416                 (Me.IsReply = other.IsReply) AndAlso
417                 (Me.IsExcludeReply = other.IsExcludeReply) AndAlso
418                 (Me.IsProtect = other.IsProtect) AndAlso
419                 (Me.IsOwl = other.IsOwl) AndAlso
420                 (Me.IsMark = other.IsMark) AndAlso
421                 (Me.InReplyToUser = other.InReplyToUser) AndAlso
422                 (Me.InReplyToStatusId = other.InReplyToStatusId) AndAlso
423                 (Me.Source = other.Source) AndAlso
424                 (Me.SourceHtml = other.SourceHtml) AndAlso
425                 (Me.ReplyToList.Equals(other.ReplyToList)) AndAlso
426                 (Me.IsMe = other.IsMe) AndAlso
427                 (Me.IsDm = other.IsDm) AndAlso
428                 (Me.UserId = other.UserId) AndAlso
429                 (Me.FilterHit = other.FilterHit) AndAlso
430                 (Me.RetweetedBy = other.RetweetedBy) AndAlso
431                 (Me.RetweetedId = other.RetweetedId) AndAlso
432                 (Me.RelTabName = other.RelTabName) AndAlso
433                 (Me.IsDeleted = other.IsDeleted) AndAlso
434                 (Me.InReplyToUserId = other.InReplyToUserId)
435
436     End Function
437 #Region "IClonable.Clone"
438     Private Function Clone() As Object Implements ICloneable.Clone
439         Return Me.MemberwiseClone()
440     End Function
441 #End Region
442 End Class
443
444 Public NotInheritable Class TabInformations
445     '個別タブの情報をDictionaryで保持
446     Private _sorter As IdComparerClass
447     Private _tabs As New Dictionary(Of String, TabClass)
448     Private _statuses As New Dictionary(Of Long, PostClass)
449     Private _addedIds As List(Of Long)
450     Private _deletedIds As New List(Of Long)
451     Private _retweets As New Dictionary(Of Long, PostClass)
452     Private _removedTab As New Stack(Of TabClass)
453
454     '発言の追加
455     'AddPost(複数回) -> DistributePosts          -> SubmitUpdate
456
457     'トランザクション用
458     Private _addCount As Integer
459     Private _soundFile As String
460     Private _notifyPosts As List(Of PostClass)
461     Private ReadOnly LockObj As New Object
462     Private ReadOnly LockUnread As New Object
463
464     Private Shared _instance As TabInformations = New TabInformations
465
466     'List
467     Private _lists As New List(Of ListElement)
468
469     Private Sub New()
470         _sorter = New IdComparerClass()
471     End Sub
472
473     Public Shared Function GetInstance() As TabInformations
474         Return _instance    'singleton
475     End Function
476
477     Public Property SubscribableLists() As List(Of ListElement)
478         Get
479             Return _lists
480         End Get
481         Set(ByVal value As List(Of ListElement))
482             If value IsNot Nothing AndAlso value.Count > 0 Then
483                 For Each tb As TabClass In Me.GetTabsByType(TabUsageType.Lists)
484                     For Each list As ListElement In value
485                         If tb.ListInfo.Id = list.Id Then
486                             tb.ListInfo = list
487                             Exit For
488                         End If
489                     Next
490                 Next
491             End If
492             _lists = value
493         End Set
494     End Property
495
496     Public Sub AddTab(ByVal TabName As String, ByVal TabType As TabUsageType, ByVal List As ListElement)
497         _tabs.Add(TabName, New TabClass(TabName, TabType, List))
498         _tabs(TabName).Sorter.Mode = _sorter.Mode
499         _tabs(TabName).Sorter.Order = _sorter.Order
500     End Sub
501
502     'Public Sub AddTab(ByVal TabName As String, ByVal Tab As TabClass)
503     '    _tabs.Add(TabName, Tab)
504     'End Sub
505
506     Public Sub RemoveTab(ByVal TabName As String)
507         SyncLock LockObj
508             If IsDefaultTab(TabName) Then Exit Sub '念のため
509             If Not _tabs(TabName).IsInnerStorageTabType Then
510                 Dim homeTab As TabClass = GetTabByType(TabUsageType.Home)
511                 Dim dmName As String = GetTabByType(TabUsageType.DirectMessage).TabName
512
513                 For idx As Integer = 0 To _tabs(TabName).AllCount - 1
514                     Dim exist As Boolean = False
515                     Dim Id As Long = _tabs(TabName).GetId(idx)
516                     If Id < 0 Then Continue For
517                     For Each key As String In _tabs.Keys
518                         If Not key = TabName AndAlso key <> dmName Then
519                             If _tabs(key).Contains(Id) Then
520                                 exist = True
521                                 Exit For
522                             End If
523                         End If
524                     Next
525                     If Not exist Then homeTab.Add(Id, _statuses(Id).IsRead, False)
526                 Next
527             End If
528             _removedTab.Push(_tabs(TabName))
529             _tabs.Remove(TabName)
530         End SyncLock
531     End Sub
532
533     Public RemovedTab As Stack(Of TabClass) = _removedTab
534
535     Public Function ContainsTab(ByVal TabText As String) As Boolean
536         Return _tabs.ContainsKey(TabText)
537     End Function
538
539     Public Function ContainsTab(ByVal ts As TabClass) As Boolean
540         Return _tabs.ContainsValue(ts)
541     End Function
542
543     Public Property Tabs() As Dictionary(Of String, TabClass)
544         Get
545             Return _tabs
546         End Get
547         Set(ByVal value As Dictionary(Of String, TabClass))
548             _tabs = value
549         End Set
550     End Property
551
552     Public ReadOnly Property KeysTab() As Collections.Generic.Dictionary(Of String, TabClass).KeyCollection
553         Get
554             Return _tabs.Keys
555         End Get
556     End Property
557
558     Public Sub SortPosts()
559         For Each key As String In _tabs.Keys
560             _tabs(key).Sort()
561         Next
562     End Sub
563
564     Public Property SortOrder() As SortOrder
565         Get
566             Return _sorter.Order
567         End Get
568         Set(ByVal value As SortOrder)
569             _sorter.Order = value
570             For Each key As String In _tabs.Keys
571                 _tabs(key).Sorter.Order = value
572             Next
573         End Set
574     End Property
575
576     Public Property SortMode() As IdComparerClass.ComparerMode
577         Get
578             Return _sorter.Mode
579         End Get
580         Set(ByVal value As IdComparerClass.ComparerMode)
581             _sorter.Mode = value
582             For Each key As String In _tabs.Keys
583                 _tabs(key).Sorter.Mode = value
584             Next
585         End Set
586     End Property
587
588     Public Function ToggleSortOrder(ByVal SortMode As IdComparerClass.ComparerMode) As Windows.Forms.SortOrder
589         If _sorter.Mode = SortMode Then
590             If _sorter.Order = Windows.Forms.SortOrder.Ascending Then
591                 _sorter.Order = Windows.Forms.SortOrder.Descending
592             Else
593                 _sorter.Order = Windows.Forms.SortOrder.Ascending
594             End If
595             For Each key As String In _tabs.Keys
596                 _tabs(key).Sorter.Order = _sorter.Order
597             Next
598         Else
599             _sorter.Mode = SortMode
600             _sorter.Order = Windows.Forms.SortOrder.Ascending
601             For Each key As String In _tabs.Keys
602                 _tabs(key).Sorter.Mode = SortMode
603                 _tabs(key).Sorter.Order = Windows.Forms.SortOrder.Ascending
604             Next
605         End If
606         Me.SortPosts()
607         Return _sorter.Order
608     End Function
609
610     Public ReadOnly Property RetweetSource(ByVal Id As Long) As PostClass
611         Get
612             If _retweets.ContainsKey(Id) Then
613                 Return _retweets(Id)
614             Else
615                 Return Nothing
616             End If
617         End Get
618     End Property
619
620     Public Sub RemoveFavPost(ByVal Id As Long)
621         SyncLock LockObj
622             Dim post As PostClass = Nothing
623             Dim tab As TabClass = Me.GetTabByType(TabUsageType.Favorites)
624             Dim tn As String = tab.TabName
625             If _statuses.ContainsKey(Id) Then
626                 post = _statuses(Id)
627                 '指定タブから該当ID削除
628                 Dim tType As TabUsageType = tab.TabType
629                 If tab.Contains(Id) Then
630                     If tab.UnreadManage AndAlso Not post.IsRead Then    '未読管理
631                         SyncLock LockUnread
632                             tab.UnreadCount -= 1
633                             Me.SetNextUnreadId(Id, tab)
634                         End SyncLock
635                     End If
636                     tab.Remove(Id)
637                 End If
638                 'FavタブからRetweet発言を削除する場合は、他の同一参照Retweetも削除
639                 If tType = TabUsageType.Favorites AndAlso post.RetweetedId > 0 Then
640                     For i As Integer = 0 To tab.AllCount - 1
641                         Dim rPost As PostClass = Nothing
642                         Try
643                             rPost = Me.Item(tn, i)
644                         Catch ex As ArgumentOutOfRangeException
645                             Exit For
646                         End Try
647                         If rPost.RetweetedId > 0 AndAlso rPost.RetweetedId = post.RetweetedId Then
648                             If tab.UnreadManage AndAlso Not rPost.IsRead Then    '未読管理
649                                 SyncLock LockUnread
650                                     tab.UnreadCount -= 1
651                                     Me.SetNextUnreadId(rPost.StatusId, tab)
652                                 End SyncLock
653                             End If
654                             tab.Remove(rPost.StatusId)
655                         End If
656                     Next
657                 End If
658             End If
659             ''TabType=PublicSearchの場合(Postの保存先がTabClass内)
660             'If tab.Contains(StatusId) AndAlso _
661             '   (tab.TabType = TabUsageType.PublicSearch OrElse tab.TabType = TabUsageType.DirectMessage) Then
662             '    post = tab.Posts(StatusId)
663             '    If tab.UnreadManage AndAlso Not post.IsRead Then    '未読管理
664             '        SyncLock LockUnread
665             '            tab.UnreadCount -= 1
666             '            Me.SetNextUnreadId(StatusId, tab)
667             '        End SyncLock
668             '    End If
669             '    tab.Remove(StatusId)
670             'End If
671         End SyncLock
672     End Sub
673
674     Public Sub RemovePostReserve(ByVal id As Long)
675         SyncLock LockObj
676             Me._deletedIds.Add(id)
677             Me.DeletePost(id)   'UI選択行がずれるため、RemovePostは使用しない
678         End SyncLock
679     End Sub
680
681     Public Sub RemovePost(ByVal Id As Long)
682         SyncLock LockObj
683             Dim post As PostClass = Nothing
684             If _statuses.ContainsKey(Id) Then
685                 post = _statuses(Id)
686                 '各タブから該当ID削除
687                 For Each key As String In _tabs.Keys
688                     Dim tab As TabClass = _tabs(key)
689                     If tab.Contains(Id) Then
690                         If Not tab.IsInnerStorageTabType Then
691                             If tab.UnreadManage AndAlso Not post.IsRead Then    '未読管理
692                                 SyncLock LockUnread
693                                     tab.UnreadCount -= 1
694                                     Me.SetNextUnreadId(Id, tab)
695                                 End SyncLock
696                             End If
697                         Else '未読数がずれる可能性があるためtab.Postsの未読も確認する
698                             If tab.UnreadManage AndAlso Not tab.Posts(Id).IsRead Then    '未読管理
699                                 SyncLock LockUnread
700                                     tab.UnreadCount -= 1
701                                     Me.SetNextUnreadId(Id, tab)
702                                 End SyncLock
703                             End If
704                         End If
705                         tab.Remove(Id)
706                     End If
707                 Next
708                 _statuses.Remove(Id)
709             End If
710         End SyncLock
711     End Sub
712
713     Private Sub DeletePost(ByVal Id As Long)
714         SyncLock LockObj
715             Dim post As PostClass = Nothing
716             If _statuses.ContainsKey(Id) Then
717                 post = _statuses(Id)
718                 post.IsDeleted = True
719             End If
720             For Each tb As TabClass In Me.GetTabsInnerStorageType
721                 If tb.Contains(Id) Then
722                     post = tb.Posts(Id)
723                     post.IsDeleted = True
724                 End If
725             Next
726         End SyncLock
727     End Sub
728
729     Public Function GetOldestUnreadIndex(ByVal TabName As String) As Integer
730         Dim tb As TabClass = _tabs(TabName)
731         If tb.OldestUnreadId > -1 AndAlso _
732            tb.Contains(tb.OldestUnreadId) AndAlso _
733            tb.UnreadCount > 0 Then
734             '未読アイテムへ
735             Dim isRead As Boolean
736             If Not tb.IsInnerStorageTabType Then
737                 isRead = _statuses(tb.OldestUnreadId).IsRead
738             Else
739                 isRead = tb.Posts(tb.OldestUnreadId).IsRead
740             End If
741             If isRead Then
742                 '状態不整合(最古未読IDが実は既読)
743                 SyncLock LockUnread
744                     Me.SetNextUnreadId(-1, tb)  '頭から探索
745                 End SyncLock
746                 If tb.OldestUnreadId = -1 Then
747                     Return -1
748                 Else
749                     Return tb.IndexOf(tb.OldestUnreadId)
750                 End If
751             Else
752                 Return tb.IndexOf(tb.OldestUnreadId)    '最短経路
753             End If
754         Else
755             '一見未読なさそうだが、未読カウントはあるので探索
756             'If tb.UnreadCount > 0 Then
757             If Not (tb.UnreadManage AndAlso AppendSettingDialog.Instance.UnreadManage) Then Return -1
758             SyncLock LockUnread
759                 Me.SetNextUnreadId(-1, tb)
760             End SyncLock
761             If tb.OldestUnreadId = -1 Then
762                 Return -1
763             Else
764                 Return tb.IndexOf(tb.OldestUnreadId)
765             End If
766             '    Else
767             '    Return -1
768             'End If
769         End If
770     End Function
771
772     Private Sub SetNextUnreadId(ByVal CurrentId As Long, ByVal Tab As TabClass)
773         'CurrentID:今既読にしたID(OldestIDの可能性あり)
774         '最古未読が設定されていて、既読の場合(1発言以上存在)
775         Try
776             Dim posts As Dictionary(Of Long, PostClass)
777             If Not Tab.IsInnerStorageTabType Then
778                 posts = _statuses
779             Else
780                 posts = Tab.Posts
781             End If
782             If Tab.OldestUnreadId > -1 AndAlso _
783                posts.ContainsKey(Tab.OldestUnreadId) AndAlso _
784                posts.Item(Tab.OldestUnreadId).IsRead AndAlso _
785                _sorter.Mode = IdComparerClass.ComparerMode.Id Then     '次の未読探索
786                 If Tab.UnreadCount = 0 Then
787                     '未読数0→最古未読なし
788                     Tab.OldestUnreadId = -1
789                 ElseIf Tab.OldestUnreadId = CurrentId AndAlso CurrentId > -1 Then
790                     '最古IDを既読にしたタイミング→次のIDから続けて探索
791                     Dim idx As Integer = Tab.IndexOf(CurrentId)
792                     If idx > -1 Then
793                         '続きから探索
794                         FindUnreadId(idx, Tab)
795                     Else
796                         '頭から探索
797                         FindUnreadId(-1, Tab)
798                     End If
799                 Else
800                     '頭から探索
801                     FindUnreadId(-1, Tab)
802                 End If
803             Else
804                 '頭から探索
805                 FindUnreadId(-1, Tab)
806             End If
807         Catch ex As Generic.KeyNotFoundException
808             '頭から探索
809             FindUnreadId(-1, Tab)
810         End Try
811     End Sub
812
813     Private Sub FindUnreadId(ByVal StartIdx As Integer, ByVal Tab As TabClass)
814         If Tab.AllCount = 0 Then
815             Tab.OldestUnreadId = -1
816             Tab.UnreadCount = 0
817             Exit Sub
818         End If
819         Dim toIdx As Integer = 0
820         Dim stp As Integer = 1
821         Tab.OldestUnreadId = -1
822         If _sorter.Order = Windows.Forms.SortOrder.Ascending Then
823             If StartIdx = -1 Then
824                 StartIdx = 0
825             Else
826                 'StartIdx += 1
827                 If StartIdx > Tab.AllCount - 1 Then StartIdx = Tab.AllCount - 1 '念のため
828             End If
829             toIdx = Tab.AllCount - 1
830             If toIdx < 0 Then toIdx = 0 '念のため
831             stp = 1
832         Else
833             If StartIdx = -1 Then
834                 StartIdx = Tab.AllCount - 1
835             Else
836                 'StartIdx -= 1
837             End If
838             If StartIdx < 0 Then StartIdx = 0 '念のため
839             toIdx = 0
840             stp = -1
841         End If
842         If Not Tab.IsInnerStorageTabType Then
843             For i As Integer = StartIdx To toIdx Step stp
844                 If Not _statuses(Tab.GetId(i)).IsRead Then
845                     Tab.OldestUnreadId = Tab.GetId(i)
846                     Exit For
847                 End If
848             Next
849         Else
850             For i As Integer = StartIdx To toIdx Step stp
851                 If Not Tab.Posts(Tab.GetId(i)).IsRead Then
852                     Tab.OldestUnreadId = Tab.GetId(i)
853                     Exit For
854                 End If
855             Next
856         End If
857     End Sub
858
859     Public Function DistributePosts() As Integer
860         SyncLock LockObj
861             '戻り値は追加件数
862             'If _addedIds Is Nothing Then Return 0
863             'If _addedIds.Count = 0 Then Return 0
864
865             If _addedIds Is Nothing Then _addedIds = New List(Of Long)
866             If _notifyPosts Is Nothing Then _notifyPosts = New List(Of PostClass)
867             Me.Distribute()    'タブに仮振分
868             Dim retCnt As Integer = _addedIds.Count
869             _addCount += retCnt
870             _addedIds.Clear()
871             _addedIds = Nothing     '後始末
872             Return retCnt     '件数
873         End SyncLock
874     End Function
875
876     Public Function SubmitUpdate(ByRef soundFile As String,
877                                  ByRef notifyPosts As PostClass(),
878                                  ByRef isMentionIncluded As Boolean,
879                                  ByRef isDeletePost As Boolean,
880                                  ByVal isUserStream As Boolean) As Integer
881         '注:メインスレッドから呼ぶこと
882         SyncLock LockObj
883             If _notifyPosts Is Nothing Then
884                 soundFile = ""
885                 notifyPosts = Nothing
886                 Return 0
887             End If
888
889             For Each tb As TabClass In _tabs.Values
890                 If tb.IsInnerStorageTabType Then
891                     _addCount += tb.GetTemporaryCount
892                 End If
893                 tb.AddSubmit(isMentionIncluded)  '振分確定(各タブに反映)
894             Next
895             ''UserStreamで反映間隔10秒以下だったら、30秒ごとにソートする
896             ''10秒以上だったら毎回ソート
897             'Static lastSort As DateTime = Now
898             'If AppendSettingDialog.Instance.UserstreamPeriodInt < 10 AndAlso isUserStream Then
899             '    If Now.Subtract(lastSort) > TimeSpan.FromSeconds(30) Then
900             '        lastSort = Now
901             '        isUserStream = False
902             '    End If
903             'Else
904             '    isUserStream = False
905             'End If
906             If Not isUserStream OrElse Me.SortMode <> IdComparerClass.ComparerMode.Id Then
907                 Me.SortPosts()
908             End If
909             If isUserStream Then
910                 isDeletePost = Me._deletedIds.Count > 0
911                 For Each id As Long In Me._deletedIds
912                     'Me.DeletePost(StatusId)
913                     Me.RemovePost(id)
914                 Next
915                 Me._deletedIds.Clear()
916             End If
917
918             soundFile = _soundFile
919             _soundFile = ""
920             notifyPosts = _notifyPosts.ToArray()
921             _notifyPosts.Clear()
922             _notifyPosts = Nothing
923             Dim retCnt As Integer = _addCount
924             _addCount = 0
925             Return retCnt    '件数(EndUpdateの戻り値と同じ)
926         End SyncLock
927     End Function
928
929     Private Sub Distribute()
930         '各タブのフィルターと照合。合致したらタブにID追加
931         '通知メッセージ用に、表示必要な発言リストと再生サウンドを返す
932         'notifyPosts = New List(Of PostClass)
933         Dim homeTab As TabClass = GetTabByType(TabUsageType.Home)
934         Dim replyTab As TabClass = GetTabByType(TabUsageType.Mentions)
935         Dim dmTab As TabClass = GetTabByType(TabUsageType.DirectMessage)
936         Dim favTab As TabClass = GetTabByType(TabUsageType.Favorites)
937         For Each id As Long In _addedIds
938             Dim post As PostClass = _statuses(id)
939             Dim add As Boolean = False  '通知リスト追加フラグ
940             Dim mv As Boolean = False   '移動フラグ(Recent追加有無)
941             Dim rslt As HITRESULT = HITRESULT.None
942             post.IsExcludeReply = False
943             For Each tn As String In _tabs.Keys
944                 rslt = _tabs(tn).AddFiltered(post)
945                 If rslt <> HITRESULT.None AndAlso rslt <> HITRESULT.Exclude Then
946                     If rslt = HITRESULT.CopyAndMark Then post.IsMark = True 'マークあり
947                     If rslt = HITRESULT.Move Then
948                         mv = True '移動
949                         post.IsMark = False
950                     End If
951                     If _tabs(tn).Notify Then add = True '通知あり
952                     If Not _tabs(tn).SoundFile = "" AndAlso _soundFile = "" Then
953                         _soundFile = _tabs(tn).SoundFile 'wavファイル(未設定の場合のみ)
954                     End If
955                     post.FilterHit = True
956                 Else
957                     If rslt = HITRESULT.Exclude AndAlso _tabs(tn).TabType = TabUsageType.Mentions Then
958                         post.IsExcludeReply = True
959                     End If
960                     post.FilterHit = False
961                 End If
962             Next
963             If Not mv Then  '移動されなかったらRecentに追加
964                 homeTab.Add(post.StatusId, post.IsRead, True)
965                 If Not homeTab.SoundFile = "" AndAlso _soundFile = "" Then _soundFile = homeTab.SoundFile
966                 If homeTab.Notify Then add = True
967             End If
968             If post.IsReply AndAlso Not post.IsExcludeReply Then    '除外ルール適用のないReplyならReplyタブに追加
969                 replyTab.Add(post.StatusId, post.IsRead, True)
970                 If Not replyTab.SoundFile = "" Then _soundFile = replyTab.SoundFile
971                 If replyTab.Notify Then add = True
972             End If
973             If post.IsFav Then    'Fav済み発言だったらFavoritesタブに追加
974                 If favTab.Contains(post.StatusId) Then
975                     '取得済みなら非通知
976                     '_soundFile = ""
977                     add = False
978                 Else
979                     favTab.Add(post.StatusId, post.IsRead, True)
980                     If Not String.IsNullOrEmpty(favTab.SoundFile) AndAlso String.IsNullOrEmpty(_soundFile) Then _soundFile = favTab.SoundFile
981                     If favTab.Notify Then add = True
982                 End If
983             End If
984             If add Then _notifyPosts.Add(post)
985         Next
986         For Each tb As TabClass In _tabs.Values
987             If tb.IsInnerStorageTabType Then
988                 If tb.Notify Then
989                     If tb.GetTemporaryCount > 0 Then
990                         For Each post As PostClass In tb.GetTemporaryPosts
991                             Dim exist As Boolean = False
992                             For Each npost As PostClass In _notifyPosts
993                                 If npost.StatusId = post.StatusId Then
994                                     exist = True
995                                     Exit For
996                                 End If
997                             Next
998                             If Not exist Then _notifyPosts.Add(post)
999                         Next
1000                         If tb.SoundFile <> "" Then
1001                             If tb.TabType = TabUsageType.DirectMessage OrElse _soundFile = "" Then
1002                                 _soundFile = tb.SoundFile
1003                             End If
1004                         End If
1005                     End If
1006                 End If
1007             End If
1008         Next
1009     End Sub
1010
1011     Public Sub AddPost(ByVal Item As PostClass)
1012         SyncLock LockObj
1013             If Item.RelTabName = "" Then
1014                 If Not Item.IsDm Then
1015                     If _statuses.ContainsKey(Item.StatusId) Then
1016                         If Item.IsFav Then
1017                             If Item.RetweetedId = 0 Then
1018                                 _statuses.Item(Item.StatusId).IsFav = True
1019                             Else
1020                                 Item.IsFav = False
1021                             End If
1022                         Else
1023                             Exit Sub        '追加済みなら何もしない
1024                         End If
1025                     Else
1026                         If Item.IsFav AndAlso Item.RetweetedId > 0 Then Item.IsFav = False
1027                         _statuses.Add(Item.StatusId, Item)
1028                     End If
1029                     If Item.RetweetedId > 0 Then
1030                         Me.AddRetweet(Item)
1031                     End If
1032                     If Item.IsFav AndAlso _retweets.ContainsKey(Item.StatusId) Then
1033                         Exit Sub    'Fav済みのRetweet元発言は追加しない
1034                     End If
1035                     If _addedIds Is Nothing Then _addedIds = New List(Of Long) 'タブ追加用IDコレクション準備
1036                     _addedIds.Add(Item.StatusId)
1037                 Else
1038                     'DM
1039                     Dim tb As TabClass = Me.GetTabByType(TabUsageType.DirectMessage)
1040                     If tb.Contains(Item.StatusId) Then Exit Sub
1041                     tb.AddPostToInnerStorage(Item)
1042                 End If
1043             Else
1044                 '公式検索、リスト、関連発言の場合
1045                 Dim tb As TabClass
1046                 If Me.Tabs.ContainsKey(Item.RelTabName) Then
1047                     tb = Me.Tabs(Item.RelTabName)
1048                 Else
1049                     Exit Sub
1050                 End If
1051                 If tb Is Nothing Then Exit Sub
1052                 If tb.Contains(Item.StatusId) Then Exit Sub
1053                 'tb.Add(Item.StatusId, Item.IsRead, True)
1054                 tb.AddPostToInnerStorage(Item)
1055             End If
1056         End SyncLock
1057     End Sub
1058
1059     Private Sub AddRetweet(ByVal item As PostClass)
1060         If _retweets.ContainsKey(item.RetweetedId) Then Exit Sub
1061
1062         _retweets.Add( _
1063                     item.RetweetedId, _
1064                     New PostClass( _
1065                         item.Nickname, _
1066                         item.TextFromApi, _
1067                         item.Text, _
1068                         item.ImageUrl, _
1069                         item.ScreenName, _
1070                         item.CreatedAt, _
1071                         item.RetweetedId, _
1072                         item.IsFav, _
1073                         item.IsRead, _
1074                         item.IsReply, _
1075                         item.IsExcludeReply, _
1076                         item.IsProtect, _
1077                         item.IsOwl, _
1078                         item.IsMark, _
1079                         item.InReplyToUser, _
1080                         item.InReplyToStatusId, _
1081                         item.Source, _
1082                         item.SourceHtml, _
1083                         item.ReplyToList, _
1084                         item.IsMe, _
1085                         item.IsDm, _
1086                         item.UserId, _
1087                         item.FilterHit, _
1088                         "", _
1089                         0 _
1090                     ) _
1091                 )
1092     End Sub
1093
1094     Public Sub SetReadAllTab(ByVal Read As Boolean, ByVal TabName As String, ByVal Index As Integer)
1095         'Read:True=既読へ False=未読へ
1096         Dim tb As TabClass = _tabs(TabName)
1097
1098         If tb.UnreadManage = False Then Exit Sub '未読管理していなければ終了
1099
1100         Dim Id As Long = tb.GetId(Index)
1101         If Id < 0 Then Exit Sub
1102         Dim post As PostClass
1103         If Not tb.IsInnerStorageTabType Then
1104             post = _statuses(Id)
1105         Else
1106             post = tb.Posts(Id)
1107         End If
1108
1109         If post.IsRead = Read Then Exit Sub '状態変更なければ終了
1110
1111         post.IsRead = Read
1112
1113         SyncLock LockUnread
1114             If Read Then
1115                 tb.UnreadCount -= 1
1116                 Me.SetNextUnreadId(Id, tb)  '次の未読セット
1117                 '他タブの最古未読IDはタブ切り替え時に。
1118                 If tb.IsInnerStorageTabType Then
1119                     '一般タブ
1120                     If _statuses.ContainsKey(Id) AndAlso Not _statuses(Id).IsRead Then
1121                         For Each key As String In _tabs.Keys
1122                             If _tabs(key).UnreadManage AndAlso _
1123                                _tabs(key).Contains(Id) AndAlso _
1124                                Not _tabs(key).IsInnerStorageTabType Then
1125                                 _tabs(key).UnreadCount -= 1
1126                                 If _tabs(key).OldestUnreadId = Id Then _tabs(key).OldestUnreadId = -1
1127                             End If
1128                         Next
1129                         _statuses(Id).IsRead = True
1130                     End If
1131                 Else
1132                     '一般タブ
1133                     For Each key As String In _tabs.Keys
1134                         If key <> TabName AndAlso _
1135                            _tabs(key).UnreadManage AndAlso _
1136                            _tabs(key).Contains(Id) AndAlso _
1137                            Not _tabs(key).IsInnerStorageTabType Then
1138                             _tabs(key).UnreadCount -= 1
1139                             If _tabs(key).OldestUnreadId = Id Then _tabs(key).OldestUnreadId = -1
1140                         End If
1141                     Next
1142                 End If
1143                 '内部保存タブ
1144                 For Each key As String In _tabs.Keys
1145                     If key <> TabName AndAlso
1146                         _tabs(key).Contains(Id) AndAlso
1147                         _tabs(key).IsInnerStorageTabType AndAlso
1148                         Not _tabs(key).Posts(Id).IsRead Then
1149                         If _tabs(key).UnreadManage Then
1150                             _tabs(key).UnreadCount -= 1
1151                             If _tabs(key).OldestUnreadId = Id Then _tabs(key).OldestUnreadId = -1
1152                         End If
1153                         _tabs(key).Posts(Id).IsRead = True
1154                     End If
1155                 Next
1156             Else
1157                 tb.UnreadCount += 1
1158                 'If tb.OldestUnreadId > Id OrElse tb.OldestUnreadId = -1 Then tb.OldestUnreadId = Id
1159                 If tb.OldestUnreadId > Id Then tb.OldestUnreadId = Id
1160                 If tb.IsInnerStorageTabType Then
1161                     '一般タブ
1162                     If _statuses.ContainsKey(Id) AndAlso _statuses(Id).IsRead Then
1163                         For Each key As String In _tabs.Keys
1164                             If _tabs(key).UnreadManage AndAlso _
1165                                _tabs(key).Contains(Id) AndAlso _
1166                                Not _tabs(key).IsInnerStorageTabType Then
1167                                 _tabs(key).UnreadCount += 1
1168                                 If _tabs(key).OldestUnreadId > Id Then _tabs(key).OldestUnreadId = Id
1169                             End If
1170                         Next
1171                         _statuses(Id).IsRead = False
1172                     End If
1173                 Else
1174                     '一般タブ
1175                     For Each key As String In _tabs.Keys
1176                         If key <> TabName AndAlso _
1177                            _tabs(key).UnreadManage AndAlso _
1178                            _tabs(key).Contains(Id) AndAlso _
1179                            Not _tabs(key).IsInnerStorageTabType Then
1180                             _tabs(key).UnreadCount += 1
1181                             If _tabs(key).OldestUnreadId > Id Then _tabs(key).OldestUnreadId = Id
1182                         End If
1183                     Next
1184                 End If
1185                 '内部保存タブ
1186                 For Each key As String In _tabs.Keys
1187                     If key <> TabName AndAlso
1188                         _tabs(key).Contains(Id) AndAlso
1189                         _tabs(key).IsInnerStorageTabType AndAlso
1190                         _tabs(key).Posts(Id).IsRead Then
1191                         If _tabs(key).UnreadManage Then
1192                             _tabs(key).UnreadCount += 1
1193                             If _tabs(key).OldestUnreadId > Id Then _tabs(key).OldestUnreadId = Id
1194                         End If
1195                         _tabs(key).Posts(Id).IsRead = False
1196                     End If
1197                 Next
1198             End If
1199         End SyncLock
1200     End Sub
1201
1202     ''' TODO: パフォーマンスを勘案して、戻すか決める
1203     Public Sub SetRead(ByVal Read As Boolean, ByVal TabName As String, ByVal Index As Integer)
1204         'Read:True=既読へ False=未読へ
1205         Dim tb As TabClass = _tabs(TabName)
1206
1207         If tb.UnreadManage = False Then Exit Sub '未読管理していなければ終了
1208
1209         Dim Id As Long = tb.GetId(Index)
1210         If Id < 0 Then Exit Sub
1211         Dim post As PostClass
1212         If Not tb.IsInnerStorageTabType Then
1213             post = _statuses(Id)
1214         Else
1215             post = tb.Posts(Id)
1216         End If
1217
1218         If post.IsRead = Read Then Exit Sub '状態変更なければ終了
1219
1220         post.IsRead = Read '指定の状態に変更
1221
1222         SyncLock LockUnread
1223             If Read Then
1224                 tb.UnreadCount -= 1
1225                 Me.SetNextUnreadId(Id, tb)  '次の未読セット
1226                 '他タブの最古未読IDはタブ切り替え時に。
1227                 If tb.IsInnerStorageTabType Then Exit Sub
1228                 For Each key As String In _tabs.Keys
1229                     If key <> TabName AndAlso _
1230                        _tabs(key).UnreadManage AndAlso _
1231                        _tabs(key).Contains(Id) AndAlso _
1232                        Not _tabs(key).IsInnerStorageTabType Then
1233                         _tabs(key).UnreadCount -= 1
1234                         If _tabs(key).OldestUnreadId = Id Then _tabs(key).OldestUnreadId = -1
1235                     End If
1236                 Next
1237             Else
1238                 tb.UnreadCount += 1
1239                 'If tb.OldestUnreadId > Id OrElse tb.OldestUnreadId = -1 Then tb.OldestUnreadId = Id
1240                 If tb.OldestUnreadId > Id Then tb.OldestUnreadId = Id
1241                 If tb.IsInnerStorageTabType Then Exit Sub
1242                 For Each key As String In _tabs.Keys
1243                     If Not key = TabName AndAlso _
1244                        _tabs(key).UnreadManage AndAlso _
1245                        _tabs(key).Contains(Id) AndAlso _
1246                        Not _tabs(key).IsInnerStorageTabType Then
1247                         _tabs(key).UnreadCount += 1
1248                         If _tabs(key).OldestUnreadId > Id Then _tabs(key).OldestUnreadId = Id
1249                     End If
1250                 Next
1251             End If
1252         End SyncLock
1253     End Sub
1254
1255     Public Sub SetRead()
1256         Dim tb As TabClass = GetTabByType(TabUsageType.Home)
1257         If tb.UnreadManage = False Then Exit Sub
1258
1259         SyncLock LockObj
1260             For i As Integer = 0 To tb.AllCount - 1
1261                 Dim id As Long = tb.GetId(i)
1262                 If id < 0 Then Exit Sub
1263                 If Not _statuses(id).IsReply AndAlso _
1264                    Not _statuses(id).IsRead AndAlso _
1265                    Not _statuses(id).FilterHit Then
1266                     _statuses(id).IsRead = True
1267                     Me.SetNextUnreadId(id, tb)  '次の未読セット
1268                     For Each key As String In _tabs.Keys
1269                         If _tabs(key).UnreadManage AndAlso _
1270                            _tabs(key).Contains(id) Then
1271                             _tabs(key).UnreadCount -= 1
1272                             If _tabs(key).OldestUnreadId = id Then _tabs(key).OldestUnreadId = -1
1273                         End If
1274                     Next
1275                 End If
1276             Next
1277         End SyncLock
1278     End Sub
1279
1280     Public ReadOnly Property Item(ByVal ID As Long) As PostClass
1281         Get
1282             If _statuses.ContainsKey(ID) Then Return _statuses(ID)
1283             For Each tb As TabClass In Me.GetTabsInnerStorageType
1284                 If tb.Contains(ID) Then
1285                     Return tb.Posts(ID)
1286                 End If
1287             Next
1288             Return Nothing
1289         End Get
1290     End Property
1291
1292     Public ReadOnly Property Item(ByVal TabName As String, ByVal Index As Integer) As PostClass
1293         Get
1294             If Not _tabs.ContainsKey(TabName) Then Throw New ArgumentException("TabName=" + TabName + " is not contained.")
1295             Dim id As Long = _tabs(TabName).GetId(Index)
1296             If id < 0 Then Throw New ArgumentException("Index can't find. Index=" + Index.ToString + "/TabName=" + TabName)
1297             Try
1298                 If _tabs(TabName).IsInnerStorageTabType Then
1299                     Return _tabs(TabName).Posts(_tabs(TabName).GetId(Index))
1300                 Else
1301                     Return _statuses(_tabs(TabName).GetId(Index))
1302                 End If
1303             Catch ex As Exception
1304                 Throw New Exception("Index=" + Index.ToString + "/TabName=" + TabName, ex)
1305             End Try
1306         End Get
1307     End Property
1308
1309     Public ReadOnly Property Item(ByVal TabName As String, ByVal StartIndex As Integer, ByVal EndIndex As Integer) As PostClass()
1310         Get
1311             Dim length As Integer = EndIndex - StartIndex + 1
1312             Dim posts() As PostClass = New PostClass(length - 1) {}
1313             If _tabs(TabName).IsInnerStorageTabType Then
1314                 For i As Integer = 0 To length - 1
1315                     posts(i) = _tabs(TabName).Posts(_tabs(TabName).GetId(StartIndex + i))
1316                 Next i
1317             Else
1318                 For i As Integer = 0 To length - 1
1319                     posts(i) = _statuses(_tabs(TabName).GetId(StartIndex + i))
1320                 Next i
1321             End If
1322             Return posts
1323         End Get
1324     End Property
1325
1326     'Public ReadOnly Property ItemCount() As Integer
1327     '    Get
1328     '        SyncLock LockObj
1329     '            Return _statuses.Count   'DM,公式検索は除く
1330     '        End SyncLock
1331     '    End Get
1332     'End Property
1333
1334     Public Function ContainsKey(ByVal Id As Long) As Boolean
1335         'DM,公式検索は非対応
1336         SyncLock LockObj
1337             Return _statuses.ContainsKey(Id)
1338         End SyncLock
1339     End Function
1340
1341     Public Function ContainsKey(ByVal Id As Long, ByVal TabName As String) As Boolean
1342         'DM,公式検索は対応版
1343         SyncLock LockObj
1344             If _tabs.ContainsKey(TabName) Then
1345                 Return _tabs(TabName).Contains(Id)
1346             Else
1347                 Return False
1348             End If
1349         End SyncLock
1350     End Function
1351
1352     Public Sub SetUnreadManage(ByVal Manage As Boolean)
1353         If Manage Then
1354             For Each key As String In _tabs.Keys
1355                 Dim tb As TabClass = _tabs(key)
1356                 If tb.UnreadManage Then
1357                     SyncLock LockUnread
1358                         Dim cnt As Integer = 0
1359                         Dim oldest As Long = Long.MaxValue
1360                         Dim posts As Dictionary(Of Long, PostClass)
1361                         If Not tb.IsInnerStorageTabType Then
1362                             posts = _statuses
1363                         Else
1364                             posts = tb.Posts
1365                         End If
1366                         For Each id As Long In tb.BackupIds
1367                             If Not posts(id).IsRead Then
1368                                 cnt += 1
1369                                 If oldest > id Then oldest = id
1370                             End If
1371                         Next
1372                         If oldest = Long.MaxValue Then oldest = -1
1373                         tb.OldestUnreadId = oldest
1374                         tb.UnreadCount = cnt
1375                     End SyncLock
1376                 End If
1377             Next
1378         Else
1379             For Each key As String In _tabs.Keys
1380                 Dim tb As TabClass = _tabs(key)
1381                 If tb.UnreadManage AndAlso tb.UnreadCount > 0 Then
1382                     SyncLock LockUnread
1383                         tb.UnreadCount = 0
1384                         tb.OldestUnreadId = -1
1385                     End SyncLock
1386                 End If
1387             Next
1388         End If
1389     End Sub
1390
1391     Public Sub RenameTab(ByVal Original As String, ByVal NewName As String)
1392         Dim tb As TabClass = _tabs(Original)
1393         _tabs.Remove(Original)
1394         tb.TabName = NewName
1395         _tabs.Add(NewName, tb)
1396     End Sub
1397
1398     Public Sub FilterAll()
1399         SyncLock LockObj
1400             Dim tbr As TabClass = GetTabByType(TabUsageType.Home)
1401             Dim replyTab As TabClass = GetTabByType(TabUsageType.Mentions)
1402             For Each key As String In _tabs.Keys
1403                 Dim tb As TabClass = _tabs(key)
1404                 If tb.FilterModified Then
1405                     tb.FilterModified = False
1406                     Dim orgIds() As Long = tb.BackupIds()
1407                     tb.ClearIDs()
1408                     ''''''''''''''フィルター前のIDsを退避。どのタブにも含まれないidはrecentへ追加
1409                     ''''''''''''''moveフィルターにヒットした際、recentに該当あればrecentから削除
1410                     For Each id As Long In _statuses.Keys
1411                         Dim post As PostClass = _statuses.Item(id)
1412                         If post.IsDm Then Continue For
1413                         Dim rslt As HITRESULT = HITRESULT.None
1414                         rslt = tb.AddFiltered(post)
1415                         Select Case rslt
1416                             Case HITRESULT.CopyAndMark
1417                                 post.IsMark = True 'マークあり
1418                                 post.FilterHit = True
1419                             Case HITRESULT.Move
1420                                 tbr.Remove(post.StatusId, post.IsRead)
1421                                 post.IsMark = False
1422                                 post.FilterHit = True
1423                             Case HITRESULT.Copy
1424                                 post.IsMark = False
1425                                 post.FilterHit = True
1426                             Case HITRESULT.Exclude
1427                                 If key = replyTab.TabName AndAlso post.IsReply Then post.IsExcludeReply = True
1428                                 If post.IsFav Then GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, True)
1429                                 post.FilterHit = False
1430                             Case HITRESULT.None
1431                                 If key = replyTab.TabName AndAlso post.IsReply Then replyTab.Add(post.StatusId, post.IsRead, True)
1432                                 If post.IsFav Then GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, True)
1433                                 post.FilterHit = False
1434                         End Select
1435                     Next
1436                     tb.AddSubmit()  '振分確定
1437                     For Each id As Long In orgIds
1438                         Dim hit As Boolean = False
1439                         For Each tkey As String In _tabs.Keys
1440                             If _tabs(tkey).Contains(id) Then
1441                                 hit = True
1442                                 Exit For
1443                             End If
1444                         Next
1445                         If Not hit Then tbr.Add(id, _statuses(id).IsRead, False)
1446                     Next
1447                 End If
1448             Next
1449             Me.SortPosts()
1450         End SyncLock
1451     End Sub
1452
1453     Public Function GetId(ByVal TabName As String, ByVal IndexCollection As ListView.SelectedIndexCollection) As Long()
1454         If IndexCollection.Count = 0 Then Return Nothing
1455
1456         Dim tb As TabClass = _tabs(TabName)
1457         Dim Ids(IndexCollection.Count - 1) As Long
1458         For i As Integer = 0 To Ids.Length - 1
1459             Ids(i) = tb.GetId(IndexCollection(i))
1460         Next
1461         Return Ids
1462     End Function
1463
1464     Public Function GetId(ByVal TabName As String, ByVal Index As Integer) As Long
1465         Return _tabs(TabName).GetId(Index)
1466     End Function
1467
1468     Public Function IndexOf(ByVal TabName As String, ByVal Ids() As Long) As Integer()
1469         If Ids Is Nothing Then Return Nothing
1470         Dim idx(Ids.Length - 1) As Integer
1471         Dim tb As TabClass = _tabs(TabName)
1472         For i As Integer = 0 To Ids.Length - 1
1473             idx(i) = tb.IndexOf(Ids(i))
1474         Next
1475         Return idx
1476     End Function
1477
1478     Public Function IndexOf(ByVal TabName As String, ByVal Id As Long) As Integer
1479         Return _tabs(TabName).IndexOf(Id)
1480     End Function
1481
1482     Public Sub ClearTabIds(ByVal TabName As String)
1483         '不要なPostを削除
1484         SyncLock LockObj
1485             If Not _tabs(TabName).IsInnerStorageTabType Then
1486                 For Each Id As Long In _tabs(TabName).BackupIds
1487                     Dim Hit As Boolean = False
1488                     For Each tb As TabClass In _tabs.Values
1489                         If tb.Contains(Id) Then
1490                             Hit = True
1491                             Exit For
1492                         End If
1493                     Next
1494                     If Not Hit Then _statuses.Remove(Id)
1495                 Next
1496             End If
1497
1498             '指定タブをクリア
1499             _tabs(TabName).ClearIDs()
1500         End SyncLock
1501     End Sub
1502
1503     Public Sub SetTabUnreadManage(ByVal TabName As String, ByVal Manage As Boolean)
1504         Dim tb As TabClass = _tabs(TabName)
1505         SyncLock LockUnread
1506             If Manage Then
1507                 Dim cnt As Integer = 0
1508                 Dim oldest As Long = Long.MaxValue
1509                 Dim posts As Dictionary(Of Long, PostClass)
1510                 If Not tb.IsInnerStorageTabType Then
1511                     posts = _statuses
1512                 Else
1513                     posts = tb.Posts
1514                 End If
1515                 For Each id As Long In tb.BackupIds
1516                     If Not posts(id).IsRead Then
1517                         cnt += 1
1518                         If oldest > id Then oldest = id
1519                     End If
1520                 Next
1521                 If oldest = Long.MaxValue Then oldest = -1
1522                 tb.OldestUnreadId = oldest
1523                 tb.UnreadCount = cnt
1524             Else
1525                 tb.OldestUnreadId = -1
1526                 tb.UnreadCount = 0
1527             End If
1528         End SyncLock
1529         tb.UnreadManage = Manage
1530     End Sub
1531
1532     Public Sub RefreshOwl(ByVal follower As List(Of Long))
1533         SyncLock LockObj
1534             If follower.Count > 0 Then
1535                 For Each post As PostClass In _statuses.Values
1536                     'If post.UserId = 0 OrElse post.IsDm Then Continue For
1537                     If post.IsMe Then
1538                         post.IsOwl = False
1539                     Else
1540                         post.IsOwl = Not follower.Contains(post.UserId)
1541                     End If
1542                 Next
1543             Else
1544                 For Each id As Long In _statuses.Keys
1545                     _statuses(id).IsOwl = False
1546                 Next
1547             End If
1548         End SyncLock
1549     End Sub
1550
1551     Public Function GetTabByType(ByVal tabType As TabUsageType) As TabClass
1552         'Home,Mentions,DM,Favは1つに制限する
1553         'その他のタイプを指定されたら、最初に合致したものを返す
1554         '合致しなければNothingを返す
1555         SyncLock LockObj
1556             For Each tb As TabClass In _tabs.Values
1557                 If tb IsNot Nothing AndAlso tb.TabType = tabType Then Return tb
1558             Next
1559             Return Nothing
1560         End SyncLock
1561     End Function
1562
1563     Public Function GetTabsByType(ByVal tabType As TabUsageType) As List(Of TabClass)
1564         '合致したタブをListで返す
1565         '合致しなければ空のListを返す
1566         SyncLock LockObj
1567             Dim tbs As New List(Of TabClass)
1568             For Each tb As TabClass In _tabs.Values
1569                 If (tabType And tb.TabType) = tb.TabType Then tbs.Add(tb)
1570             Next
1571             Return tbs
1572         End SyncLock
1573     End Function
1574
1575     Public Function GetTabsInnerStorageType() As List(Of TabClass)
1576         '合致したタブをListで返す
1577         '合致しなければ空のListを返す
1578         SyncLock LockObj
1579             Dim tbs As New List(Of TabClass)
1580             For Each tb As TabClass In _tabs.Values
1581                 If tb.IsInnerStorageTabType Then tbs.Add(tb)
1582             Next
1583             Return tbs
1584         End SyncLock
1585     End Function
1586
1587     Public Function GetTabByName(ByVal tabName As String) As TabClass
1588         SyncLock LockObj
1589             If _tabs.ContainsKey(tabName) Then Return _tabs(tabName)
1590             Return Nothing
1591         End SyncLock
1592     End Function
1593
1594     ' デフォルトタブの判定処理
1595     Public Function IsDefaultTab(ByVal tabName As String) As Boolean
1596         If tabName IsNot Nothing AndAlso _
1597            _tabs.ContainsKey(tabName) AndAlso _
1598            (_tabs(tabName).TabType = TabUsageType.Home OrElse _
1599            _tabs(tabName).TabType = TabUsageType.Mentions OrElse _
1600            _tabs(tabName).TabType = TabUsageType.DirectMessage OrElse _
1601            _tabs(tabName).TabType = TabUsageType.Favorites) Then
1602             Return True
1603         Else
1604             Return False
1605         End If
1606     End Function
1607
1608     '振り分け可能タブの判定処理
1609     Public Function IsDistributableTab(ByVal tabName As String) As Boolean
1610         Return tabName IsNot Nothing AndAlso
1611             _tabs.ContainsKey(tabName) AndAlso
1612             (_tabs(tabName).TabType = TabUsageType.Mentions OrElse
1613              _tabs(tabName).TabType = TabUsageType.UserDefined)
1614     End Function
1615
1616     Public Function GetUniqueTabName() As String
1617         Dim tabNameTemp As String = "MyTab" + (_tabs.Count + 1).ToString
1618         For i As Integer = 2 To 100
1619             If _tabs.ContainsKey(tabNameTemp) Then
1620                 tabNameTemp = "MyTab" + (_tabs.Count + i).ToString
1621             Else
1622                 Exit For
1623             End If
1624         Next
1625         Return tabNameTemp
1626     End Function
1627
1628     Public ReadOnly Property Posts() As Dictionary(Of Long, PostClass)
1629         Get
1630             Return _statuses
1631         End Get
1632     End Property
1633 End Class
1634
1635 <Serializable()> _
1636 Public NotInheritable Class TabClass
1637     Private _unreadManage As Boolean = False
1638     Private _filters As List(Of FiltersClass)
1639     Private _unreadCount As Integer = 0
1640     Private _ids As List(Of Long)
1641     Private _tmpIds As New List(Of TemporaryId)
1642     Private _tabType As TabUsageType = TabUsageType.Undefined
1643     Private _sorter As New IdComparerClass
1644
1645     Private ReadOnly _lockObj As New Object
1646
1647     Public Property User As String
1648
1649 #Region "検索"
1650     'Search query
1651     Private _searchLang As String = ""
1652     Private _searchWords As String = ""
1653     Private _nextPageQuery As String = ""
1654
1655     Public Property SearchLang() As String
1656         Get
1657             Return _searchLang
1658         End Get
1659         Set(ByVal value As String)
1660             _SinceId = 0
1661             _searchLang = value
1662         End Set
1663     End Property
1664     Public Property SearchWords() As String
1665         Get
1666             Return _searchWords
1667         End Get
1668         Set(ByVal value As String)
1669             _SinceId = 0
1670             _searchWords = value.Trim
1671         End Set
1672     End Property
1673
1674     Public Property NextPageQuery() As String
1675         Get
1676             Return _nextPageQuery
1677         End Get
1678         Set(ByVal value As String)
1679             _nextPageQuery = value
1680         End Set
1681     End Property
1682
1683     Public Function GetSearchPage(ByVal count As Integer) As Integer
1684         Return ((_ids.Count \ count) + 1)
1685     End Function
1686     Private _beforeQuery As New Dictionary(Of String, String)
1687     Public Sub SaveQuery(ByVal more As Boolean)
1688         Dim qry As New Dictionary(Of String, String)
1689         If String.IsNullOrEmpty(_searchWords) Then
1690             _beforeQuery = qry
1691             Exit Sub
1692         End If
1693         qry.Add("q", _searchWords)
1694         If Not String.IsNullOrEmpty(_searchLang) Then qry.Add("lang", _searchLang)
1695         _beforeQuery = qry
1696     End Sub
1697
1698     Public Function IsQueryChanged() As Boolean
1699         Dim qry As New Dictionary(Of String, String)
1700         If Not String.IsNullOrEmpty(_searchWords) Then
1701             qry.Add("q", _searchWords)
1702             If Not String.IsNullOrEmpty(_searchLang) Then qry.Add("lang", _searchLang)
1703         End If
1704         If qry.Count <> _beforeQuery.Count Then Return True
1705
1706         For Each kvp As KeyValuePair(Of String, String) In qry
1707             If Not _beforeQuery.ContainsKey(kvp.Key) OrElse _beforeQuery(kvp.Key) <> kvp.Value Then
1708                 Return True
1709             End If
1710         Next
1711     End Function
1712 #End Region
1713
1714 #Region "リスト"
1715     Private _listInfo As ListElement
1716     Public Property ListInfo() As ListElement
1717         Get
1718             Return _listInfo
1719         End Get
1720         Set(ByVal value As ListElement)
1721             _listInfo = value
1722         End Set
1723     End Property
1724 #End Region
1725
1726     <Xml.Serialization.XmlIgnore()> _
1727     Public Property RelationTargetPost() As PostClass
1728
1729     <Xml.Serialization.XmlIgnore()> _
1730     Public Property OldestId() As Long = Long.MaxValue
1731
1732     <Xml.Serialization.XmlIgnore()> _
1733     Public Property SinceId() As Long
1734
1735     <Xml.Serialization.XmlIgnore()> _
1736     Public Property Posts() As New Dictionary(Of Long, PostClass)
1737
1738     Public Function GetTemporaryPosts() As PostClass()
1739         Dim tempPosts As New List(Of PostClass)
1740         If _tmpIds.Count = 0 Then Return tempPosts.ToArray
1741         For Each tempId As TemporaryId In _tmpIds
1742             tempPosts.Add(_Posts(tempId.Id))
1743         Next
1744         Return tempPosts.ToArray
1745     End Function
1746
1747     Public Function GetTemporaryCount() As Integer
1748         Return _tmpIds.Count
1749     End Function
1750
1751     Private Structure TemporaryId
1752         Public Id As Long
1753         Public Read As Boolean
1754
1755         Public Sub New(ByVal argId As Long, ByVal argRead As Boolean)
1756             Id = argId
1757             Read = argRead
1758         End Sub
1759     End Structure
1760
1761     Public Sub New()
1762         _filters = New List(Of FiltersClass)
1763         _Notify = True
1764         _SoundFile = ""
1765         _unreadManage = True
1766         _ids = New List(Of Long)
1767         Me.OldestUnreadId = -1
1768         _tabType = TabUsageType.Undefined
1769         _listInfo = Nothing
1770     End Sub
1771
1772     Public Sub New(ByVal TabName As String, ByVal TabType As TabUsageType, ByVal list As ListElement)
1773         _TabName = TabName
1774         _filters = New List(Of FiltersClass)
1775         _Notify = True
1776         _SoundFile = ""
1777         _unreadManage = True
1778         _ids = New List(Of Long)
1779         Me.OldestUnreadId = -1
1780         _tabType = TabType
1781         Me.ListInfo = list
1782         If Me.IsInnerStorageTabType Then
1783             _sorter.posts = _Posts
1784         Else
1785             _sorter.posts = TabInformations.GetInstance.Posts
1786         End If
1787     End Sub
1788
1789     Public Sub Sort()
1790         If _sorter.Mode = IdComparerClass.ComparerMode.Id Then
1791             _ids.Sort(_sorter.CmpMethod)
1792             Exit Sub
1793         End If
1794         Dim ar() As Long = Nothing
1795         If _sorter.Order = SortOrder.Ascending Then
1796             Select Case _sorter.Mode
1797                 Case IdComparerClass.ComparerMode.Data
1798                     ar = _ids.OrderBy(Function(n) _sorter.posts(n).TextFromApi).ToArray
1799                 Case IdComparerClass.ComparerMode.Name
1800                     ar = _ids.OrderBy(Function(n) _sorter.posts(n).ScreenName).ToArray
1801                 Case IdComparerClass.ComparerMode.Nickname
1802                     ar = _ids.OrderBy(Function(n) _sorter.posts(n).Nickname).ToArray
1803                 Case IdComparerClass.ComparerMode.Source
1804                     ar = _ids.OrderBy(Function(n) _sorter.posts(n).Source).ToArray
1805             End Select
1806         Else
1807             Select Case _sorter.Mode
1808                 Case IdComparerClass.ComparerMode.Data
1809                     ar = _ids.OrderByDescending(Function(n) _sorter.posts(n).TextFromApi).ToArray
1810                 Case IdComparerClass.ComparerMode.Name
1811                     ar = _ids.OrderByDescending(Function(n) _sorter.posts(n).ScreenName).ToArray
1812                 Case IdComparerClass.ComparerMode.Nickname
1813                     ar = _ids.OrderByDescending(Function(n) _sorter.posts(n).Nickname).ToArray
1814                 Case IdComparerClass.ComparerMode.Source
1815                     ar = _ids.OrderByDescending(Function(n) _sorter.posts(n).Source).ToArray
1816             End Select
1817         End If
1818         _ids = New List(Of Long)(ar)
1819     End Sub
1820
1821     Public ReadOnly Property Sorter() As IdComparerClass
1822         Get
1823             Return _sorter
1824         End Get
1825     End Property
1826
1827     '無条件に追加
1828     Private Sub Add(ByVal ID As Long, ByVal Read As Boolean)
1829         If Me._ids.Contains(ID) Then Exit Sub
1830
1831         If Me.Sorter.Mode = IdComparerClass.ComparerMode.Id Then
1832             If Me.Sorter.Order = SortOrder.Ascending Then
1833                 Me._ids.Add(ID)
1834             Else
1835                 Me._ids.Insert(0, ID)
1836             End If
1837         Else
1838             Me._ids.Add(ID)
1839         End If
1840
1841         If Not Read AndAlso Me._unreadManage Then
1842             Me._unreadCount += 1
1843             If ID < Me.OldestUnreadId Then Me.OldestUnreadId = ID
1844             'If Me.OldestUnreadId = -1 Then
1845             '    Me.OldestUnreadId = ID
1846             'Else
1847             '    If ID < Me.OldestUnreadId Then Me.OldestUnreadId = ID
1848             'End If
1849         End If
1850     End Sub
1851
1852     Public Sub Add(ByVal ID As Long, ByVal Read As Boolean, ByVal Temporary As Boolean)
1853         If Not Temporary Then
1854             Me.Add(ID, Read)
1855         Else
1856             _tmpIds.Add(New TemporaryId(ID, Read))
1857         End If
1858     End Sub
1859
1860     'フィルタに合致したら追加
1861     Public Function AddFiltered(ByVal post As PostClass) As HITRESULT
1862         If Me.IsInnerStorageTabType Then Return HITRESULT.None
1863
1864         Dim rslt As HITRESULT = HITRESULT.None
1865         '全フィルタ評価(優先順位あり)
1866         SyncLock Me._lockObj
1867             For Each ft As FiltersClass In _filters
1868                 Select Case ft.IsHit(post)   'フィルタクラスでヒット判定
1869                     Case HITRESULT.None
1870                     Case HITRESULT.Copy
1871                         If rslt <> HITRESULT.CopyAndMark Then rslt = HITRESULT.Copy
1872                     Case HITRESULT.CopyAndMark
1873                         rslt = HITRESULT.CopyAndMark
1874                     Case HITRESULT.Move
1875                         rslt = HITRESULT.Move
1876                     Case HITRESULT.Exclude
1877                         rslt = HITRESULT.Exclude
1878                         Exit For
1879                 End Select
1880             Next
1881         End SyncLock
1882
1883         If rslt <> HITRESULT.None AndAlso rslt <> HITRESULT.Exclude Then
1884             _tmpIds.Add(New TemporaryId(post.StatusId, post.IsRead))
1885         End If
1886
1887         Return rslt 'マーク付けは呼び出し元で行うこと
1888     End Function
1889
1890     '検索結果の追加
1891     Public Sub AddPostToInnerStorage(ByVal Post As PostClass)
1892         If _Posts.ContainsKey(Post.StatusId) Then Exit Sub
1893         _Posts.Add(Post.StatusId, Post)
1894         _tmpIds.Add(New TemporaryId(Post.StatusId, Post.IsRead))
1895     End Sub
1896
1897     Public Sub AddSubmit(ByRef isMentionIncluded As Boolean)
1898         If _tmpIds.Count = 0 Then Exit Sub
1899         _tmpIds.Sort(Function(x As TemporaryId, y As TemporaryId) x.Id.CompareTo(y.Id))
1900         For Each tId As TemporaryId In _tmpIds
1901             If Me.TabType = TabUsageType.Mentions AndAlso TabInformations.GetInstance.Item(tId.Id).IsReply Then isMentionIncluded = True
1902             Me.Add(tId.Id, tId.Read)
1903         Next
1904         _tmpIds.Clear()
1905     End Sub
1906
1907     Public Sub AddSubmit()
1908         Dim mention As Boolean
1909         AddSubmit(mention)
1910     End Sub
1911
1912     Public Sub Remove(ByVal Id As Long)
1913         If Not Me._ids.Contains(Id) Then Exit Sub
1914         Me._ids.Remove(Id)
1915         If Me.IsInnerStorageTabType Then _Posts.Remove(Id)
1916     End Sub
1917
1918     Public Sub Remove(ByVal Id As Long, ByVal Read As Boolean)
1919         If Not Me._ids.Contains(Id) Then Exit Sub
1920
1921         If Not Read AndAlso Me._unreadManage Then
1922             Me._unreadCount -= 1
1923             Me.OldestUnreadId = -1
1924         End If
1925
1926         Me._ids.Remove(Id)
1927         If Me.IsInnerStorageTabType Then _Posts.Remove(Id)
1928     End Sub
1929
1930     Public Property UnreadManage() As Boolean
1931         Get
1932             Return _unreadManage
1933         End Get
1934         Set(ByVal value As Boolean)
1935             Me._unreadManage = value
1936             If Not value Then
1937                 Me.OldestUnreadId = -1
1938                 Me._unreadCount = 0
1939             End If
1940         End Set
1941     End Property
1942
1943     Public Property Notify() As Boolean
1944
1945     Public Property SoundFile() As String = ""
1946
1947     <Xml.Serialization.XmlIgnore()> _
1948     Public Property OldestUnreadId() As Long = -1
1949
1950     <Xml.Serialization.XmlIgnore()> _
1951     Public Property UnreadCount() As Integer
1952         Get
1953             Return If(Me.UnreadManage AndAlso AppendSettingDialog.Instance.UnreadManage, _unreadCount, 0)
1954         End Get
1955         Set(ByVal value As Integer)
1956             If value < 0 Then value = 0
1957             _unreadCount = value
1958         End Set
1959     End Property
1960
1961     Public ReadOnly Property AllCount() As Integer
1962         Get
1963             Return Me._ids.Count
1964         End Get
1965     End Property
1966
1967     Public Function GetFilters() As FiltersClass()
1968         SyncLock Me._lockObj
1969             Return _filters.ToArray()
1970         End SyncLock
1971     End Function
1972
1973     Public Sub RemoveFilter(ByVal filter As FiltersClass)
1974         SyncLock Me._lockObj
1975             _filters.Remove(filter)
1976             Me.FilterModified = True
1977         End SyncLock
1978     End Sub
1979
1980     Public Function AddFilter(ByVal filter As FiltersClass) As Boolean
1981         SyncLock Me._lockObj
1982             If _filters.Contains(filter) Then Return False
1983             _filters.Add(filter)
1984             Me.FilterModified = True
1985             Return True
1986         End SyncLock
1987     End Function
1988
1989     Public Sub EditFilter(ByVal original As FiltersClass, ByVal modified As FiltersClass)
1990         original.BodyFilter = modified.BodyFilter
1991         original.NameFilter = modified.NameFilter
1992         original.SearchBoth = modified.SearchBoth
1993         original.SearchUrl = modified.SearchUrl
1994         original.UseRegex = modified.UseRegex
1995         original.CaseSensitive = modified.CaseSensitive
1996         original.IsRt = modified.IsRt
1997         original.UseLambda = modified.UseLambda
1998         original.Source = modified.Source
1999         original.ExBodyFilter = modified.ExBodyFilter
2000         original.ExNameFilter = modified.ExNameFilter
2001         original.ExSearchBoth = modified.ExSearchBoth
2002         original.ExSearchUrl = modified.ExSearchUrl
2003         original.ExUseRegex = modified.ExUseRegex
2004         original.ExCaseSensitive = modified.ExCaseSensitive
2005         original.IsExRt = modified.IsExRt
2006         original.ExUseLambda = modified.ExUseLambda
2007         original.ExSource = modified.ExSource
2008         original.MoveFrom = modified.MoveFrom
2009         original.SetMark = modified.SetMark
2010         Me.FilterModified = True
2011     End Sub
2012
2013     <Xml.Serialization.XmlIgnore()> _
2014     Public Property Filters() As List(Of FiltersClass)
2015         Get
2016             SyncLock Me._lockObj
2017                 Return _filters
2018             End SyncLock
2019         End Get
2020         Set(ByVal value As List(Of FiltersClass))
2021             SyncLock Me._lockObj
2022                 _filters = value
2023             End SyncLock
2024         End Set
2025     End Property
2026
2027     Public Property FilterArray() As FiltersClass()
2028         Get
2029             SyncLock Me._lockObj
2030                 Return _filters.ToArray
2031             End SyncLock
2032         End Get
2033         Set(ByVal value As FiltersClass())
2034             SyncLock Me._lockObj
2035                 For Each filters As FiltersClass In value
2036                     _filters.Add(filters)
2037                 Next
2038             End SyncLock
2039         End Set
2040     End Property
2041     Public Function Contains(ByVal ID As Long) As Boolean
2042         Return _ids.Contains(ID)
2043     End Function
2044
2045     Public Sub ClearIDs()
2046         _ids.Clear()
2047         _tmpIds.Clear()
2048         _unreadCount = 0
2049         Me.OldestUnreadId = -1
2050         If _Posts IsNot Nothing Then
2051             _Posts.Clear()
2052         End If
2053     End Sub
2054
2055     Public Function GetId(ByVal Index As Integer) As Long
2056         Return If(Index < _ids.Count, _ids(Index), -1)
2057     End Function
2058
2059     Public Function IndexOf(ByVal ID As Long) As Integer
2060         Return _ids.IndexOf(ID)
2061     End Function
2062
2063     <Xml.Serialization.XmlIgnore()> _
2064     Public Property FilterModified() As Boolean
2065
2066     Public Function BackupIds() As Long()
2067         Return _ids.ToArray()
2068     End Function
2069
2070     Public Property TabName() As String = ""
2071
2072     Public Property TabType() As TabUsageType
2073         Get
2074             Return _tabType
2075         End Get
2076         Set(ByVal value As TabUsageType)
2077             _tabType = value
2078             If Me.IsInnerStorageTabType Then
2079                 _sorter.posts = _Posts
2080             Else
2081                 _sorter.posts = TabInformations.GetInstance.Posts
2082             End If
2083         End Set
2084     End Property
2085
2086     Public ReadOnly Property IsInnerStorageTabType As Boolean
2087         Get
2088             If _tabType = TabUsageType.PublicSearch OrElse
2089                 _tabType = TabUsageType.DirectMessage OrElse
2090                 _tabType = TabUsageType.Lists OrElse
2091                 _tabType = TabUsageType.UserTimeline OrElse
2092                 _tabType = TabUsageType.Related Then
2093                 Return True
2094             Else
2095                 Return False
2096             End If
2097         End Get
2098     End Property
2099 End Class
2100
2101 <Serializable()> _
2102 Public NotInheritable Class FiltersClass
2103     Implements System.IEquatable(Of FiltersClass)
2104     Private _name As String = ""
2105     Private _body As New List(Of String)
2106     Private _searchBoth As Boolean = True
2107     Private _searchUrl As Boolean = False
2108     Private _caseSensitive As Boolean = False
2109     Private _useRegex As Boolean = False
2110     Private _isRt As Boolean = False
2111     Private _source As String = ""
2112     Private _exname As String = ""
2113     Private _exbody As New List(Of String)
2114     Private _exsearchBoth As Boolean = True
2115     Private _exsearchUrl As Boolean = False
2116     Private _exuseRegex As Boolean = False
2117     Private _excaseSensitive As Boolean = False
2118     Private _isExRt As Boolean = False
2119     Private _exSource As String = ""
2120     Private _moveFrom As Boolean = False
2121     Private _setMark As Boolean = True
2122     Private _useLambda As Boolean = False
2123     Private _exuseLambda As Boolean = False
2124
2125     ' ラムダ式コンパイルキャッシュ
2126     Private _lambdaExp As LambdaExpression = Nothing
2127     Private _lambdaExpDelegate As [Delegate] = Nothing
2128     Private _exlambdaExp As LambdaExpression = Nothing
2129     Private _exlambdaExpDelegate As [Delegate] = Nothing
2130
2131     Public Sub New()
2132
2133     End Sub
2134
2135     'フィルタ一覧に表示する文言生成
2136     Private Function MakeSummary() As String
2137         Dim fs As New StringBuilder()
2138         If Not String.IsNullOrEmpty(_name) OrElse _body.Count > 0 OrElse _isRt OrElse Not String.IsNullOrEmpty(_source) Then
2139             If _searchBoth Then
2140                 If Not String.IsNullOrEmpty(_name) Then
2141                     fs.AppendFormat(My.Resources.SetFiltersText1, _name)
2142                 Else
2143                     fs.Append(My.Resources.SetFiltersText2)
2144                 End If
2145             End If
2146             If _body.Count > 0 Then
2147                 fs.Append(My.Resources.SetFiltersText3)
2148                 For Each bf As String In _body
2149                     fs.Append(bf)
2150                     fs.Append(" ")
2151                 Next
2152                 fs.Length -= 1
2153                 fs.Append(My.Resources.SetFiltersText4)
2154             End If
2155             fs.Append("(")
2156             If _searchBoth Then
2157                 fs.Append(My.Resources.SetFiltersText5)
2158             Else
2159                 fs.Append(My.Resources.SetFiltersText6)
2160             End If
2161             If _useRegex Then
2162                 fs.Append(My.Resources.SetFiltersText7)
2163             End If
2164             If _searchUrl Then
2165                 fs.Append(My.Resources.SetFiltersText8)
2166             End If
2167             If _caseSensitive Then
2168                 fs.Append(My.Resources.SetFiltersText13)
2169             End If
2170             If _isRt Then
2171                 fs.Append("RT/")
2172             End If
2173             If _useLambda Then
2174                 fs.Append("LambdaExp/")
2175             End If
2176             If Not String.IsNullOrEmpty(_source) Then
2177                 fs.AppendFormat("Src…{0}/", _source)
2178             End If
2179             fs.Length -= 1
2180             fs.Append(")")
2181         End If
2182         If Not String.IsNullOrEmpty(_exname) OrElse _exbody.Count > 0 OrElse _isExRt OrElse Not String.IsNullOrEmpty(_exSource) Then
2183             '除外
2184             fs.Append(My.Resources.SetFiltersText12)
2185             If _exsearchBoth Then
2186                 If Not String.IsNullOrEmpty(_exname) Then
2187                     fs.AppendFormat(My.Resources.SetFiltersText1, _exname)
2188                 Else
2189                     fs.Append(My.Resources.SetFiltersText2)
2190                 End If
2191             End If
2192             If _exbody.Count > 0 Then
2193                 fs.Append(My.Resources.SetFiltersText3)
2194                 For Each bf As String In _exbody
2195                     fs.Append(bf)
2196                     fs.Append(" ")
2197                 Next
2198                 fs.Length -= 1
2199                 fs.Append(My.Resources.SetFiltersText4)
2200             End If
2201             fs.Append("(")
2202             If _exsearchBoth Then
2203                 fs.Append(My.Resources.SetFiltersText5)
2204             Else
2205                 fs.Append(My.Resources.SetFiltersText6)
2206             End If
2207             If _exuseRegex Then
2208                 fs.Append(My.Resources.SetFiltersText7)
2209             End If
2210             If _exsearchUrl Then
2211                 fs.Append(My.Resources.SetFiltersText8)
2212             End If
2213             If _excaseSensitive Then
2214                 fs.Append(My.Resources.SetFiltersText13)
2215             End If
2216             If _isExRt Then
2217                 fs.Append("RT/")
2218             End If
2219             If _exuseLambda Then
2220                 fs.Append("LambdaExp/")
2221             End If
2222             If Not String.IsNullOrEmpty(_exSource) Then
2223                 fs.AppendFormat("Src…{0}/", _exSource)
2224             End If
2225             fs.Length -= 1
2226             fs.Append(")")
2227         End If
2228
2229         fs.Append("(")
2230         If _moveFrom Then
2231             fs.Append(My.Resources.SetFiltersText9)
2232         Else
2233             fs.Append(My.Resources.SetFiltersText11)
2234         End If
2235         If Not _moveFrom AndAlso _setMark Then
2236             fs.Append(My.Resources.SetFiltersText10)
2237         ElseIf Not _moveFrom Then
2238             fs.Length -= 1
2239         End If
2240
2241         fs.Append(")")
2242
2243         Return fs.ToString()
2244     End Function
2245
2246     Public Property NameFilter() As String
2247         Get
2248             Return _name
2249         End Get
2250         Set(ByVal value As String)
2251             _name = value
2252         End Set
2253     End Property
2254
2255     Public Property ExNameFilter() As String
2256         Get
2257             Return _exname
2258         End Get
2259         Set(ByVal value As String)
2260             _exname = value
2261         End Set
2262     End Property
2263
2264     <Xml.Serialization.XmlIgnore()> _
2265     Public Property BodyFilter() As List(Of String)
2266         Get
2267             Return _body
2268         End Get
2269         Set(ByVal value As List(Of String))
2270             _lambdaExp = Nothing
2271             _lambdaExpDelegate = Nothing
2272             _body = value
2273         End Set
2274     End Property
2275
2276     Public Property BodyFilterArray() As String()
2277         Get
2278             Return _body.ToArray
2279         End Get
2280         Set(ByVal value As String())
2281             _body = New List(Of String)
2282             For Each filter As String In value
2283                 _body.Add(filter)
2284             Next
2285         End Set
2286     End Property
2287
2288     <Xml.Serialization.XmlIgnore()> _
2289     Public Property ExBodyFilter() As List(Of String)
2290         Get
2291             Return _exbody
2292         End Get
2293         Set(ByVal value As List(Of String))
2294             _exlambdaExp = Nothing
2295             _exlambdaExpDelegate = Nothing
2296             _exbody = value
2297         End Set
2298     End Property
2299
2300     Public Property ExBodyFilterArray() As String()
2301         Get
2302             Return _exbody.ToArray
2303         End Get
2304         Set(ByVal value As String())
2305             _exbody = New List(Of String)
2306             For Each filter As String In value
2307                 _exbody.Add(filter)
2308             Next
2309         End Set
2310     End Property
2311
2312     Public Property SearchBoth() As Boolean
2313         Get
2314             Return _searchBoth
2315         End Get
2316         Set(ByVal value As Boolean)
2317             _searchBoth = value
2318         End Set
2319     End Property
2320
2321     Public Property ExSearchBoth() As Boolean
2322         Get
2323             Return _exsearchBoth
2324         End Get
2325         Set(ByVal value As Boolean)
2326             _exsearchBoth = value
2327         End Set
2328     End Property
2329
2330     Public Property MoveFrom() As Boolean
2331         Get
2332             Return _moveFrom
2333         End Get
2334         Set(ByVal value As Boolean)
2335             _moveFrom = value
2336         End Set
2337     End Property
2338
2339     Public Property SetMark() As Boolean
2340         Get
2341             Return _setMark
2342         End Get
2343         Set(ByVal value As Boolean)
2344             _setMark = value
2345         End Set
2346     End Property
2347
2348     Public Property SearchUrl() As Boolean
2349         Get
2350             Return _searchUrl
2351         End Get
2352         Set(ByVal value As Boolean)
2353             _searchUrl = value
2354         End Set
2355     End Property
2356
2357     Public Property ExSearchUrl() As Boolean
2358         Get
2359             Return _exsearchUrl
2360         End Get
2361         Set(ByVal value As Boolean)
2362             _exsearchUrl = value
2363         End Set
2364     End Property
2365
2366     Public Property CaseSensitive() As Boolean
2367         Get
2368             Return _caseSensitive
2369         End Get
2370         Set(ByVal value As Boolean)
2371             _caseSensitive = value
2372         End Set
2373     End Property
2374
2375     Public Property ExCaseSensitive() As Boolean
2376         Get
2377             Return _excaseSensitive
2378         End Get
2379         Set(ByVal value As Boolean)
2380             _excaseSensitive = value
2381         End Set
2382     End Property
2383
2384     Public Property UseLambda() As Boolean
2385         Get
2386             Return _useLambda
2387         End Get
2388         Set(ByVal value As Boolean)
2389             _lambdaExp = Nothing
2390             _lambdaExpDelegate = Nothing
2391             _useLambda = value
2392         End Set
2393     End Property
2394
2395     Public Property ExUseLambda() As Boolean
2396         Get
2397             Return _exuseLambda
2398         End Get
2399         Set(ByVal value As Boolean)
2400             _exlambdaExp = Nothing
2401             _exlambdaExpDelegate = Nothing
2402             _exuseLambda = value
2403         End Set
2404     End Property
2405
2406     Public Property UseRegex() As Boolean
2407         Get
2408             Return _useRegex
2409         End Get
2410         Set(ByVal value As Boolean)
2411             _useRegex = value
2412         End Set
2413     End Property
2414
2415     Public Property ExUseRegex() As Boolean
2416         Get
2417             Return _exuseRegex
2418         End Get
2419         Set(ByVal value As Boolean)
2420             _exuseRegex = value
2421         End Set
2422     End Property
2423
2424     Public Property IsRt() As Boolean
2425         Get
2426             Return _isRt
2427         End Get
2428         Set(ByVal value As Boolean)
2429             _isRt = value
2430         End Set
2431     End Property
2432
2433     Public Property IsExRt() As Boolean
2434         Get
2435             Return _isExRt
2436         End Get
2437         Set(ByVal value As Boolean)
2438             _isExRt = value
2439         End Set
2440     End Property
2441
2442     Public Property Source() As String
2443         Get
2444             Return _source
2445         End Get
2446         Set(ByVal value As String)
2447             _source = value
2448         End Set
2449     End Property
2450
2451     Public Property ExSource() As String
2452         Get
2453             Return _exSource
2454         End Get
2455         Set(ByVal value As String)
2456             _exSource = value
2457         End Set
2458     End Property
2459
2460     Public Overrides Function ToString() As String
2461         Return MakeSummary()
2462     End Function
2463
2464     Public Function ExecuteLambdaExpression(ByVal expr As String, ByVal post As PostClass) As Boolean
2465         If _lambdaExp Is Nothing OrElse _lambdaExpDelegate Is Nothing Then
2466             _lambdaExp = ParseLambda(Of PostClass, Boolean)(expr, post)
2467             _lambdaExpDelegate = _lambdaExp.Compile()
2468         End If
2469         Return (DirectCast(_lambdaExpDelegate.DynamicInvoke(post), Boolean))
2470     End Function
2471
2472     Public Function ExecuteExLambdaExpression(ByVal expr As String, ByVal post As PostClass) As Boolean
2473         If _exlambdaExp Is Nothing OrElse _exlambdaExpDelegate Is Nothing Then
2474             _exlambdaExp = ParseLambda(Of PostClass, Boolean)(expr, post)
2475             _exlambdaExpDelegate = _exlambdaExp.Compile()
2476         End If
2477         Return (DirectCast(_exlambdaExpDelegate.DynamicInvoke(post), Boolean))
2478     End Function
2479
2480     Public Function IsHit(ByVal post As PostClass) As HITRESULT
2481         Dim bHit As Boolean = True
2482         Dim tBody As String
2483         Dim tSource As String
2484         If _searchUrl Then
2485             tBody = post.Text
2486             tSource = post.SourceHtml
2487         Else
2488             tBody = post.TextFromApi
2489             tSource = post.Source
2490         End If
2491         '検索オプション
2492         Dim compOpt As System.StringComparison
2493         Dim rgOpt As System.Text.RegularExpressions.RegexOptions
2494         If _caseSensitive Then
2495             compOpt = StringComparison.Ordinal
2496             rgOpt = RegexOptions.None
2497         Else
2498             compOpt = StringComparison.OrdinalIgnoreCase
2499             rgOpt = RegexOptions.IgnoreCase
2500         End If
2501         If _searchBoth Then
2502             If String.IsNullOrEmpty(_name) OrElse
2503                 (Not _useRegex AndAlso
2504                  (post.ScreenName.Equals(_name, compOpt) OrElse
2505                   post.RetweetedBy.Equals(_name, compOpt)
2506                  )
2507                 ) OrElse
2508                 (_useRegex AndAlso
2509                  (Regex.IsMatch(post.ScreenName, _name, rgOpt) OrElse
2510                   (Not String.IsNullOrEmpty(post.RetweetedBy) AndAlso Regex.IsMatch(post.RetweetedBy, _name, rgOpt))
2511                  )
2512                 ) Then
2513                 If _useLambda Then
2514                     If Not ExecuteLambdaExpression(_body.Item(0), post) Then bHit = False
2515                 Else
2516                     For Each fs As String In _body
2517                         If _useRegex Then
2518                             If Not Regex.IsMatch(tBody, fs, rgOpt) Then bHit = False
2519                         Else
2520                             If _caseSensitive Then
2521                                 If Not tBody.Contains(fs) Then bHit = False
2522                             Else
2523                                 If Not tBody.ToLower().Contains(fs.ToLower()) Then bHit = False
2524                             End If
2525                         End If
2526                         If Not bHit Then Exit For
2527                     Next
2528                 End If
2529             Else
2530                 bHit = False
2531             End If
2532         Else
2533             If _useLambda Then
2534                 If Not ExecuteLambdaExpression(_body.Item(0), post) Then bHit = False
2535             Else
2536                 For Each fs As String In _body
2537                     If _useRegex Then
2538                         If Not (Regex.IsMatch(post.ScreenName, fs, rgOpt) OrElse
2539                                 (Not String.IsNullOrEmpty(post.RetweetedBy) AndAlso Regex.IsMatch(post.RetweetedBy, fs, rgOpt)) OrElse
2540                                 Regex.IsMatch(tBody, fs, rgOpt)) Then bHit = False
2541                     Else
2542                         If _caseSensitive Then
2543                             If Not (post.ScreenName.Contains(fs) OrElse _
2544                                     post.RetweetedBy.Contains(fs) OrElse _
2545                                     tBody.Contains(fs)) Then bHit = False
2546                         Else
2547                             If Not (post.ScreenName.ToLower().Contains(fs.ToLower()) OrElse _
2548                                     post.RetweetedBy.ToLower().Contains(fs.ToLower()) OrElse _
2549                                     tBody.ToLower().Contains(fs.ToLower())) Then bHit = False
2550                         End If
2551                     End If
2552                     If Not bHit Then Exit For
2553                 Next
2554             End If
2555         End If
2556         If _isRt Then
2557             If post.RetweetedId = 0 Then bHit = False
2558         End If
2559         If Not String.IsNullOrEmpty(_source) Then
2560             If _useRegex Then
2561                 If Not Regex.IsMatch(tSource, _source, rgOpt) Then bHit = False
2562             Else
2563                 If Not tSource.Equals(_source, compOpt) Then bHit = False
2564             End If
2565         End If
2566         If bHit Then
2567             '除外判定
2568             If _exsearchUrl Then
2569                 tBody = post.Text
2570                 tSource = post.SourceHtml
2571             Else
2572                 tBody = post.TextFromApi
2573                 tSource = post.Source
2574             End If
2575
2576             Dim exFlag As Boolean = False
2577             If Not String.IsNullOrEmpty(_exname) OrElse _exbody.Count > 0 Then
2578                 If _excaseSensitive Then
2579                     compOpt = StringComparison.Ordinal
2580                     rgOpt = RegexOptions.None
2581                 Else
2582                     compOpt = StringComparison.OrdinalIgnoreCase
2583                     rgOpt = RegexOptions.IgnoreCase
2584                 End If
2585                 If _exsearchBoth Then
2586                     If String.IsNullOrEmpty(_exname) OrElse
2587                         (Not _exuseRegex AndAlso
2588                          (post.ScreenName.Equals(_exname, compOpt) OrElse
2589                           post.RetweetedBy.Equals(_exname, compOpt)
2590                          )
2591                         ) OrElse
2592                         (_exuseRegex AndAlso _
2593                             (Regex.IsMatch(post.ScreenName, _exname, rgOpt) OrElse _
2594                              (Not String.IsNullOrEmpty(post.RetweetedBy) AndAlso Regex.IsMatch(post.RetweetedBy, _exname, rgOpt))
2595                             )
2596                         ) Then
2597                         If _exbody.Count > 0 Then
2598                             If _exuseLambda Then
2599                                 If ExecuteExLambdaExpression(_exbody.Item(0), post) Then exFlag = True
2600                             Else
2601                                 For Each fs As String In _exbody
2602                                     If _exuseRegex Then
2603                                         If Regex.IsMatch(tBody, fs, rgOpt) Then exFlag = True
2604                                     Else
2605                                         If _excaseSensitive Then
2606                                             If tBody.Contains(fs) Then exFlag = True
2607                                         Else
2608                                             If tBody.ToLower().Contains(fs.ToLower()) Then exFlag = True
2609                                         End If
2610                                     End If
2611                                     If exFlag Then Exit For
2612                                 Next
2613                             End If
2614                         Else
2615                             exFlag = True
2616                         End If
2617                     End If
2618                 Else
2619                     If _exuseLambda Then
2620                         If ExecuteExLambdaExpression(_exbody.Item(0), post) Then exFlag = True
2621                     Else
2622                         For Each fs As String In _exbody
2623                             If _exuseRegex Then
2624                                 If Regex.IsMatch(post.ScreenName, fs, rgOpt) OrElse
2625                                    (Not String.IsNullOrEmpty(post.RetweetedBy) AndAlso Regex.IsMatch(post.RetweetedBy, fs, rgOpt)) OrElse
2626                                    Regex.IsMatch(tBody, fs, rgOpt) Then exFlag = True
2627                             Else
2628                                 If _excaseSensitive Then
2629                                     If post.ScreenName.Contains(fs) OrElse _
2630                                        post.RetweetedBy.Contains(fs) OrElse _
2631                                        tBody.Contains(fs) Then exFlag = True
2632                                 Else
2633                                     If post.ScreenName.ToLower().Contains(fs.ToLower()) OrElse _
2634                                        post.RetweetedBy.ToLower().Contains(fs.ToLower()) OrElse _
2635                                        tBody.ToLower().Contains(fs.ToLower()) Then exFlag = True
2636                                 End If
2637                             End If
2638                             If exFlag Then Exit For
2639                         Next
2640                     End If
2641                 End If
2642             End If
2643             If _isExRt Then
2644                 If post.RetweetedId > 0 Then exFlag = True
2645             End If
2646             If Not String.IsNullOrEmpty(_exSource) Then
2647                 If _exuseRegex Then
2648                     If Regex.IsMatch(tSource, _exSource, rgOpt) Then exFlag = True
2649                 Else
2650                     If tSource.Equals(_exSource, compOpt) Then exFlag = True
2651                 End If
2652             End If
2653
2654             If String.IsNullOrEmpty(_name) AndAlso _body.Count = 0 AndAlso Not _isRt AndAlso String.IsNullOrEmpty(_source) Then
2655                 bHit = False
2656             End If
2657             If bHit Then
2658                 If Not exFlag Then
2659                     If _moveFrom Then
2660                         Return HITRESULT.Move
2661                     Else
2662                         If _setMark Then
2663                             Return HITRESULT.CopyAndMark
2664                         End If
2665                         Return HITRESULT.Copy
2666                     End If
2667                 Else
2668                     Return HITRESULT.Exclude
2669                 End If
2670             Else
2671                 If exFlag Then
2672                     Return HITRESULT.Exclude
2673                 Else
2674                     Return HITRESULT.None
2675                 End If
2676             End If
2677         Else
2678             Return HITRESULT.None
2679         End If
2680     End Function
2681
2682     Public Overloads Function Equals(ByVal other As FiltersClass) As Boolean _
2683      Implements System.IEquatable(Of Tween.FiltersClass).Equals
2684
2685         If Me.BodyFilter.Count <> other.BodyFilter.Count Then Return False
2686         If Me.ExBodyFilter.Count <> other.ExBodyFilter.Count Then Return False
2687         For i As Integer = 0 To Me.BodyFilter.Count - 1
2688             If Me.BodyFilter(i) <> other.BodyFilter(i) Then Return False
2689         Next
2690         For i As Integer = 0 To Me.ExBodyFilter.Count - 1
2691             If Me.ExBodyFilter(i) <> other.ExBodyFilter(i) Then Return False
2692         Next
2693
2694         Return (Me.MoveFrom = other.MoveFrom) And
2695                (Me.SetMark = other.SetMark) And
2696                (Me.NameFilter = other.NameFilter) And
2697                (Me.SearchBoth = other.SearchBoth) And
2698                (Me.SearchUrl = other.SearchUrl) And
2699                (Me.UseRegex = other.UseRegex) And
2700                (Me.ExNameFilter = other.ExNameFilter) And
2701                (Me.ExSearchBoth = other.ExSearchBoth) And
2702                (Me.ExSearchUrl = other.ExSearchUrl) And
2703                (Me.ExUseRegex = other.ExUseRegex) And
2704                (Me.IsRt = other.IsRt) And
2705                (Me.Source = other.Source) And
2706                (Me.IsExRt = other.IsExRt) And
2707                (Me.ExSource = other.ExSource) And
2708                (Me.UseLambda = other.UseLambda) And
2709                (Me.ExUseLambda = other.ExUseLambda)
2710     End Function
2711
2712     Public Function CopyTo(ByVal destination As FiltersClass) As FiltersClass
2713
2714         If Me.BodyFilter.Count > 0 Then
2715             For Each flt As String In Me.BodyFilter
2716                 destination.BodyFilter.Add(String.Copy(flt))
2717             Next
2718         End If
2719
2720         If Me.ExBodyFilter.Count > 0 Then
2721             For Each flt As String In Me.ExBodyFilter
2722                 destination.ExBodyFilter.Add(String.Copy(flt))
2723             Next
2724         End If
2725
2726         destination.MoveFrom = Me.MoveFrom
2727         destination.SetMark = Me.SetMark
2728         destination.NameFilter = Me.NameFilter
2729         destination.SearchBoth = Me.SearchBoth
2730         destination.SearchUrl = Me.SearchUrl
2731         destination.UseRegex = Me.UseRegex
2732         destination.ExNameFilter = Me.ExNameFilter
2733         destination.ExSearchBoth = Me.ExSearchBoth
2734         destination.ExSearchUrl = Me.ExSearchUrl
2735         destination.ExUseRegex = Me.ExUseRegex
2736         destination.IsRt = Me.IsRt
2737         destination.Source = Me.Source
2738         destination.IsExRt = Me.IsExRt
2739         destination.ExSource = Me.ExSource
2740         destination.UseLambda = Me.UseLambda
2741         destination.ExUseLambda = Me.ExUseLambda
2742         Return destination
2743     End Function
2744
2745     Public Overrides Function Equals(ByVal obj As Object) As Boolean
2746         If (obj Is Nothing) OrElse Not (Me.GetType() Is obj.GetType()) Then Return False
2747         Return Me.Equals(CType(obj, FiltersClass))
2748     End Function
2749
2750     Public Overrides Function GetHashCode() As Integer
2751         Return Me.MoveFrom.GetHashCode Xor
2752                Me.SetMark.GetHashCode Xor
2753                Me.BodyFilter.GetHashCode Xor
2754                Me.NameFilter.GetHashCode Xor
2755                Me.SearchBoth.GetHashCode Xor
2756                Me.SearchUrl.GetHashCode Xor
2757                Me.UseRegex.GetHashCode Xor
2758                Me.ExBodyFilter.GetHashCode Xor
2759                Me.ExNameFilter.GetHashCode Xor
2760                Me.ExSearchBoth.GetHashCode Xor
2761                Me.ExSearchUrl.GetHashCode Xor
2762                Me.ExUseRegex.GetHashCode Xor
2763                Me.IsRt.GetHashCode Xor
2764                Me.Source.GetHashCode Xor
2765                Me.IsExRt.GetHashCode Xor
2766                Me.ExSource.GetHashCode Xor
2767                Me.UseLambda.GetHashCode Xor
2768                Me.ExUseLambda.GetHashCode
2769     End Function
2770 End Class
2771
2772 'ソート比較クラス:ID比較のみ
2773 Public NotInheritable Class IdComparerClass
2774     Implements IComparer(Of Long)
2775
2776     ''' <summary>
2777     ''' 比較する方法
2778     ''' </summary>
2779     Public Enum ComparerMode
2780         Id
2781         Data
2782         Name
2783         Nickname
2784         Source
2785     End Enum
2786
2787     Private _order As SortOrder
2788     Private _mode As ComparerMode
2789     Private _statuses As Dictionary(Of Long, PostClass)
2790     Private _CmpMethod As Comparison(Of Long)
2791
2792     ''' <summary>
2793     ''' 昇順か降順か Setの際は同時に比較関数の切り替えを行う
2794     ''' </summary>
2795     Public Property Order() As SortOrder
2796         Get
2797             Return _order
2798         End Get
2799         Set(ByVal Value As SortOrder)
2800             _order = Value
2801             SetCmpMethod(_mode, _order)
2802         End Set
2803     End Property
2804
2805     ''' <summary>
2806     ''' 並び替えの方法 Setの際は同時に比較関数の切り替えを行う
2807     ''' </summary>
2808     Public Property Mode() As ComparerMode
2809         Get
2810             Return _mode
2811         End Get
2812         Set(ByVal Value As ComparerMode)
2813             _mode = Value
2814             SetCmpMethod(_mode, _order)
2815         End Set
2816     End Property
2817
2818     ''' <summary>
2819     ''' ListViewItemComparerクラスのコンストラクタ(引数付は未使用)
2820     ''' </summary>
2821     ''' <param name="col">並び替える列番号</param>
2822     ''' <param name="ord">昇順か降順か</param>
2823     ''' <param name="cmod">並び替えの方法</param>
2824
2825     Public Sub New()
2826         _order = SortOrder.Ascending
2827         _mode = ComparerMode.Id
2828         SetCmpMethod(_mode, _order)
2829     End Sub
2830
2831     Public Property posts() As Dictionary(Of Long, PostClass)
2832         Set(ByVal value As Dictionary(Of Long, PostClass))
2833             _statuses = value
2834         End Set
2835         Get
2836             Return _statuses
2837         End Get
2838     End Property
2839
2840     ' 指定したソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
2841     Public Overloads ReadOnly Property CmpMethod(ByVal _sortmode As ComparerMode, ByVal _sortorder As SortOrder) As Comparison(Of Long)
2842         Get
2843             Dim _method As Comparison(Of Long) = Nothing
2844             If _sortorder = SortOrder.Ascending Then
2845                 ' 昇順
2846                 Select Case _sortmode
2847                     Case ComparerMode.Data
2848                         _method = AddressOf Compare_ModeData_Ascending
2849                     Case ComparerMode.Id
2850                         _method = AddressOf Compare_ModeId_Ascending
2851                     Case ComparerMode.Name
2852                         _method = AddressOf Compare_ModeName_Ascending
2853                     Case ComparerMode.Nickname
2854                         _method = AddressOf Compare_ModeNickName_Ascending
2855                     Case ComparerMode.Source
2856                         _method = AddressOf Compare_ModeSource_Ascending
2857                 End Select
2858             Else
2859                 ' 降順
2860                 Select Case _sortmode
2861                     Case ComparerMode.Data
2862                         _method = AddressOf Compare_ModeData_Descending
2863                     Case ComparerMode.Id
2864                         _method = AddressOf Compare_ModeId_Descending
2865                     Case ComparerMode.Name
2866                         _method = AddressOf Compare_ModeName_Descending
2867                     Case ComparerMode.Nickname
2868                         _method = AddressOf Compare_ModeNickName_Descending
2869                     Case ComparerMode.Source
2870                         _method = AddressOf Compare_ModeSource_Descending
2871                 End Select
2872             End If
2873             Return _method
2874         End Get
2875     End Property
2876
2877     ' ソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
2878     ' (overload 現在の使用中の比較関数のアドレスを返す)
2879     Public Overloads ReadOnly Property CmpMethod() As Comparison(Of Long)
2880         Get
2881             Return _CmpMethod
2882         End Get
2883     End Property
2884
2885     ' ソートモードとソートオーダーに従い比較関数のアドレスを切り替え
2886     Private Sub SetCmpMethod(ByVal mode As ComparerMode, ByVal order As SortOrder)
2887         _CmpMethod = Me.CmpMethod(mode, order)
2888     End Sub
2889
2890     'xがyより小さいときはマイナスの数、大きいときはプラスの数、
2891     '同じときは0を返す (こちらは未使用 一応比較関数群呼び出しの形のまま残しておく)
2892     Public Function Compare(ByVal x As Long, ByVal y As Long) _
2893             As Integer Implements IComparer(Of Long).Compare
2894         Return _CmpMethod(x, y)
2895     End Function
2896
2897     ' 比較用関数群 いずれもステータスIDの順序を考慮する
2898     ' 本文比較 昇順
2899     Public Function Compare_ModeData_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2900         Dim result As Integer = String.Compare(_statuses.Item(x).TextFromApi, _statuses.Item(y).TextFromApi)
2901         If result = 0 Then result = x.CompareTo(y)
2902         Return result
2903     End Function
2904
2905     ' 本文比較 降順
2906     Public Function Compare_ModeData_Descending(ByVal x As Long, ByVal y As Long) As Integer
2907         Dim result As Integer = String.Compare(_statuses.Item(y).TextFromApi, _statuses.Item(x).TextFromApi)
2908         If result = 0 Then result = y.CompareTo(x)
2909         Return result
2910     End Function
2911
2912     ' ステータスID比較 昇順
2913     Public Function Compare_ModeId_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2914         Return x.CompareTo(y)
2915     End Function
2916
2917     ' ステータスID比較 降順
2918     Public Function Compare_ModeId_Descending(ByVal x As Long, ByVal y As Long) As Integer
2919         Return y.CompareTo(x)
2920     End Function
2921
2922     ' 表示名比較 昇順
2923     Public Function Compare_ModeName_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2924         Dim result As Integer = String.Compare(_statuses.Item(x).ScreenName, _statuses.Item(y).ScreenName)
2925         If result = 0 Then result = x.CompareTo(y)
2926         Return result
2927     End Function
2928
2929     ' 表示名比較 降順
2930     Public Function Compare_ModeName_Descending(ByVal x As Long, ByVal y As Long) As Integer
2931         Dim result As Integer = String.Compare(_statuses.Item(y).ScreenName, _statuses.Item(x).ScreenName)
2932         If result = 0 Then result = y.CompareTo(x)
2933         Return result
2934     End Function
2935
2936     ' ユーザー名比較 昇順
2937     Public Function Compare_ModeNickName_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2938         Dim result As Integer = String.Compare(_statuses.Item(x).Nickname, _statuses.Item(y).Nickname)
2939         If result = 0 Then result = x.CompareTo(y)
2940         Return result
2941     End Function
2942
2943     ' ユーザー名比較 降順
2944     Public Function Compare_ModeNickName_Descending(ByVal x As Long, ByVal y As Long) As Integer
2945         Dim result As Integer = String.Compare(_statuses.Item(y).Nickname, _statuses.Item(x).Nickname)
2946         If result = 0 Then result = y.CompareTo(x)
2947         Return result
2948     End Function
2949
2950     ' Source比較 昇順
2951     Public Function Compare_ModeSource_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2952         Dim result As Integer = String.Compare(_statuses.Item(x).Source, _statuses.Item(y).Source)
2953         If result = 0 Then result = x.CompareTo(y)
2954         Return result
2955     End Function
2956
2957     ' Source比較 降順
2958     Public Function Compare_ModeSource_Descending(ByVal x As Long, ByVal y As Long) As Integer
2959         Dim result As Integer = String.Compare(_statuses.Item(y).Source, _statuses.Item(x).Source)
2960         If result = 0 Then result = y.CompareTo(x)
2961         Return result
2962     End Function
2963 End Class