OSDN Git Service

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