OSDN Git Service

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