OSDN Git Service

328f0cc94bd8b92e0ed405c9f08cd27977e1ebc9
[opentween/open-tween.git] / Tween / StatusDictionary.vb
1 ' Tween - Client of Twitter
2 ' Copyright (c) 2007-2009 kiri_feather (@kiri_feather) <kiri_feather@gmail.com>
3 '           (c) 2008-2009 Moz (@syo68k) <http://iddy.jp/profile/moz/>
4 '           (c) 2008-2009 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
30 Public NotInheritable Class PostClass
31     Private _Nick As String
32     Private _Data As String
33     Private _ImageUrl As String
34     Private _Name As String
35     Private _PDate As Date
36     Private _Id As Long
37     Private _IsFav As Boolean
38     Private _OrgData As String
39     Private _IsRead As Boolean
40     Private _IsReply As Boolean
41     Private _IsProtect As Boolean
42     Private _IsOWL As Boolean
43     Private _IsMark As Boolean
44     Private _InReplyToUser As String
45     Private _InReplyToId As Long
46     Private _Source As String
47     Private _ReplyToList As New List(Of String)
48     Private _IsMe As Boolean
49     Private _ImageIndex As Integer
50     Private _IsDm As Boolean
51     Private _statuses As Statuses = Statuses.None
52     Private _Uid As Long
53     Private _FilterHit As Boolean
54     Private _RetweetedBy As String = ""
55     Private _RetweetedId As Long = 0
56
57     <FlagsAttribute()> _
58     Private Enum Statuses
59         None = 0
60         Protect = 1
61         Mark = 2
62         Read = 4
63         Reply = 8
64     End Enum
65
66     Public Sub New(ByVal Nickname As String, _
67             ByVal Data As String, _
68             ByVal OriginalData As String, _
69             ByVal ImageUrl As String, _
70             ByVal Name As String, _
71             ByVal PDate As Date, _
72             ByVal Id As Long, _
73             ByVal IsFav As Boolean, _
74             ByVal IsRead As Boolean, _
75             ByVal IsReply As Boolean, _
76             ByVal IsProtect As Boolean, _
77             ByVal IsOwl As Boolean, _
78             ByVal IsMark As Boolean, _
79             ByVal InReplyToUser As String, _
80             ByVal InReplyToId As Long, _
81             ByVal Source As String, _
82             ByVal ReplyToList As List(Of String), _
83             ByVal IsMe As Boolean, _
84             ByVal ImageIndex As Integer, _
85             ByVal IsDm As Boolean, _
86             ByVal Uid As Long, _
87             ByVal FilterHit As Boolean, _
88             ByVal RetweetedBy As String, _
89             ByVal RetweetedId As Long)
90         _Nick = Nickname
91         _Data = Data
92         _ImageUrl = ImageUrl
93         _Name = Name
94         _PDate = PDate
95         _Id = Id
96         _IsFav = IsFav
97         _OrgData = OriginalData
98         _IsRead = IsRead
99         _IsReply = IsReply
100         _IsProtect = IsProtect
101         _IsOWL = IsOwl
102         _IsMark = IsMark
103         _InReplyToUser = InReplyToUser
104         _InReplyToId = InReplyToId
105         _Source = Source
106         _ReplyToList = ReplyToList
107         _IsMe = IsMe
108         _ImageIndex = ImageIndex
109         _IsDm = IsDm
110         _Uid = Uid
111         _FilterHit = FilterHit
112         _RetweetedBy = RetweetedBy
113         _RetweetedId = RetweetedId
114     End Sub
115
116     Public Sub New()
117     End Sub
118
119     Public Property Nickname() As String
120         Get
121             Return _Nick
122         End Get
123         Set(ByVal value As String)
124             _Nick = value
125         End Set
126     End Property
127     Public Property Data() As String
128         Get
129             Return _Data
130         End Get
131         Set(ByVal value As String)
132             _Data = value
133         End Set
134     End Property
135     Public Property ImageUrl() As String
136         Get
137             Return _ImageUrl
138         End Get
139         Set(ByVal value As String)
140             _ImageUrl = value
141         End Set
142     End Property
143     Public Property Name() As String
144         Get
145             Return _Name
146         End Get
147         Set(ByVal value As String)
148             _Name = value
149         End Set
150     End Property
151     Public Property PDate() As Date
152         Get
153             Return _PDate
154         End Get
155         Set(ByVal value As Date)
156             _PDate = value
157         End Set
158     End Property
159     Public Property Id() As Long
160         Get
161             Return _Id
162         End Get
163         Set(ByVal value As Long)
164             _Id = value
165         End Set
166     End Property
167     Public Property IsFav() As Boolean
168         Get
169             If Me.RetweetedId > 0 AndAlso TabInformations.GetInstance.RetweetSource(Me.RetweetedId) IsNot Nothing Then
170                 Return TabInformations.GetInstance.RetweetSource(Me.RetweetedId).IsFav
171             Else
172                 Return _IsFav
173             End If
174         End Get
175         Set(ByVal value As Boolean)
176             _IsFav = value
177             If Me.RetweetedId > 0 AndAlso TabInformations.GetInstance.RetweetSource(Me.RetweetedId) IsNot Nothing Then
178                 TabInformations.GetInstance.RetweetSource(Me.RetweetedId).IsFav = value
179             End If
180         End Set
181     End Property
182     Public Property OriginalData() As String
183         Get
184             Return _OrgData
185         End Get
186         Set(ByVal value As String)
187             _OrgData = value
188         End Set
189     End Property
190     Public Property IsRead() As Boolean
191         Get
192             Return _IsRead
193         End Get
194         Set(ByVal value As Boolean)
195             If value Then
196                 _statuses = _statuses Or Statuses.Read
197             Else
198                 _statuses = _statuses And Not Statuses.Read
199             End If
200             _IsRead = value
201         End Set
202     End Property
203     Public Property IsReply() As Boolean
204         Get
205             Return _IsReply
206         End Get
207         Set(ByVal value As Boolean)
208             _IsReply = value
209         End Set
210     End Property
211     Public Property IsProtect() As Boolean
212         Get
213             Return _IsProtect
214         End Get
215         Set(ByVal value As Boolean)
216             If value Then
217                 _statuses = _statuses Or Statuses.Protect
218             Else
219                 _statuses = _statuses And Not Statuses.Protect
220             End If
221             _IsProtect = value
222         End Set
223     End Property
224     Public Property IsOwl() As Boolean
225         Get
226             Return _IsOWL
227         End Get
228         Set(ByVal value As Boolean)
229             _IsOWL = value
230         End Set
231     End Property
232     Public Property IsMark() As Boolean
233         Get
234             Return _IsMark
235         End Get
236         Set(ByVal value As Boolean)
237             If value Then
238                 _statuses = _statuses Or Statuses.Mark
239             Else
240                 _statuses = _statuses And Not Statuses.Mark
241             End If
242             _IsMark = value
243         End Set
244     End Property
245     Public Property InReplyToUser() As String
246         Get
247             Return _InReplyToUser
248         End Get
249         Set(ByVal value As String)
250             _InReplyToUser = value
251         End Set
252     End Property
253     Public Property InReplyToId() As Long
254         Get
255             Return _InReplyToId
256         End Get
257         Set(ByVal value As Long)
258             _InReplyToId = value
259         End Set
260     End Property
261     Public Property Source() As String
262         Get
263             Return _Source
264         End Get
265         Set(ByVal value As String)
266             _Source = value
267         End Set
268     End Property
269     Public Property ReplyToList() As List(Of String)
270         Get
271             Return _ReplyToList
272         End Get
273         Set(ByVal value As List(Of String))
274             _ReplyToList = value
275         End Set
276     End Property
277     Public Property IsMe() As Boolean
278         Get
279             Return _IsMe
280         End Get
281         Set(ByVal value As Boolean)
282             _IsMe = value
283         End Set
284     End Property
285     Public Property ImageIndex() As Integer
286         Get
287             Return _ImageIndex
288         End Get
289         Set(ByVal value As Integer)
290             _ImageIndex = value
291         End Set
292     End Property
293     Public Property IsDm() As Boolean
294         Get
295             Return _IsDm
296         End Get
297         Set(ByVal value As Boolean)
298             _IsDm = value
299         End Set
300     End Property
301     Public ReadOnly Property StatusIndex() As Integer
302         Get
303             Return _statuses
304         End Get
305     End Property
306     Public Property Uid() As Long
307         Get
308             Return _Uid
309         End Get
310         Set(ByVal value As Long)
311             _Uid = value
312         End Set
313     End Property
314     Public Property FilterHit() As Boolean
315         Get
316             Return _FilterHit
317         End Get
318         Set(ByVal value As Boolean)
319             _FilterHit = value
320         End Set
321     End Property
322     Public Property RetweetedBy() As String
323         Get
324             Return _RetweetedBy
325         End Get
326         Set(ByVal value As String)
327             _RetweetedBy = value
328         End Set
329     End Property
330     Public Property RetweetedId() As Long
331         Get
332             Return _RetweetedId
333         End Get
334         Set(ByVal value As Long)
335             _RetweetedId = value
336         End Set
337     End Property
338 End Class
339
340 Public NotInheritable Class TabInformations
341     '個別タブの情報をDictionaryで保持
342     Private _sorter As IdComparerClass
343     Private _tabs As New Dictionary(Of String, TabClass)
344     Private _statuses As New Dictionary(Of Long, PostClass)
345     Private _addedIds As List(Of Long)
346     Private _retweets As New Dictionary(Of Long, PostClass)
347
348     '発言の追加
349     'AddPost(複数回) -> DistributePosts          -> SubmitUpdate
350
351     'トランザクション用
352     Private _addCount As Integer
353     Private _soundFile As String
354     Private _notifyPosts As List(Of PostClass)
355     Private ReadOnly LockObj As New Object
356     Private ReadOnly LockUnread As New Object
357
358     Private Shared _instance As TabInformations = New TabInformations
359
360     Private Sub New()
361         _sorter = New IdComparerClass(Me)
362     End Sub
363
364     Public Shared Function GetInstance() As TabInformations
365         Return _instance    'singleton
366     End Function
367
368     Public Sub AddTab(ByVal TabName As String, ByVal TabType As TabUsageType)
369         _tabs.Add(TabName, New TabClass(TabName, TabType))
370     End Sub
371
372     Public Sub AddTab(ByVal TabName As String, ByVal Tab As TabClass)
373         _tabs.Add(TabName, Tab)
374     End Sub
375
376     Public Sub RemoveTab(ByVal TabName As String)
377         SyncLock LockObj
378             If IsDefaultTab(TabName) Then Exit Sub '念のため
379             Dim homeTab As TabClass = GetTabByType(TabUsageType.Home)
380             Dim dmName As String = GetTabByType(TabUsageType.DirectMessage).TabName
381
382             For idx As Integer = 0 To _tabs(TabName).AllCount - 1
383                 Dim exist As Boolean = False
384                 Dim Id As Long = _tabs(TabName).GetId(idx)
385                 For Each key As String In _tabs.Keys
386                     If Not key = TabName AndAlso key <> dmName Then
387                         If _tabs(key).Contains(Id) Then
388                             exist = True
389                             Exit For
390                         End If
391                     End If
392                 Next
393                 If Not exist Then homeTab.Add(Id, _statuses(Id).IsRead, False)
394             Next
395
396             _tabs.Remove(TabName)
397         End SyncLock
398     End Sub
399
400     Public Function ContainsTab(ByVal TabText As String) As Boolean
401         Return _tabs.ContainsKey(TabText)
402     End Function
403
404     Public Function ContainsTab(ByVal ts As TabClass) As Boolean
405         Return _tabs.ContainsValue(ts)
406     End Function
407
408     Public Property Tabs() As Dictionary(Of String, TabClass)
409         Get
410             Return _tabs
411         End Get
412         Set(ByVal value As Dictionary(Of String, TabClass))
413             _tabs = value
414         End Set
415     End Property
416
417     Public ReadOnly Property KeysTab() As Collections.Generic.Dictionary(Of String, TabClass).KeyCollection
418         Get
419             Return _tabs.Keys
420         End Get
421     End Property
422
423     Public Sub SortPosts()
424         For Each key As String In _tabs.Keys
425             _tabs(key).Sort(_sorter)
426         Next
427     End Sub
428
429     Public ReadOnly Property Sorter() As IdComparerClass
430         Get
431             Return _sorter
432         End Get
433     End Property
434
435     Public Property SortOrder() As SortOrder
436         Get
437             Return _sorter.Order
438         End Get
439         Set(ByVal value As SortOrder)
440             _sorter.Order = value
441         End Set
442     End Property
443
444     Public Property SortMode() As IdComparerClass.ComparerMode
445         Get
446             Return _sorter.Mode
447         End Get
448         Set(ByVal value As IdComparerClass.ComparerMode)
449             _sorter.Mode = value
450         End Set
451     End Property
452
453     Public Sub ToggleSortOrder(ByVal SortMode As IdComparerClass.ComparerMode)
454         If _sorter.Mode = SortMode Then
455             If _sorter.Order = Windows.Forms.SortOrder.Ascending Then
456                 _sorter.Order = Windows.Forms.SortOrder.Descending
457             Else
458                 _sorter.Order = Windows.Forms.SortOrder.Ascending
459             End If
460         Else
461             _sorter.Mode = SortMode
462             _sorter.Order = Windows.Forms.SortOrder.Ascending
463         End If
464         Me.SortPosts()
465     End Sub
466
467     Public ReadOnly Property RetweetSource(ByVal Id As Long) As PostClass
468         Get
469             If _retweets.ContainsKey(Id) Then
470                 Return _retweets(Id)
471             Else
472                 Return Nothing
473             End If
474         End Get
475     End Property
476
477     Public Sub RemovePost(ByVal Name As String, ByVal Id As Long)
478         SyncLock LockObj
479             Dim post As PostClass = _statuses(Id)
480             '指定タブから該当ID削除
481             Dim tab As TabClass = _tabs(Name)
482             Dim tType As TabUsageType = tab.TabType
483             If tab.Contains(Id) Then
484                 If tab.UnreadManage AndAlso Not post.IsRead Then    '未読管理
485                     SyncLock LockUnread
486                         tab.UnreadCount -= 1
487                         Me.SetNextUnreadId(Id, tab)
488                     End SyncLock
489                 End If
490                 tab.Remove(Id)
491             End If
492             'FavタブからRetweet発言を削除する場合は、他の同一参照Retweetも削除
493             If tType = TabUsageType.Favorites AndAlso post.RetweetedId > 0 Then
494                 For i As Integer = 0 To tab.AllCount - 1
495                     Dim rPost As PostClass = Me.Item(Name, i)
496                     If rPost.RetweetedId > 0 AndAlso rPost.RetweetedId = post.RetweetedId Then
497                         If tab.UnreadManage AndAlso Not rPost.IsRead Then    '未読管理
498                             SyncLock LockUnread
499                                 tab.UnreadCount -= 1
500                                 Me.SetNextUnreadId(rPost.Id, tab)
501                             End SyncLock
502                         End If
503                         tab.Remove(rPost.Id)
504                     End If
505                 Next
506             End If
507         End SyncLock
508     End Sub
509
510     Public Sub RemovePost(ByVal Id As Long)
511         SyncLock LockObj
512             Dim post As PostClass = _statuses(Id)
513             '各タブから該当ID削除
514             For Each key As String In _tabs.Keys
515                 Dim tab As TabClass = _tabs(key)
516                 If tab.Contains(Id) Then
517                     If tab.UnreadManage AndAlso Not post.IsRead Then    '未読管理
518                         SyncLock LockUnread
519                             tab.UnreadCount -= 1
520                             Me.SetNextUnreadId(Id, tab)
521                         End SyncLock
522                     End If
523                     tab.Remove(Id)
524                 End If
525             Next
526             _statuses.Remove(Id)
527         End SyncLock
528     End Sub
529
530     Public Function GetOldestUnreadId(ByVal TabName As String) As Integer
531         Dim tb As TabClass = _tabs(TabName)
532         If tb.OldestUnreadId > -1 AndAlso _
533            tb.Contains(tb.OldestUnreadId) AndAlso _
534            tb.UnreadCount > 0 Then
535             '未読アイテムへ
536             If _statuses.Item(tb.OldestUnreadId).IsRead Then
537                 '状態不整合(最古未読IDが実は既読)
538                 SyncLock LockUnread
539                     Me.SetNextUnreadId(-1, tb)  '頭から探索
540                 End SyncLock
541                 If tb.OldestUnreadId = -1 Then
542                     Return -1
543                 Else
544                     Return tb.IndexOf(tb.OldestUnreadId)
545                 End If
546             Else
547                 Return tb.IndexOf(tb.OldestUnreadId)    '最短経路
548             End If
549         Else
550             '一見未読なさそうだが、未読カウントはあるので探索
551             If tb.UnreadCount > 0 Then
552                 SyncLock LockUnread
553                     Me.SetNextUnreadId(-1, tb)
554                 End SyncLock
555                 If tb.OldestUnreadId = -1 Then
556                     Return -1
557                 Else
558                     Return tb.IndexOf(tb.OldestUnreadId)
559                 End If
560             Else
561                 Return -1
562             End If
563         End If
564     End Function
565
566     Private Sub SetNextUnreadId(ByVal CurrentId As Long, ByVal Tab As TabClass)
567         'CurrentID:今既読にしたID(OldestIDの可能性あり)
568         '最古未読が設定されていて、既読の場合(1発言以上存在)
569         Try
570             If Tab.OldestUnreadId > -1 AndAlso _
571                _statuses.ContainsKey(Tab.OldestUnreadId) AndAlso _
572                _statuses.Item(Tab.OldestUnreadId).IsRead AndAlso _
573                _sorter.Mode = IdComparerClass.ComparerMode.Id Then     '次の未読探索
574                 If Tab.UnreadCount = 0 Then
575                     '未読数0→最古未読なし
576                     Tab.OldestUnreadId = -1
577                 ElseIf Tab.OldestUnreadId = CurrentId Then
578                     '最古IDを既読にしたタイミング→次のIDから続けて探索
579                     Dim idx As Integer = Tab.IndexOf(CurrentId)
580                     If idx > -1 Then
581                         '続きから探索
582                         FindUnreadId(idx, Tab)
583                     Else
584                         '頭から探索
585                         FindUnreadId(-1, Tab)
586                     End If
587                 Else
588                     '頭から探索
589                     FindUnreadId(-1, Tab)
590                 End If
591             Else
592                 '頭から探索
593                 FindUnreadId(-1, Tab)
594             End If
595         Catch ex As Generic.KeyNotFoundException
596             '頭から探索
597             FindUnreadId(-1, Tab)
598         End Try
599     End Sub
600
601     Private Sub FindUnreadId(ByVal StartIdx As Integer, ByVal Tab As TabClass)
602         If Tab.AllCount = 0 Then
603             Tab.OldestUnreadId = -1
604             Tab.UnreadCount = 0
605             Exit Sub
606         End If
607         Dim toIdx As Integer = 0
608         Dim stp As Integer = 1
609         Tab.OldestUnreadId = -1
610         If _sorter.Order = Windows.Forms.SortOrder.Ascending Then
611             If StartIdx = -1 Then
612                 StartIdx = 0
613             Else
614                 'StartIdx += 1
615                 If StartIdx > Tab.AllCount - 1 Then StartIdx = Tab.AllCount - 1 '念のため
616             End If
617             toIdx = Tab.AllCount - 1
618             If toIdx < 0 Then toIdx = 0 '念のため
619             stp = 1
620         Else
621             If StartIdx = -1 Then
622                 StartIdx = Tab.AllCount - 1
623             Else
624                 'StartIdx -= 1
625             End If
626             If StartIdx < 0 Then StartIdx = 0 '念のため
627             toIdx = 0
628             stp = -1
629         End If
630         For i As Integer = StartIdx To toIdx Step stp
631             If Not _statuses(Tab.GetId(i)).IsRead Then
632                 Tab.OldestUnreadId = Tab.GetId(i)
633                 Exit For
634             End If
635         Next
636     End Sub
637
638     Public Function DistributePosts() As Integer
639         SyncLock LockObj
640             '戻り値は追加件数
641             If _addedIds Is Nothing Then Return 0
642             If _addedIds.Count = 0 Then Return 0
643
644             If _notifyPosts Is Nothing Then _notifyPosts = New List(Of PostClass)
645             Me.Distribute()    'タブに仮振分
646             _addCount = _addedIds.Count
647             _addedIds.Clear()
648             _addedIds = Nothing     '後始末
649             Return _addCount     '件数
650         End SyncLock
651     End Function
652
653     Public Function SubmitUpdate(ByRef soundFile As String, ByRef notifyPosts As PostClass()) As Integer
654         '注:メインスレッドから呼ぶこと
655         SyncLock LockObj
656             If _notifyPosts Is Nothing Then
657                 soundFile = ""
658                 notifyPosts = Nothing
659                 Return 0
660             End If
661
662             For Each key As String In _tabs.Keys
663                 _tabs(key).AddSubmit()  '振分確定(各タブに反映)
664             Next
665             Me.SortPosts()
666
667             soundFile = _soundFile
668             _soundFile = ""
669             notifyPosts = _notifyPosts.ToArray()
670             _notifyPosts.Clear()
671             _notifyPosts = Nothing
672             Dim retCnt As Integer = _addCount
673             _addCount = 0
674             Return retCnt    '件数(EndUpdateの戻り値と同じ)
675         End SyncLock
676     End Function
677
678     Private Sub Distribute()
679         '各タブのフィルターと照合。合致したらタブにID追加
680         '通知メッセージ用に、表示必要な発言リストと再生サウンドを返す
681         'notifyPosts = New List(Of PostClass)
682         Dim homeTab As TabClass = GetTabByType(TabUsageType.Home)
683         Dim replyTab As TabClass = GetTabByType(TabUsageType.Mentions)
684         Dim dmTab As TabClass = GetTabByType(TabUsageType.DirectMessage)
685         Dim favTab As TabClass = GetTabByType(TabUsageType.Favorites)
686         For Each id As Long In _addedIds
687             Dim post As PostClass = _statuses(id)
688             If Not post.IsDm Then
689                 Dim add As Boolean = False  '通知リスト追加フラグ
690                 Dim mv As Boolean = False   '移動フラグ(Recent追加有無)
691                 For Each tn As String In _tabs.Keys
692                     Dim rslt As HITRESULT = HITRESULT.None
693                     If post.RetweetedId = 0 Then
694                         rslt = _tabs(tn).AddFiltered(post.Id, post.IsRead, post.Name, post.Data, post.OriginalData)
695                     Else
696                         rslt = _tabs(tn).AddFiltered(post.Id, post.IsRead, post.RetweetedBy, post.Data, post.OriginalData)
697                     End If
698                     If rslt <> HITRESULT.None Then
699                         If rslt = HITRESULT.CopyAndMark Then post.IsMark = True 'マークあり
700                         If rslt = HITRESULT.Move Then
701                             mv = True '移動
702                             post.IsMark = False
703                         End If
704                         If _tabs(tn).Notify Then add = True '通知あり
705                         If Not _tabs(tn).SoundFile = "" AndAlso _soundFile = "" Then
706                             _soundFile = _tabs(tn).SoundFile 'wavファイル(未設定の場合のみ)
707                         End If
708                         post.FilterHit = True
709                     Else
710                         post.FilterHit = False
711                     End If
712                 Next
713                 If Not mv Then  '移動されなかったらRecentに追加
714                     homeTab.Add(post.Id, post.IsRead, True)
715                     If Not homeTab.SoundFile = "" AndAlso _soundFile = "" Then _soundFile = homeTab.SoundFile
716                     If homeTab.Notify Then add = True
717                 End If
718                 If post.IsReply Then    'ReplyだったらReplyタブに追加
719                     replyTab.Add(post.Id, post.IsRead, True)
720                     If Not replyTab.SoundFile = "" Then _soundFile = replyTab.SoundFile
721                     If replyTab.Notify Then add = True
722                 End If
723                 If post.IsFav Then    'Fav済み発言だったらFavoritesタブに追加
724                     If favTab.Contains(post.Id) Then
725                         '取得済みなら非通知
726                         _soundFile = ""
727                         add = False
728                     Else
729                         favTab.Add(post.Id, post.IsRead, True)
730                         If Not favTab.SoundFile = "" Then _soundFile = favTab.SoundFile
731                         If favTab.Notify Then add = True
732                     End If
733                 End If
734                 If add Then _notifyPosts.Add(post)
735             Else
736                 dmTab.Add(post.Id, post.IsRead, True)
737                 If dmTab.Notify Then _notifyPosts.Add(post)
738                 _soundFile = dmTab.SoundFile
739             End If
740         Next
741     End Sub
742
743     Public Sub AddPost(ByVal Item As PostClass)
744         SyncLock LockObj
745             If _statuses.ContainsKey(Item.Id) Then
746                 If Item.IsFav Then
747                     _statuses.Item(Item.Id).IsFav = True
748                 Else
749                     Exit Sub        '追加済みなら何もしない
750                 End If
751             Else
752                 _statuses.Add(Item.Id, Item)    'DMと区別しない?
753             End If
754             If Item.RetweetedId > 0 Then
755                 If Not _retweets.ContainsKey(Item.RetweetedId) Then
756                     Me.AddRetweet(Item)
757                 End If
758             End If
759             If Item.IsFav AndAlso _retweets.ContainsKey(Item.Id) Then
760                 Exit Sub    'Fav済みのRetweet元発言は追加しない
761             End If
762             If _addedIds Is Nothing Then _addedIds = New List(Of Long) 'タブ追加用IDコレクション準備
763             _addedIds.Add(Item.Id)
764         End SyncLock
765     End Sub
766
767     Private Sub AddRetweet(ByVal item As PostClass)
768         _retweets.Add( _
769             item.RetweetedId, _
770             New PostClass( _
771                 item.Nickname, _
772                 item.Data, _
773                 item.OriginalData, _
774                 item.ImageUrl, _
775                 item.Name, _
776                 item.PDate, _
777                 item.RetweetedId, _
778                 item.IsFav, _
779                 item.IsRead, _
780                 item.IsReply, _
781                 item.IsProtect, _
782                 item.IsOwl, _
783                 item.IsMark, _
784                 item.InReplyToUser, _
785                 item.InReplyToId, _
786                 item.Source, _
787                 item.ReplyToList, _
788                 item.IsMe, _
789                 item.ImageIndex, _
790                 item.IsDm, _
791                 item.Uid, _
792                 item.FilterHit, _
793                 "", _
794                 0 _
795             ) _
796         )
797     End Sub
798
799     Public Sub SetRead(ByVal Read As Boolean, ByVal TabName As String, ByVal Index As Integer)
800         'Read:True=既読へ False=未読へ
801         Dim tb As TabClass = _tabs(TabName)
802
803         If tb.UnreadManage = False Then Exit Sub '未読管理していなければ終了
804
805         Dim Id As Long = tb.GetId(Index)
806
807         If _statuses(Id).IsRead = Read Then Exit Sub '状態変更なければ終了
808
809         _statuses(Id).IsRead = Read '指定の状態に変更
810
811         SyncLock LockUnread
812             If Read Then
813                 tb.UnreadCount -= 1
814                 Me.SetNextUnreadId(Id, tb)  '次の未読セット
815                 '他タブの最古未読IDはタブ切り替え時に。
816                 For Each key As String In _tabs.Keys
817                     If key <> TabName AndAlso _
818                        _tabs(key).UnreadManage AndAlso _
819                        _tabs(key).Contains(Id) Then
820                         _tabs(key).UnreadCount -= 1
821                         If _tabs(key).OldestUnreadId = Id Then _tabs(key).OldestUnreadId = -1
822                     End If
823                 Next
824             Else
825                 tb.UnreadCount += 1
826                 If tb.OldestUnreadId > Id OrElse tb.OldestUnreadId = -1 Then tb.OldestUnreadId = Id
827                 For Each key As String In _tabs.Keys
828                     If Not key = TabName AndAlso _tabs(key).UnreadManage AndAlso _tabs(key).Contains(Id) Then
829                         _tabs(key).UnreadCount += 1
830                         If _tabs(key).OldestUnreadId > Id Then _tabs(key).OldestUnreadId = Id
831                     End If
832                 Next
833             End If
834         End SyncLock
835     End Sub
836
837     Public Sub SetRead()
838         Dim tb As TabClass = GetTabByType(TabUsageType.Home)
839         If tb.UnreadManage = False Then Exit Sub
840
841         For i As Integer = 0 To tb.AllCount - 1
842             Dim id As Long = tb.GetId(i)
843             If Not _statuses(id).IsDm AndAlso _
844                Not _statuses(id).IsReply AndAlso _
845                Not _statuses(id).IsRead AndAlso _
846                Not _statuses(id).FilterHit Then
847                 _statuses(id).IsRead = True
848                 Me.SetNextUnreadId(id, tb)  '次の未読セット
849                 For Each key As String In _tabs.Keys
850                     If _tabs(key).UnreadManage AndAlso _
851                        _tabs(key).Contains(id) Then
852                         _tabs(key).UnreadCount -= 1
853                         If _tabs(key).OldestUnreadId = id Then _tabs(key).OldestUnreadId = -1
854                     End If
855                 Next
856             End If
857         Next
858     End Sub
859
860     Public ReadOnly Property Item(ByVal ID As Long) As PostClass
861         Get
862             Return _statuses(ID)
863         End Get
864     End Property
865
866     Public ReadOnly Property Item(ByVal TabName As String, ByVal Index As Integer) As PostClass
867         Get
868             Return _statuses(_tabs(TabName).GetId(Index))
869         End Get
870     End Property
871
872     Public ReadOnly Property Item(ByVal TabName As String, ByVal StartIndex As Integer, ByVal EndIndex As Integer) As PostClass()
873         Get
874             Dim length As Integer = EndIndex - StartIndex + 1
875             Dim posts() As PostClass = New PostClass(length - 1) {}
876             For i As Integer = 0 To length - 1
877                 posts(i) = _statuses(_tabs(TabName).GetId(StartIndex + i))
878             Next i
879             Return posts
880         End Get
881     End Property
882
883     Public ReadOnly Property ItemCount() As Integer
884         Get
885             SyncLock LockObj
886                 Return _statuses.Count
887             End SyncLock
888         End Get
889     End Property
890
891     Public Function ContainsKey(ByVal Id As Long) As Boolean
892         SyncLock LockObj
893             Return _statuses.ContainsKey(Id)
894         End SyncLock
895     End Function
896
897     Public Sub SetUnreadManage(ByVal Manage As Boolean)
898         If Manage Then
899             For Each key As String In _tabs.Keys
900                 Dim tb As TabClass = _tabs(key)
901                 If tb.UnreadManage Then
902                     SyncLock LockUnread
903                         Dim cnt As Integer = 0
904                         Dim oldest As Long = Long.MaxValue
905                         For Each id As Long In tb.BackupIds
906                             If Not _statuses(id).IsRead Then
907                                 cnt += 1
908                                 If oldest > id Then oldest = id
909                             End If
910                         Next
911                         tb.OldestUnreadId = oldest
912                         tb.UnreadCount = cnt
913                     End SyncLock
914                 End If
915             Next
916         Else
917             For Each key As String In _tabs.Keys
918                 Dim tb As TabClass = _tabs(key)
919                 If tb.UnreadManage AndAlso tb.UnreadCount > 0 Then
920                     SyncLock LockUnread
921                         tb.UnreadCount = 0
922                         tb.OldestUnreadId = -1
923                     End SyncLock
924                 End If
925             Next
926         End If
927     End Sub
928
929     Public Sub RenameTab(ByVal Original As String, ByVal NewName As String)
930         Dim tb As TabClass = _tabs(Original)
931         _tabs.Remove(Original)
932         tb.TabName = NewName
933         _tabs.Add(NewName, tb)
934     End Sub
935
936     Public Sub FilterAll()
937         SyncLock LockObj
938             Dim tbr As TabClass = GetTabByType(TabUsageType.Home)
939             Dim replyTab As TabClass = GetTabByType(TabUsageType.Mentions)
940             For Each key As String In _tabs.Keys
941                 Dim tb As TabClass = _tabs(key)
942                 If tb.FilterModified Then
943                     tb.FilterModified = False
944                     Dim orgIds() As Long = tb.BackupIds()
945                     tb.ClearIDs()
946                     ''''''''''''''フィルター前のIDsを退避。どのタブにも含まれないidはrecentへ追加
947                     ''''''''''''''moveフィルターにヒットした際、recentに該当あればrecentから削除
948                     For Each id As Long In _statuses.Keys
949                         Dim post As PostClass = _statuses.Item(id)
950                         If post.IsDm Then Continue For
951                         Dim rslt As HITRESULT = HITRESULT.None
952                         If post.RetweetedId = 0 Then
953                             rslt = tb.AddFiltered(post.Id, post.IsRead, post.Name, post.Data, post.OriginalData)
954                         Else
955                             rslt = tb.AddFiltered(post.Id, post.IsRead, post.RetweetedBy, post.Data, post.OriginalData)
956                         End If
957                         Select Case rslt
958                             Case HITRESULT.CopyAndMark
959                                 post.IsMark = True 'マークあり
960                                 post.FilterHit = True
961                             Case HITRESULT.Move
962                                 tbr.Remove(post.Id, post.IsRead)
963                                 post.IsMark = False
964                                 post.FilterHit = True
965                             Case HITRESULT.Copy
966                                 post.IsMark = False
967                                 post.FilterHit = True
968                             Case HITRESULT.None
969                                 If key = replyTab.TabName AndAlso post.IsReply Then replyTab.Add(post.Id, post.IsRead, True)
970                                 If post.IsFav Then GetTabByType(TabUsageType.Favorites).Add(post.Id, post.IsRead, True)
971                                 post.FilterHit = False
972                         End Select
973                     Next
974                     tb.AddSubmit()  '振分確定
975                     For Each id As Long In orgIds
976                         Dim hit As Boolean = False
977                         For Each tkey As String In _tabs.Keys
978                             If _tabs(tkey).Contains(id) Then
979                                 hit = True
980                                 Exit For
981                             End If
982                         Next
983                         If Not hit Then tbr.Add(id, _statuses(id).IsRead, False)
984                     Next
985                 End If
986             Next
987
988             Me.SortPosts()
989         End SyncLock
990     End Sub
991
992     Public Function GetId(ByVal TabName As String, ByVal IndexCollection As ListView.SelectedIndexCollection) As Long()
993         If IndexCollection.Count = 0 Then Return Nothing
994
995         Dim tb As TabClass = _tabs(TabName)
996         Dim Ids(IndexCollection.Count - 1) As Long
997         For i As Integer = 0 To Ids.Length - 1
998             Ids(i) = tb.GetId(IndexCollection(i))
999         Next
1000         Return Ids
1001     End Function
1002
1003     Public Function GetId(ByVal TabName As String, ByVal Index As Integer) As Long
1004         Return _tabs(TabName).GetId(Index)
1005     End Function
1006
1007     Public Function IndexOf(ByVal TabName As String, ByVal Ids() As Long) As Integer()
1008         If Ids Is Nothing Then Return Nothing
1009         Dim idx(Ids.Length - 1) As Integer
1010         Dim tb As TabClass = _tabs(TabName)
1011         For i As Integer = 0 To Ids.Length - 1
1012             idx(i) = tb.IndexOf(Ids(i))
1013         Next
1014         Return idx
1015     End Function
1016
1017     Public Function IndexOf(ByVal TabName As String, ByVal Id As Long) As Integer
1018         Return _tabs(TabName).IndexOf(Id)
1019     End Function
1020
1021     Public Sub ClearTabIds(ByVal TabName As String)
1022         '不要なPostを削除
1023         For Each Id As Long In _tabs(TabName).BackupIds
1024             Dim Hit As Boolean = False
1025             For Each tb As TabClass In _tabs.Values
1026                 If tb.Contains(Id) Then
1027                     Hit = True
1028                     Exit For
1029                 End If
1030             Next
1031             If Not Hit Then _statuses.Remove(Id)
1032         Next
1033         '指定タブをクリア
1034         _tabs(TabName).ClearIDs()
1035     End Sub
1036
1037     Public Sub SetTabUnreadManage(ByVal TabName As String, ByVal Manage As Boolean)
1038         Dim tb As TabClass = _tabs(TabName)
1039         SyncLock LockUnread
1040             If Manage Then
1041                 Dim cnt As Integer = 0
1042                 Dim oldest As Long = Long.MaxValue
1043                 For Each id As Long In tb.BackupIds
1044                     If Not _statuses(id).IsRead Then
1045                         cnt += 1
1046                         If oldest > id Then oldest = id
1047                     End If
1048                 Next
1049                 tb.OldestUnreadId = oldest
1050                 tb.UnreadCount = cnt
1051             Else
1052                 tb.OldestUnreadId = -1
1053                 tb.UnreadCount = 0
1054             End If
1055         End SyncLock
1056         tb.UnreadManage = Manage
1057     End Sub
1058
1059     Public Sub RefreshOwl(ByVal follower As List(Of String))
1060         SyncLock LockObj
1061             If follower.Count > 1 Then
1062                 For Each id As Long In _statuses.Keys
1063                     If Not _statuses(id).IsDm Then _statuses(id).IsOwl = Not follower.Contains(_statuses(id).Name.ToLower())
1064                 Next
1065             Else
1066                 For Each id As Long In _statuses.Keys
1067                     If Not _statuses(id).IsDm Then _statuses(id).IsOwl = False
1068                 Next
1069             End If
1070         End SyncLock
1071     End Sub
1072
1073     Public Function GetTabByType(ByVal tabType As TabUsageType) As TabClass
1074         'Home,Mentions,DM,Favは1つに制限する
1075         'その他のタイプを指定されたら、最初に合致したものを返す
1076         '合致しなければNothingを返す
1077         For Each tb As TabClass In _tabs.Values
1078             If tb.TabType = tabType Then Return tb
1079         Next
1080         Return Nothing
1081     End Function
1082
1083     ' デフォルトタブの判定処理
1084     Public Function IsDefaultTab(ByVal tabName As String) As Boolean
1085         If tabName IsNot Nothing AndAlso _
1086            _tabs.ContainsKey(tabName) AndAlso _
1087            (_tabs(tabName).TabType = TabUsageType.Home OrElse _
1088            _tabs(tabName).TabType = TabUsageType.Mentions OrElse _
1089            _tabs(tabName).TabType = TabUsageType.DirectMessage OrElse _
1090            _tabs(tabName).TabType = TabUsageType.Favorites) Then
1091             Return True
1092         Else
1093             Return False
1094         End If
1095     End Function
1096 End Class
1097
1098 <Serializable()> _
1099 Public NotInheritable Class TabClass
1100     Private _unreadManage As Boolean = False
1101     Private _notify As Boolean = False
1102     Private _soundFile As String = ""
1103     Private _filters As List(Of FiltersClass)
1104     Private _oldestUnreadItem As Long = -1     'ID
1105     Private _unreadCount As Integer = 0
1106     Private _ids As List(Of Long)
1107     Private _filterMod As Boolean = False
1108     Private _tmpIds As List(Of TempolaryId)
1109     Private _tabName As String = ""
1110     Private _tabType As TabUsageType = TabUsageType.Undefined
1111
1112     'Search query
1113     Private _searchLang As String = ""
1114     Private _searchSource As String = ""
1115     Private _searchLinks As Boolean = False
1116     Private _searchWords As String = ""
1117
1118     Public Property SearchLang() As String
1119         Get
1120             Return _searchLang
1121         End Get
1122         Set(ByVal value As String)
1123             _searchLang = value
1124         End Set
1125     End Property
1126     Public Property SearchSource() As String
1127         Get
1128             Return _searchSource
1129         End Get
1130         Set(ByVal value As String)
1131             _searchSource = value
1132         End Set
1133     End Property
1134     Public Property SearchLinks() As Boolean
1135         Get
1136             Return _searchLinks
1137         End Get
1138         Set(ByVal value As Boolean)
1139             _searchLinks = value
1140         End Set
1141     End Property
1142     Public Property SearchWords() As String
1143         Get
1144             Return _searchWords
1145         End Get
1146         Set(ByVal value As String)
1147             _searchWords = value.Trim
1148         End Set
1149     End Property
1150     Public ReadOnly Property SearchQuery() As String
1151         Get
1152             Dim qry As String = ""
1153             If String.IsNullOrEmpty(_searchWords) Then Return ""
1154             qry = "q=" + _searchWords
1155             If Not String.IsNullOrEmpty(_searchSource) Then qry += " source:" + _searchSource
1156             If _searchLinks Then qry += " filter:links"
1157             If Not String.IsNullOrEmpty(_searchLang) Then qry += "&lang=" + _searchLang
1158             Return UrlEncode(qry)
1159         End Get
1160     End Property
1161
1162     Private Structure TempolaryId
1163         Public Id As Long
1164         Public Read As Boolean
1165
1166         Public Sub New(ByVal argId As Long, ByVal argRead As Boolean)
1167             Id = argId
1168             Read = argRead
1169         End Sub
1170     End Structure
1171
1172     Public Sub New()
1173         _filters = New List(Of FiltersClass)
1174         _notify = True
1175         _soundFile = ""
1176         _unreadManage = True
1177         _ids = New List(Of Long)
1178         _oldestUnreadItem = -1
1179         _tabType = TabUsageType.Undefined
1180     End Sub
1181
1182     Public Sub New(ByVal TabName As String, ByVal TabType As TabUsageType)
1183         _tabName = TabName
1184         _filters = New List(Of FiltersClass)
1185         _notify = True
1186         _soundFile = ""
1187         _unreadManage = True
1188         _ids = New List(Of Long)
1189         _oldestUnreadItem = -1
1190         _tabType = TabType
1191     End Sub
1192
1193     Public Sub Sort(ByVal Sorter As IdComparerClass)
1194         _ids.Sort(Sorter.CmpMethod)
1195     End Sub
1196
1197     '無条件に追加
1198     Private Sub Add(ByVal ID As Long, ByVal Read As Boolean)
1199         If Me._ids.Contains(ID) Then Exit Sub
1200
1201         Me._ids.Add(ID)
1202
1203         If Not Read AndAlso Me._unreadManage Then
1204             Me._unreadCount += 1
1205             If Me._oldestUnreadItem = -1 Then
1206                 Me._oldestUnreadItem = ID
1207             Else
1208                 If ID < Me._oldestUnreadItem Then Me._oldestUnreadItem = ID
1209             End If
1210         End If
1211     End Sub
1212
1213     Public Sub Add(ByVal ID As Long, ByVal Read As Boolean, ByVal Temporary As Boolean)
1214         If Not Temporary Then
1215             Me.Add(ID, Read)
1216         Else
1217             If _tmpIds Is Nothing Then _tmpIds = New List(Of TempolaryId)
1218             _tmpIds.Add(New TempolaryId(ID, Read))
1219         End If
1220     End Sub
1221
1222     'フィルタに合致したら追加
1223     Public Function AddFiltered(ByVal ID As Long, _
1224                                 ByVal Read As Boolean, _
1225                                 ByVal Name As String, _
1226                                 ByVal Body As String, _
1227                                 ByVal OrgData As String) As HITRESULT
1228         'Try
1229         '    rwLock.AcquireReaderLock(System.Threading.Timeout.Infinite) '読み取りロック取得
1230
1231         Dim rslt As HITRESULT = HITRESULT.None
1232         '全フィルタ評価(優先順位あり)
1233         For Each ft As FiltersClass In _filters
1234             Select Case ft.IsHit(Name, Body, OrgData)   'フィルタクラスでヒット判定
1235                 Case HITRESULT.None
1236                 Case HITRESULT.Copy
1237                     If rslt <> HITRESULT.CopyAndMark Then rslt = HITRESULT.Copy
1238                 Case HITRESULT.CopyAndMark
1239                     rslt = HITRESULT.CopyAndMark
1240                 Case HITRESULT.Move
1241                     rslt = HITRESULT.Move
1242                 Case HITRESULT.Exclude
1243                     rslt = HITRESULT.None
1244                     Exit For
1245             End Select
1246         Next
1247
1248         If rslt <> HITRESULT.None Then
1249             If _tmpIds Is Nothing Then _tmpIds = New List(Of TempolaryId)
1250             _tmpIds.Add(New TempolaryId(ID, Read))
1251         End If
1252         'Me.Add(ID, Read)
1253
1254         Return rslt 'マーク付けは呼び出し元で行うこと
1255
1256         'Finally
1257         '    rwLock.ReleaseReaderLock()
1258         'End Try
1259     End Function
1260
1261     Public Sub AddSubmit()
1262         If _tmpIds Is Nothing Then Exit Sub
1263         For Each tId As TempolaryId In _tmpIds
1264             Me.Add(tId.Id, tId.Read)
1265         Next
1266         _tmpIds.Clear()
1267         _tmpIds = Nothing
1268     End Sub
1269
1270     Public Sub Remove(ByVal Id As Long)
1271         If Not Me._ids.Contains(Id) Then Exit Sub
1272
1273         Me._ids.Remove(Id)
1274     End Sub
1275
1276     Public Sub Remove(ByVal Id As Long, ByVal Read As Boolean)
1277         If Not Me._ids.Contains(Id) Then Exit Sub
1278
1279         If Not Read AndAlso Me._unreadManage Then
1280             Me._unreadCount -= 1
1281             Me._oldestUnreadItem = -1
1282         End If
1283
1284         Me._ids.Remove(Id)
1285     End Sub
1286
1287     Public Property UnreadManage() As Boolean
1288         Get
1289             Return _unreadManage
1290         End Get
1291         Set(ByVal value As Boolean)
1292             Me._unreadManage = value
1293             If Not value Then
1294                 Me._oldestUnreadItem = -1
1295                 Me._unreadCount = 0
1296             End If
1297         End Set
1298     End Property
1299
1300     Public Property Notify() As Boolean
1301         Get
1302             Return _notify
1303         End Get
1304         Set(ByVal value As Boolean)
1305             _notify = value
1306         End Set
1307     End Property
1308
1309     Public Property SoundFile() As String
1310         Get
1311             Return _soundFile
1312         End Get
1313         Set(ByVal value As String)
1314             _soundFile = value
1315         End Set
1316     End Property
1317
1318     <Xml.Serialization.XmlIgnore()> _
1319     Public Property OldestUnreadId() As Long
1320         Get
1321             Return _oldestUnreadItem
1322         End Get
1323         Set(ByVal value As Long)
1324             _oldestUnreadItem = value
1325         End Set
1326     End Property
1327
1328     <Xml.Serialization.XmlIgnore()> _
1329     Public Property UnreadCount() As Integer
1330         Get
1331             Return _unreadCount
1332         End Get
1333         Set(ByVal value As Integer)
1334             _unreadCount = value
1335         End Set
1336     End Property
1337
1338     Public ReadOnly Property AllCount() As Integer
1339         Get
1340             Return Me._ids.Count
1341         End Get
1342     End Property
1343
1344     Public Function GetFilters() As FiltersClass()
1345         Return _filters.ToArray()
1346     End Function
1347
1348     Public Sub RemoveFilter(ByVal filter As FiltersClass)
1349         _filters.Remove(filter)
1350         _filterMod = True
1351     End Sub
1352
1353     Public Function AddFilter(ByVal filter As FiltersClass) As Boolean
1354         If _filters.Contains(filter) Then Return False
1355         _filters.Add(filter)
1356         _filterMod = True
1357         Return True
1358     End Function
1359
1360     Public Sub EditFilter(ByVal original As FiltersClass, ByVal modified As FiltersClass)
1361         original.BodyFilter = modified.BodyFilter
1362         original.NameFilter = modified.NameFilter
1363         original.SearchBoth = modified.SearchBoth
1364         original.SearchUrl = modified.SearchUrl
1365         original.UseRegex = modified.UseRegex
1366         original.CaseSensitive = modified.CaseSensitive
1367         original.ExBodyFilter = modified.ExBodyFilter
1368         original.ExNameFilter = modified.ExNameFilter
1369         original.ExSearchBoth = modified.ExSearchBoth
1370         original.ExSearchUrl = modified.ExSearchUrl
1371         original.ExUseRegex = modified.ExUseRegex
1372         original.ExCaseSensitive = modified.ExCaseSensitive
1373         original.MoveFrom = modified.MoveFrom
1374         original.SetMark = modified.SetMark
1375         _filterMod = True
1376     End Sub
1377
1378     <Xml.Serialization.XmlIgnore()> _
1379     Public Property Filters() As List(Of FiltersClass)
1380         Get
1381             Return _filters
1382         End Get
1383         Set(ByVal value As List(Of FiltersClass))
1384             _filters = value
1385         End Set
1386     End Property
1387
1388     Public Property FilterArray() As FiltersClass()
1389         Get
1390             Return _filters.ToArray
1391         End Get
1392         Set(ByVal value As FiltersClass())
1393             For Each filters As FiltersClass In value
1394                 _filters.Add(filters)
1395             Next
1396         End Set
1397     End Property
1398
1399     Public Function Contains(ByVal ID As Long) As Boolean
1400         Return _ids.Contains(ID)
1401     End Function
1402
1403     Public Sub ClearIDs()
1404         _ids.Clear()
1405         _unreadCount = 0
1406         _oldestUnreadItem = -1
1407     End Sub
1408
1409     Public Function GetId(ByVal Index As Integer) As Long
1410         Return _ids(Index)
1411     End Function
1412
1413     Public Function IndexOf(ByVal ID As Long) As Integer
1414         Return _ids.IndexOf(ID)
1415     End Function
1416
1417     <Xml.Serialization.XmlIgnore()> _
1418     Public Property FilterModified() As Boolean
1419         Get
1420             Return _filterMod
1421         End Get
1422         Set(ByVal value As Boolean)
1423             _filterMod = value
1424         End Set
1425     End Property
1426
1427     Public Function BackupIds() As Long()
1428         Return _ids.ToArray()
1429     End Function
1430
1431     Public Property TabName() As String
1432         Get
1433             Return _tabName
1434         End Get
1435         Set(ByVal value As String)
1436             _tabName = value
1437         End Set
1438     End Property
1439
1440     Public Property TabType() As TabUsageType
1441         Get
1442             Return _tabType
1443         End Get
1444         Set(ByVal value As TabUsageType)
1445             _tabType = value
1446         End Set
1447     End Property
1448 End Class
1449
1450 <Serializable()> _
1451 Public NotInheritable Class FiltersClass
1452     Implements System.IEquatable(Of FiltersClass)
1453     Private _name As String = ""
1454     Private _body As New List(Of String)
1455     Private _searchBoth As Boolean = True
1456     Private _searchUrl As Boolean = False
1457     Private _caseSensitive As Boolean = False
1458     Private _useRegex As Boolean = False
1459     Private _exname As String = ""
1460     Private _exbody As New List(Of String)
1461     Private _exsearchBoth As Boolean = True
1462     Private _exsearchUrl As Boolean = False
1463     Private _exuseRegex As Boolean = False
1464     Private _excaseSensitive As Boolean = False
1465     Private _moveFrom As Boolean = False
1466     Private _setMark As Boolean = True
1467
1468     Public Sub New(ByVal NameFilter As String, _
1469         ByVal BodyFilter As List(Of String), _
1470         ByVal SearchBoth As Boolean, _
1471         ByVal SearchUrl As Boolean, _
1472         ByVal CaseSensitive As Boolean, _
1473         ByVal UseRegex As Boolean, _
1474         ByVal ParentTab As String, _
1475         ByVal ExNameFilter As String, _
1476         ByVal ExBodyFilter As List(Of String), _
1477         ByVal ExSearchBoth As Boolean, _
1478         ByVal ExSearchUrl As Boolean, _
1479         ByVal ExUseRegex As Boolean, _
1480         ByVal ExCaseSensitive As Boolean, _
1481         ByVal MoveFrom As Boolean, _
1482         ByVal SetMark As Boolean)
1483         _name = NameFilter
1484         _body = BodyFilter
1485         _searchBoth = SearchBoth
1486         _searchUrl = SearchUrl
1487         _caseSensitive = CaseSensitive
1488         _useRegex = UseRegex
1489         _exname = ExNameFilter
1490         _exbody = ExBodyFilter
1491         _exsearchBoth = ExSearchBoth
1492         _exsearchUrl = ExSearchUrl
1493         _exuseRegex = ExUseRegex
1494         _excaseSensitive = ExCaseSensitive
1495         _moveFrom = MoveFrom
1496         _setMark = SetMark
1497         '正規表現検証
1498         If _useRegex Then
1499             Try
1500                 Dim rgx As New Regex(_name)
1501             Catch ex As Exception
1502                 Throw New Exception(My.Resources.ButtonOK_ClickText3 + ex.Message)
1503                 Exit Sub
1504             End Try
1505             For Each bs As String In _body
1506                 Try
1507                     Dim rgx As New Regex(bs)
1508                 Catch ex As Exception
1509                     Throw New Exception(My.Resources.ButtonOK_ClickText3 + ex.Message)
1510                     Exit Sub
1511                 End Try
1512             Next
1513         End If
1514         If _exuseRegex Then
1515             Try
1516                 Dim rgx As New Regex(_exname)
1517             Catch ex As Exception
1518                 Throw New Exception(My.Resources.ButtonOK_ClickText3 + ex.Message)
1519                 Exit Sub
1520             End Try
1521             For Each bs As String In _exbody
1522                 Try
1523                     Dim rgx As New Regex(bs)
1524                 Catch ex As Exception
1525                     Throw New Exception(My.Resources.ButtonOK_ClickText3 + ex.Message)
1526                     Exit Sub
1527                 End Try
1528             Next
1529         End If
1530     End Sub
1531
1532     Public Sub New()
1533
1534     End Sub
1535
1536     'フィルタ一覧に表示する文言生成
1537     Private Function MakeSummary() As String
1538         Dim fs As New System.Text.StringBuilder()
1539         If _name <> "" OrElse _body.Count > 0 Then
1540             If _searchBoth Then
1541                 If _name <> "" Then
1542                     fs.AppendFormat(My.Resources.SetFiltersText1, _name)
1543                 Else
1544                     fs.Append(My.Resources.SetFiltersText2)
1545                 End If
1546             End If
1547             If _body.Count > 0 Then
1548                 fs.Append(My.Resources.SetFiltersText3)
1549                 For Each bf As String In _body
1550                     fs.Append(bf)
1551                     fs.Append(" ")
1552                 Next
1553                 fs.Length -= 1
1554                 fs.Append(My.Resources.SetFiltersText4)
1555             End If
1556             fs.Append("(")
1557             If _searchBoth Then
1558                 fs.Append(My.Resources.SetFiltersText5)
1559             Else
1560                 fs.Append(My.Resources.SetFiltersText6)
1561             End If
1562             If _useRegex Then
1563                 fs.Append(My.Resources.SetFiltersText7)
1564             End If
1565             If _searchUrl Then
1566                 fs.Append(My.Resources.SetFiltersText8)
1567             End If
1568             If _caseSensitive Then
1569                 fs.Append(My.Resources.SetFiltersText13)
1570             End If
1571             'If _moveFrom Then
1572             '    fs.Append(My.Resources.SetFiltersText9)
1573             'ElseIf _setMark Then
1574             '    fs.Append(My.Resources.SetFiltersText10)
1575             'Else
1576             '    fs.Append(My.Resources.SetFiltersText11)
1577             'End If
1578             fs.Length -= 1
1579             fs.Append(")")
1580         End If
1581         If _exname <> "" OrElse _exbody.Count > 0 Then
1582             '除外
1583             fs.Append(My.Resources.SetFiltersText12)
1584             If _exsearchBoth Then
1585                 If _exname <> "" Then
1586                     fs.AppendFormat(My.Resources.SetFiltersText1, _exname)
1587                 Else
1588                     fs.Append(My.Resources.SetFiltersText2)
1589                 End If
1590             End If
1591             If _exbody.Count > 0 Then
1592                 fs.Append(My.Resources.SetFiltersText3)
1593                 For Each bf As String In _exbody
1594                     fs.Append(bf)
1595                     fs.Append(" ")
1596                 Next
1597                 fs.Length -= 1
1598                 fs.Append(My.Resources.SetFiltersText4)
1599             End If
1600             fs.Append("(")
1601             If _exsearchBoth Then
1602                 fs.Append(My.Resources.SetFiltersText5)
1603             Else
1604                 fs.Append(My.Resources.SetFiltersText6)
1605             End If
1606             If _exuseRegex Then
1607                 fs.Append(My.Resources.SetFiltersText7)
1608             End If
1609             If _exsearchUrl Then
1610                 fs.Append(My.Resources.SetFiltersText8)
1611             End If
1612             If _excaseSensitive Then
1613                 fs.Append(My.Resources.SetFiltersText13)
1614             End If
1615             fs.Length -= 1
1616             fs.Append(")")
1617         End If
1618
1619         fs.Append("(")
1620         If _moveFrom Then
1621             fs.Append(My.Resources.SetFiltersText9)
1622         Else
1623             fs.Append(My.Resources.SetFiltersText11)
1624         End If
1625         If Not _moveFrom AndAlso _setMark Then
1626             fs.Append(My.Resources.SetFiltersText10)
1627         ElseIf Not _moveFrom Then
1628             fs.Length -= 1
1629         End If
1630
1631         fs.Append(")")
1632
1633         Return fs.ToString()
1634     End Function
1635
1636     Public Property NameFilter() As String
1637         Get
1638             Return _name
1639         End Get
1640         Set(ByVal value As String)
1641             _name = value
1642         End Set
1643     End Property
1644
1645     Public Property ExNameFilter() As String
1646         Get
1647             Return _exname
1648         End Get
1649         Set(ByVal value As String)
1650             _exname = value
1651         End Set
1652     End Property
1653
1654     <Xml.Serialization.XmlIgnore()> _
1655     Public Property BodyFilter() As List(Of String)
1656         Get
1657             Return _body
1658         End Get
1659         Set(ByVal value As List(Of String))
1660             _body = value
1661         End Set
1662     End Property
1663
1664     Public Property BodyFilterArray() As String()
1665         Get
1666             Return _body.ToArray
1667         End Get
1668         Set(ByVal value As String())
1669             _body = New List(Of String)
1670             For Each filter As String In value
1671                 _body.Add(filter)
1672             Next
1673         End Set
1674     End Property
1675
1676     <Xml.Serialization.XmlIgnore()> _
1677     Public Property ExBodyFilter() As List(Of String)
1678         Get
1679             Return _exbody
1680         End Get
1681         Set(ByVal value As List(Of String))
1682             _exbody = value
1683         End Set
1684     End Property
1685
1686     Public Property ExBodyFilterArray() As String()
1687         Get
1688             Return _exbody.ToArray
1689         End Get
1690         Set(ByVal value As String())
1691             _exbody = New List(Of String)
1692             For Each filter As String In value
1693                 _exbody.Add(filter)
1694             Next
1695         End Set
1696     End Property
1697
1698     Public Property SearchBoth() As Boolean
1699         Get
1700             Return _searchBoth
1701         End Get
1702         Set(ByVal value As Boolean)
1703             _searchBoth = value
1704         End Set
1705     End Property
1706
1707     Public Property ExSearchBoth() As Boolean
1708         Get
1709             Return _exsearchBoth
1710         End Get
1711         Set(ByVal value As Boolean)
1712             _exsearchBoth = value
1713         End Set
1714     End Property
1715
1716     Public Property MoveFrom() As Boolean
1717         Get
1718             Return _moveFrom
1719         End Get
1720         Set(ByVal value As Boolean)
1721             _moveFrom = value
1722         End Set
1723     End Property
1724
1725     Public Property SetMark() As Boolean
1726         Get
1727             Return _setMark
1728         End Get
1729         Set(ByVal value As Boolean)
1730             _setMark = value
1731         End Set
1732     End Property
1733
1734     Public Property SearchUrl() As Boolean
1735         Get
1736             Return _searchUrl
1737         End Get
1738         Set(ByVal value As Boolean)
1739             _searchUrl = value
1740         End Set
1741     End Property
1742
1743     Public Property ExSearchUrl() As Boolean
1744         Get
1745             Return _exsearchUrl
1746         End Get
1747         Set(ByVal value As Boolean)
1748             _exsearchUrl = value
1749         End Set
1750     End Property
1751
1752     Public Property CaseSensitive() As Boolean
1753         Get
1754             Return _caseSensitive
1755         End Get
1756         Set(ByVal value As Boolean)
1757             _caseSensitive = value
1758         End Set
1759     End Property
1760
1761     Public Property ExCaseSensitive() As Boolean
1762         Get
1763             Return _excaseSensitive
1764         End Get
1765         Set(ByVal value As Boolean)
1766             _excaseSensitive = value
1767         End Set
1768     End Property
1769
1770     Public Property UseRegex() As Boolean
1771         Get
1772             Return _useRegex
1773         End Get
1774         Set(ByVal value As Boolean)
1775             _useRegex = value
1776         End Set
1777     End Property
1778
1779     Public Property ExUseRegex() As Boolean
1780         Get
1781             Return _exuseRegex
1782         End Get
1783         Set(ByVal value As Boolean)
1784             _exuseRegex = value
1785         End Set
1786     End Property
1787
1788     Public Overrides Function ToString() As String
1789         Return MakeSummary()
1790     End Function
1791
1792     Public Function IsHit(ByVal Name As String, ByVal Body As String, ByVal OrgData As String) As HITRESULT
1793         Dim bHit As Boolean = True
1794         Dim tBody As String
1795         If _searchUrl Then
1796             tBody = OrgData
1797         Else
1798             tBody = Body
1799         End If
1800         '検索オプション
1801         Dim compOpt As System.StringComparison
1802         Dim rgOpt As System.Text.RegularExpressions.RegexOptions
1803         If _caseSensitive Then
1804             compOpt = StringComparison.Ordinal
1805             rgOpt = RegexOptions.None
1806         Else
1807             compOpt = StringComparison.OrdinalIgnoreCase
1808             rgOpt = RegexOptions.IgnoreCase
1809         End If
1810         If _searchBoth Then
1811             If _name = "" OrElse _
1812                 Name.Equals(_name, compOpt) OrElse _
1813                 (_useRegex AndAlso Regex.IsMatch(Name, _name, rgOpt)) Then
1814                 For Each fs As String In _body
1815                     If _useRegex Then
1816                         If Regex.IsMatch(tBody, fs, rgOpt) = False Then bHit = False
1817                     Else
1818                         If _caseSensitive Then
1819                             If tBody.Contains(fs) = False Then bHit = False
1820                         Else
1821                             If tBody.ToLower().Contains(fs.ToLower()) = False Then bHit = False
1822                         End If
1823                     End If
1824                     If Not bHit Then Exit For
1825                 Next
1826             Else
1827                 bHit = False
1828             End If
1829         Else
1830             For Each fs As String In _body
1831                 If _useRegex Then
1832                     If Not (Regex.IsMatch(Name, fs, rgOpt) OrElse _
1833                             Regex.IsMatch(tBody, fs, rgOpt)) Then bHit = False
1834                 Else
1835                     If _caseSensitive Then
1836                         If Not (Name.Contains(fs) OrElse _
1837                                 tBody.Contains(fs)) Then bHit = False
1838                     Else
1839                         If Not (Name.ToLower().Contains(fs.ToLower()) OrElse _
1840                                 tBody.ToLower().Contains(fs.ToLower())) Then bHit = False
1841                     End If
1842                 End If
1843                 If Not bHit Then Exit For
1844             Next
1845         End If
1846         If bHit Then
1847             '除外判定
1848             Dim exFlag As Boolean = False
1849             'If _name = "" AndAlso _body.Count = 0 Then
1850             '    exFlag = True
1851             '    'bHit = False
1852             'End If
1853             If _exname <> "" OrElse _exbody.Count > 0 Then
1854                 If _excaseSensitive Then
1855                     compOpt = StringComparison.Ordinal
1856                     rgOpt = RegexOptions.None
1857                 Else
1858                     compOpt = StringComparison.OrdinalIgnoreCase
1859                     rgOpt = RegexOptions.IgnoreCase
1860                 End If
1861                 If _exsearchBoth Then
1862                     If _exname = "" OrElse Name.Equals(_exname, compOpt) OrElse _
1863                                     (_exuseRegex AndAlso Regex.IsMatch(Name, _exname, rgOpt)) Then
1864                         If _exbody.Count > 0 Then
1865                             For Each fs As String In _exbody
1866                                 If _exuseRegex Then
1867                                     If Regex.IsMatch(tBody, fs, rgOpt) Then exFlag = True
1868                                 Else
1869                                     If _excaseSensitive Then
1870                                         If tBody.Contains(fs) Then exFlag = True
1871                                     Else
1872                                         If tBody.ToLower().Contains(fs.ToLower()) Then exFlag = True
1873                                     End If
1874                                 End If
1875                                 If exFlag Then Exit For
1876                             Next
1877                         Else
1878                             exFlag = True
1879                         End If
1880                     End If
1881                 Else
1882                     For Each fs As String In _exbody
1883                         If _exuseRegex Then
1884                             If Regex.IsMatch(Name, fs, rgOpt) OrElse _
1885                                Regex.IsMatch(tBody, fs, rgOpt) Then exFlag = True
1886                         Else
1887                             If _excaseSensitive Then
1888                                 If Name.Contains(fs) OrElse _
1889                                    tBody.Contains(fs) Then exFlag = True
1890                             Else
1891                                 If Name.ToLower().Contains(fs.ToLower()) OrElse _
1892                                    tBody.ToLower().Contains(fs.ToLower()) Then exFlag = True
1893                             End If
1894                         End If
1895                         If exFlag Then Exit For
1896                     Next
1897                 End If
1898             End If
1899
1900             If _name = "" AndAlso _body.Count = 0 Then
1901                 bHit = False
1902             End If
1903             If bHit Then
1904                 If Not exFlag Then
1905                     'If _setMark Then Return HITRESULT.CopyAndMark
1906                     If _moveFrom Then
1907                         Return HITRESULT.Move
1908                     Else
1909                         If _setMark Then
1910                             Return HITRESULT.CopyAndMark
1911                         End If
1912                         Return HITRESULT.Copy
1913                     End If
1914                     'Return HITRESULT.Copy
1915                 Else
1916                     Return HITRESULT.Exclude
1917                 End If
1918             Else
1919                 If exFlag Then
1920                     Return HITRESULT.Exclude
1921                 Else
1922                     Return HITRESULT.None
1923                 End If
1924             End If
1925         Else
1926             Return HITRESULT.None
1927         End If
1928     End Function
1929
1930     Public Overloads Function Equals(ByVal other As FiltersClass) As Boolean _
1931      Implements System.IEquatable(Of Tween.FiltersClass).Equals
1932
1933         If Me.BodyFilter.Count <> other.BodyFilter.Count Then Return False
1934         If Me.ExBodyFilter.Count <> other.ExBodyFilter.Count Then Return False
1935         For i As Integer = 0 To Me.BodyFilter.Count - 1
1936             If Me.BodyFilter(i) <> other.BodyFilter(i) Then Return False
1937         Next
1938         For i As Integer = 0 To Me.ExBodyFilter.Count - 1
1939             If Me.ExBodyFilter(i) <> other.ExBodyFilter(i) Then Return False
1940         Next
1941
1942         Return (Me.MoveFrom = other.MoveFrom) And _
1943                (Me.SetMark = other.SetMark) And _
1944                (Me.NameFilter = other.NameFilter) And _
1945                (Me.SearchBoth = other.SearchBoth) And _
1946                (Me.SearchUrl = other.SearchUrl) And _
1947                (Me.UseRegex = other.UseRegex) And _
1948                (Me.ExNameFilter = other.ExNameFilter) And _
1949                (Me.ExSearchBoth = other.ExSearchBoth) And _
1950                (Me.ExSearchUrl = other.ExSearchUrl) And _
1951                (Me.ExUseRegex = other.ExUseRegex)
1952     End Function
1953
1954     Public Overrides Function Equals(ByVal obj As Object) As Boolean
1955         If (obj Is Nothing) OrElse Not (Me.GetType() Is obj.GetType()) Then Return False
1956         Return Me.Equals(CType(obj, FiltersClass))
1957     End Function
1958
1959     Public Overrides Function GetHashCode() As Integer
1960         Return Me.MoveFrom.GetHashCode Xor _
1961                Me.SetMark.GetHashCode Xor _
1962                Me.BodyFilter.GetHashCode Xor _
1963                Me.NameFilter.GetHashCode Xor _
1964                Me.SearchBoth.GetHashCode Xor _
1965                Me.SearchUrl.GetHashCode Xor _
1966                Me.UseRegex.GetHashCode Xor _
1967                Me.ExBodyFilter.GetHashCode Xor _
1968                Me.ExNameFilter.GetHashCode Xor _
1969                Me.ExSearchBoth.GetHashCode Xor _
1970                Me.ExSearchUrl.GetHashCode Xor _
1971                Me.ExUseRegex.GetHashCode
1972     End Function
1973 End Class
1974
1975 'ソート比較クラス:ID比較のみ
1976 Public NotInheritable Class IdComparerClass
1977     Implements IComparer(Of Long)
1978
1979     ''' <summary>
1980     ''' 比較する方法
1981     ''' </summary>
1982     Public Enum ComparerMode
1983         Id
1984         Data
1985         Name
1986         Nickname
1987         Source
1988     End Enum
1989
1990     Private _order As SortOrder
1991     Private _mode As ComparerMode
1992     Private _statuses As TabInformations
1993     Private _CmpMethod As Comparison(Of Long)
1994
1995     ''' <summary>
1996     ''' 昇順か降順か Setの際は同時に比較関数の切り替えを行う
1997     ''' </summary>
1998     Public Property Order() As SortOrder
1999         Get
2000             Return _order
2001         End Get
2002         Set(ByVal Value As SortOrder)
2003             _order = Value
2004             SetCmpMethod(_mode, _order)
2005         End Set
2006     End Property
2007
2008     ''' <summary>
2009     ''' 並び替えの方法 Setの際は同時に比較関数の切り替えを行う
2010     ''' </summary>
2011     Public Property Mode() As ComparerMode
2012         Get
2013             Return _mode
2014         End Get
2015         Set(ByVal Value As ComparerMode)
2016             _mode = Value
2017             SetCmpMethod(_mode, _order)
2018         End Set
2019     End Property
2020
2021     ''' <summary>
2022     ''' ListViewItemComparerクラスのコンストラクタ(引数付は未使用)
2023     ''' </summary>
2024     ''' <param name="col">並び替える列番号</param>
2025     ''' <param name="ord">昇順か降順か</param>
2026     ''' <param name="cmod">並び替えの方法</param>
2027     Public Sub New(ByVal ord As SortOrder, ByVal SortMode As ComparerMode)
2028         _order = ord
2029         _mode = SortMode
2030         SetCmpMethod(_mode, _order)
2031     End Sub
2032
2033     Public Sub New(ByVal TabInf As TabInformations)
2034         _order = SortOrder.Ascending
2035         _mode = ComparerMode.Id
2036         _statuses = TabInf
2037         SetCmpMethod(_mode, _order)
2038     End Sub
2039
2040     ' 指定したソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
2041     Public Overloads ReadOnly Property CmpMethod(ByVal _sortmode As ComparerMode, ByVal _sortorder As SortOrder) As Comparison(Of Long)
2042         Get
2043             Dim _method As Comparison(Of Long) = Nothing
2044             If _sortorder = SortOrder.Ascending Then
2045                 ' 昇順
2046                 Select Case _sortmode
2047                     Case ComparerMode.Data
2048                         _method = AddressOf Compare_ModeData_Ascending
2049                     Case ComparerMode.Id
2050                         _method = AddressOf Compare_ModeId_Ascending
2051                     Case ComparerMode.Name
2052                         _method = AddressOf Compare_ModeName_Ascending
2053                     Case ComparerMode.Nickname
2054                         _method = AddressOf Compare_ModeNickName_Ascending
2055                     Case ComparerMode.Source
2056                         _method = AddressOf Compare_ModeSource_Ascending
2057                 End Select
2058             Else
2059                 ' 降順
2060                 Select Case _sortmode
2061                     Case ComparerMode.Data
2062                         _method = AddressOf Compare_ModeData_Descending
2063                     Case ComparerMode.Id
2064                         _method = AddressOf Compare_ModeId_Descending
2065                     Case ComparerMode.Name
2066                         _method = AddressOf Compare_ModeName_Descending
2067                     Case ComparerMode.Nickname
2068                         _method = AddressOf Compare_ModeNickName_Descending
2069                     Case ComparerMode.Source
2070                         _method = AddressOf Compare_ModeSource_Descending
2071                 End Select
2072             End If
2073             Return _method
2074         End Get
2075     End Property
2076
2077     ' ソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
2078     ' (overload 現在の使用中の比較関数のアドレスを返す)
2079     Public Overloads ReadOnly Property CmpMethod() As Comparison(Of Long)
2080         Get
2081             Return _CmpMethod
2082         End Get
2083     End Property
2084
2085     ' ソートモードとソートオーダーに従い比較関数のアドレスを切り替え
2086     Private Sub SetCmpMethod(ByVal mode As ComparerMode, ByVal order As SortOrder)
2087         _CmpMethod = Me.CmpMethod(mode, order)
2088     End Sub
2089
2090     'xがyより小さいときはマイナスの数、大きいときはプラスの数、
2091     '同じときは0を返す (こちらは未使用 一応比較関数群呼び出しの形のまま残しておく)
2092     Public Function Compare(ByVal x As Long, ByVal y As Long) _
2093             As Integer Implements IComparer(Of Long).Compare
2094         Return _CmpMethod(x, y)
2095     End Function
2096
2097     ' 比較用関数群 いずれもステータスIDの順序を考慮する
2098     ' 注:ID比較でCTypeを使用しているが、abs(x-y)がInteger(Int32)に収まらないことはあり得ないのでこれでよい
2099     ' 本文比較 昇順
2100     Public Function Compare_ModeData_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2101         Dim result As Integer = String.Compare(_statuses.Item(x).Data, _statuses.Item(y).Data)
2102         If result = 0 Then result = x.CompareTo(y)
2103         Return result
2104     End Function
2105
2106     ' 本文比較 降順
2107     Public Function Compare_ModeData_Descending(ByVal x As Long, ByVal y As Long) As Integer
2108         Dim result As Integer = String.Compare(_statuses.Item(y).Data, _statuses.Item(x).Data)
2109         If result = 0 Then result = y.CompareTo(x)
2110         Return result
2111     End Function
2112
2113     ' ステータスID比較 昇順
2114     Public Function Compare_ModeId_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2115         Return x.CompareTo(y)
2116     End Function
2117
2118     ' ステータスID比較 降順
2119     Public Function Compare_ModeId_Descending(ByVal x As Long, ByVal y As Long) As Integer
2120         Return y.CompareTo(x)
2121     End Function
2122
2123     ' 表示名比較 昇順
2124     Public Function Compare_ModeName_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2125         Dim result As Integer = String.Compare(_statuses.Item(x).Name, _statuses.Item(y).Name)
2126         If result = 0 Then result = x.CompareTo(y)
2127         Return result
2128     End Function
2129
2130     ' 表示名比較 降順
2131     Public Function Compare_ModeName_Descending(ByVal x As Long, ByVal y As Long) As Integer
2132         Dim result As Integer = String.Compare(_statuses.Item(y).Name, _statuses.Item(x).Name)
2133         If result = 0 Then result = y.CompareTo(x)
2134         Return result
2135     End Function
2136
2137     ' ユーザー名比較 昇順
2138     Public Function Compare_ModeNickName_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2139         Dim result As Integer = String.Compare(_statuses.Item(x).Nickname, _statuses.Item(y).Nickname)
2140         If result = 0 Then result = x.CompareTo(y)
2141         Return result
2142     End Function
2143
2144     ' ユーザー名比較 降順
2145     Public Function Compare_ModeNickName_Descending(ByVal x As Long, ByVal y As Long) As Integer
2146         Dim result As Integer = String.Compare(_statuses.Item(y).Nickname, _statuses.Item(x).Nickname)
2147         If result = 0 Then result = y.CompareTo(x)
2148         Return result
2149     End Function
2150
2151     ' Source比較 昇順
2152     Public Function Compare_ModeSource_Ascending(ByVal x As Long, ByVal y As Long) As Integer
2153         Dim result As Integer = String.Compare(_statuses.Item(x).Source, _statuses.Item(y).Source)
2154         If result = 0 Then result = x.CompareTo(y)
2155         Return result
2156     End Function
2157
2158     ' Source比較 降順
2159     Public Function Compare_ModeSource_Descending(ByVal x As Long, ByVal y As Long) As Integer
2160         Dim result As Integer = String.Compare(_statuses.Item(y).Source, _statuses.Item(x).Source)
2161         If result = 0 Then result = y.CompareTo(x)
2162         Return result
2163     End Function
2164 End Class