OSDN Git Service

ビルド時に警告が出る箇所の修正
[opentween/open-tween.git] / OpenTween / StatusDictionary.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      Egtra (@egtra) <http://dev.activebasic.com/egtra/>
8 // All rights reserved.
9 //
10 // This file is part of OpenTween.
11 //
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
15 // any later version.
16 //
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
26
27 using System;
28 using System.Collections.Generic;
29 using System.Linq;
30 using System.Linq.Expressions;
31 using System.Reflection;
32 using System.Text;
33 using System.Text.RegularExpressions;
34 using System.Windows.Forms;
35 using System.Xml.Serialization;
36
37 namespace OpenTween
38 {
39     public sealed class PostClass : ICloneable
40     {
41         public class StatusGeo
42         {
43             public double Lng { get; set; }
44             public double Lat { get; set; }
45         }
46         private string _Nick;
47         private string _textFromApi;
48         private string _ImageUrl;
49         private string _screenName;
50         private DateTime _createdAt;
51         private long _statusId;
52         private bool _IsFav;
53         private string _text;
54         private bool _IsRead;
55         private bool _IsReply;
56         private bool _IsExcludeReply;
57         private bool _IsProtect;
58         private bool _IsOWL;
59         private bool _IsMark;
60         private string _InReplyToUser;
61         private long _InReplyToStatusId;
62         private string _Source;
63         private string _SourceHtml;
64         private List<string> _ReplyToList = new List<string>();
65         private bool _IsMe;
66         private bool _IsDm;
67         private States _states = States.None;
68         private long _UserId;
69         private bool _FilterHit;
70         private string _RetweetedBy = "";
71         private long _RetweetedId = 0;
72         private string _SearchTabName = "";
73         private bool _IsDeleted = false;
74         private long _InReplyToUserId = 0;
75         private StatusGeo _postGeo = new StatusGeo();
76         public int RetweetedCount { get; set; }
77         public long RetweetedByUserId { get; set; }
78         public Dictionary<string, string> Media { get; set; }
79
80         [Flags]
81         private enum States
82         {
83             None = 0,
84             Protect = 1,
85             Mark = 2,
86             Reply = 4,
87             Geo = 8,
88         }
89
90         public PostClass(string Nickname,
91                 string textFromApi,
92                 string text,
93                 string ImageUrl,
94                 string screenName,
95                 DateTime createdAt,
96                 long statusId,
97                 bool IsFav,
98                 bool IsRead,
99                 bool IsReply,
100                 bool IsExcludeReply,
101                 bool IsProtect,
102                 bool IsOwl,
103                 bool IsMark,
104                 string InReplyToUser,
105                 long InReplyToStatusId,
106                 string Source,
107                 string SourceHtml,
108                 List<string> ReplyToList,
109                 bool IsMe,
110                 bool IsDm,
111                 long userId,
112                 bool FilterHit,
113                 string RetweetedBy,
114                 long RetweetedId,
115                 StatusGeo Geo)
116             : this()
117         {
118             _Nick = Nickname;
119             _textFromApi = textFromApi;
120             _ImageUrl = ImageUrl;
121             _screenName = screenName;
122             _createdAt = createdAt;
123             _statusId = statusId;
124             _IsFav = IsFav;
125             _text = text;
126             _IsRead = IsRead;
127             _IsReply = IsReply;
128             _IsExcludeReply = IsExcludeReply;
129             _IsProtect = IsProtect;
130             _IsOWL = IsOwl;
131             _IsMark = IsMark;
132             _InReplyToUser = InReplyToUser;
133             _InReplyToStatusId = InReplyToStatusId;
134             _Source = Source;
135             _SourceHtml = SourceHtml;
136             _ReplyToList = ReplyToList;
137             _IsMe = IsMe;
138             _IsDm = IsDm;
139             _UserId = userId;
140             _FilterHit = FilterHit;
141             _RetweetedBy = RetweetedBy;
142             _RetweetedId = RetweetedId;
143             _postGeo = Geo;
144         }
145
146         public PostClass()
147         {
148             RetweetedCount = 0;
149             RetweetedByUserId = 0;
150             Media = new Dictionary<string, string>();
151         }
152
153         public string Nickname
154         {
155             get
156             {
157                 return _Nick;
158             }
159             set
160             {
161                 _Nick = value;
162             }
163         }
164         public string TextFromApi
165         {
166             get
167             {
168                 return _textFromApi;
169             }
170             set
171             {
172                 _textFromApi = value;
173             }
174         }
175         public string ImageUrl
176         {
177             get
178             {
179                 return _ImageUrl;
180             }
181             set
182             {
183                 _ImageUrl = value;
184             }
185         }
186         public string ScreenName
187         {
188             get
189             {
190                 return _screenName;
191             }
192             set
193             {
194                 _screenName = value;
195             }
196         }
197         public DateTime CreatedAt
198         {
199             get
200             {
201                 return _createdAt;
202             }
203             set
204             {
205                 _createdAt = value;
206             }
207         }
208         public long StatusId
209         {
210             get
211             {
212                 return _statusId;
213             }
214             set
215             {
216                 _statusId = value;
217             }
218         }
219         public bool IsFav
220         {
221             get
222             {
223                 if (this.RetweetedId > 0 && TabInformations.GetInstance().RetweetSource(this.RetweetedId) != null)
224                 {
225                     return TabInformations.GetInstance().RetweetSource(this.RetweetedId).IsFav;
226                 }
227                 else
228                 {
229                     return _IsFav;
230                 }
231             }
232             set
233             {
234                 _IsFav = value;
235                 if (this.RetweetedId > 0 && TabInformations.GetInstance().RetweetSource(this.RetweetedId) != null)
236                 {
237                     TabInformations.GetInstance().RetweetSource(this.RetweetedId).IsFav = value;
238                 }
239             }
240         }
241         public string Text
242         {
243             get
244             {
245                 return _text;
246             }
247             set
248             {
249                 _text = value;
250             }
251         }
252         public bool IsRead
253         {
254             get
255             {
256                 return _IsRead;
257             }
258             set
259             {
260                 _IsRead = value;
261             }
262         }
263         public bool IsReply
264         {
265             get
266             {
267                 return _IsReply;
268             }
269             set
270             {
271                 _IsReply = value;
272             }
273         }
274         public bool IsExcludeReply
275         {
276             get
277             {
278                 return _IsExcludeReply;
279             }
280             set
281             {
282                 _IsExcludeReply = value;
283             }
284         }
285         public bool IsProtect
286         {
287             get
288             {
289                 return _IsProtect;
290             }
291             set
292             {
293                 if (value)
294                 {
295                     _states = _states | States.Protect;
296                 }
297                 else
298                 {
299                     _states = _states & ~States.Protect;
300                 }
301                 _IsProtect = value;
302             }
303         }
304         public bool IsOwl
305         {
306             get
307             {
308                 return _IsOWL;
309             }
310             set
311             {
312                 _IsOWL = value;
313             }
314         }
315         public bool IsMark
316         {
317             get
318             {
319                 return _IsMark;
320             }
321             set
322             {
323                 if (value)
324                 {
325                     _states = _states | States.Mark;
326                 }
327                 else
328                 {
329                     _states = _states & ~States.Mark;
330                 }
331                 _IsMark = value;
332             }
333         }
334         public string InReplyToUser
335         {
336             get
337             {
338                 return _InReplyToUser;
339             }
340             set
341             {
342                 _InReplyToUser = value;
343             }
344         }
345         public long InReplyToStatusId
346         {
347             get
348             {
349                 return _InReplyToStatusId;
350             }
351             set
352             {
353                 if (value > 0)
354                 {
355                     _states = _states | States.Reply;
356                 }
357                 else
358                 {
359                     _states = _states & ~States.Reply;
360                 }
361                 _InReplyToStatusId = value;
362             }
363         }
364
365         public long InReplyToUserId
366         {
367             get
368             {
369                 return _InReplyToUserId;
370             }
371             set
372             {
373                 _InReplyToUserId = value;
374             }
375         }
376         public string Source
377         {
378             get
379             {
380                 return _Source;
381             }
382             set
383             {
384                 _Source = value;
385             }
386         }
387         public string SourceHtml
388         {
389             get
390             {
391                 return _SourceHtml;
392             }
393             set
394             {
395                 _SourceHtml = value;
396             }
397         }
398         public List<string> ReplyToList
399         {
400             get
401             {
402                 return _ReplyToList;
403             }
404             set
405             {
406                 _ReplyToList = value;
407             }
408         }
409         public bool IsMe
410         {
411             get
412             {
413                 return _IsMe;
414             }
415             set
416             {
417                 _IsMe = value;
418             }
419         }
420         public bool IsDm
421         {
422             get
423             {
424                 return _IsDm;
425             }
426             set
427             {
428                 _IsDm = value;
429             }
430         }
431         //public readonly int StatusIndex
432         //{
433         //    get
434         //    {
435         //        return _statuses;
436         //    }
437         //}
438         public long UserId
439         {
440             get
441             {
442                 return _UserId;
443             }
444             set
445             {
446                 _UserId = value;
447             }
448         }
449         public bool FilterHit
450         {
451             get
452             {
453                 return _FilterHit;
454             }
455             set
456             {
457                 _FilterHit = value;
458             }
459         }
460         public string RetweetedBy
461         {
462             get
463             {
464                 return _RetweetedBy;
465             }
466             set
467             {
468                 _RetweetedBy = value;
469             }
470         }
471         public long RetweetedId
472         {
473             get
474             {
475                 return _RetweetedId;
476             }
477             set
478             {
479                 _RetweetedId = value;
480             }
481         }
482         public string RelTabName
483         {
484             get
485             {
486                 return _SearchTabName;
487             }
488             set
489             {
490                 _SearchTabName = value;
491             }
492         }
493         public bool IsDeleted
494         {
495             get
496             {
497                 return _IsDeleted;
498             }
499             set
500             {
501                 if (value)
502                 {
503                     this.InReplyToStatusId = 0;
504                     this.InReplyToUser = "";
505                     this.InReplyToUserId = 0;
506                     this.IsReply = false;
507                     this.ReplyToList = new List<string>();
508                     this._states = States.None;
509                 }
510                 _IsDeleted = value;
511             }
512         }
513
514         public int FavoritedCount { get; set; }
515
516         public StatusGeo PostGeo
517         {
518             get
519             {
520                 return _postGeo;
521             }
522             set
523             {
524                 if (value != null && (value.Lat != 0 || value.Lng != 0))
525                 {
526                     _states |= States.Geo;
527                 }
528                 else
529                 {
530                     _states &= ~States.Geo;
531                 }
532                 _postGeo = value;
533             }
534         }
535
536         public int StateIndex
537         {
538             get
539             {
540                 return (int)_states - 1;
541             }
542         }
543
544         public PostClass Copy()
545         {
546             var post = (PostClass)((ICloneable)this).Clone();
547             post.ReplyToList = new List<string>(this.ReplyToList);
548             return post;
549         }
550
551         public override bool Equals(object obj)
552         {
553             if (obj == null || this.GetType() != obj.GetType()) return false;
554             return this.Equals((PostClass)obj);
555         }
556
557         public bool Equals(PostClass other)
558         {
559             if (other == null) return false;
560             return (this.Nickname == other.Nickname) &&
561                     (this.TextFromApi == other.TextFromApi) &&
562                     (this.ImageUrl == other.ImageUrl) &&
563                     (this.ScreenName == other.ScreenName) &&
564                     (this.CreatedAt == other.CreatedAt) &&
565                     (this.StatusId == other.StatusId) &&
566                     (this.IsFav == other.IsFav) &&
567                     (this.Text == other.Text) &&
568                     (this.IsRead == other.IsRead) &&
569                     (this.IsReply == other.IsReply) &&
570                     (this.IsExcludeReply == other.IsExcludeReply) &&
571                     (this.IsProtect == other.IsProtect) &&
572                     (this.IsOwl == other.IsOwl) &&
573                     (this.IsMark == other.IsMark) &&
574                     (this.InReplyToUser == other.InReplyToUser) &&
575                     (this.InReplyToStatusId == other.InReplyToStatusId) &&
576                     (this.Source == other.Source) &&
577                     (this.SourceHtml == other.SourceHtml) &&
578                     (this.ReplyToList.Equals(other.ReplyToList)) &&
579                     (this.IsMe == other.IsMe) &&
580                     (this.IsDm == other.IsDm) &&
581                     (this.UserId == other.UserId) &&
582                     (this.FilterHit == other.FilterHit) &&
583                     (this.RetweetedBy == other.RetweetedBy) &&
584                     (this.RetweetedId == other.RetweetedId) &&
585                     (this.RelTabName == other.RelTabName) &&
586                     (this.IsDeleted == other.IsDeleted) &&
587                     (this.InReplyToUserId == other.InReplyToUserId);
588
589         }
590
591         public override int GetHashCode()
592         {
593             return this.StatusId.GetHashCode();
594         }
595
596 #region "IClonable.Clone"
597         object ICloneable.Clone()
598         {
599             return this.MemberwiseClone();
600         }
601 #endregion
602     }
603
604     public sealed class TabInformations
605     {
606         //個別タブの情報をDictionaryで保持
607         private IdComparerClass _sorter;
608         private Dictionary<string, TabClass> _tabs = new Dictionary<string, TabClass>();
609         private Dictionary<long, PostClass> _statuses = new Dictionary<long, PostClass>();
610         private List<long> _addedIds;
611         private List<long> _deletedIds = new List<long>();
612         private Dictionary<long, PostClass> _retweets = new Dictionary<long, PostClass>();
613         private Stack<TabClass> _removedTab = new Stack<TabClass>();
614         private List<ScrubGeoInfo> _scrubGeo = new List<ScrubGeoInfo>();
615
616         private class ScrubGeoInfo
617         {
618             public long UserId = 0;
619             public long UpToStatusId = 0;
620         }
621
622         public List<long> BlockIds = new List<long>();
623
624         //発言の追加
625         //AddPost(複数回) -> DistributePosts          -> SubmitUpdate
626
627         //トランザクション用
628         private int _addCount;
629         private string _soundFile;
630         private List<PostClass> _notifyPosts;
631         private readonly object LockObj = new object();
632         private readonly object LockUnread = new object();
633
634         private static TabInformations _instance = new TabInformations();
635
636         //List
637         private List<ListElement> _lists = new List<ListElement>();
638
639         private TabInformations()
640         {
641             _sorter = new IdComparerClass();
642             RemovedTab = _removedTab;
643         }
644
645         public static TabInformations GetInstance()
646         {
647             return _instance;    //singleton
648         }
649
650         public List<ListElement> SubscribableLists
651         {
652             get
653             {
654                 return _lists;
655             }
656             set
657             {
658                 if (value != null && value.Count > 0)
659                 {
660                     foreach (var tb in this.GetTabsByType(MyCommon.TabUsageType.Lists))
661                     {
662                         foreach (var list in value)
663                         {
664                             if (tb.ListInfo.Id == list.Id)
665                             {
666                                 tb.ListInfo = list;
667                                 break;
668                             }
669                         }
670                     }
671                 }
672                 _lists = value;
673             }
674         }
675
676         public bool AddTab(string TabName, MyCommon.TabUsageType TabType, ListElement List)
677         {
678             if (_tabs.ContainsKey(TabName)) return false;
679             _tabs.Add(TabName, new TabClass(TabName, TabType, List));
680             _tabs[TabName].Sorter.Mode = _sorter.Mode;
681             _tabs[TabName].Sorter.Order = _sorter.Order;
682             return true;
683         }
684
685         //public void AddTab(string TabName, TabClass Tab)
686         //{
687         //    _tabs.Add(TabName, Tab);
688         //}
689
690         public void RemoveTab(string TabName)
691         {
692             lock (LockObj)
693             {
694                 if (IsDefaultTab(TabName)) return; //念のため
695                 if (!_tabs[TabName].IsInnerStorageTabType)
696                 {
697                     var homeTab = GetTabByType(MyCommon.TabUsageType.Home);
698                     var dmName = GetTabByType(MyCommon.TabUsageType.DirectMessage).TabName;
699
700                     for (int idx = 0; idx < _tabs[TabName].AllCount; ++idx)
701                     {
702                         var exist = false;
703                         var Id = _tabs[TabName].GetId(idx);
704                         if (Id < 0) continue;
705                         foreach (var key in _tabs.Keys)
706                         {
707                             if (key != TabName && key != dmName)
708                             {
709                                 if (_tabs[key].Contains(Id))
710                                 {
711                                     exist = true;
712                                     break;
713                                 }
714                             }
715                         }
716                         if (!exist) homeTab.Add(Id, _statuses[Id].IsRead, false);
717                     }
718                 }
719                 _removedTab.Push(_tabs[TabName]);
720                 _tabs.Remove(TabName);
721             }
722         }
723
724         public Stack<TabClass> RemovedTab;
725
726         public bool ContainsTab(string TabText)
727         {
728             return _tabs.ContainsKey(TabText);
729         }
730
731         public bool ContainsTab(TabClass ts)
732         {
733             return _tabs.ContainsValue(ts);
734         }
735
736         public Dictionary<string, TabClass> Tabs
737         {
738             get
739             {
740                 return _tabs;
741             }
742             set
743             {
744                 _tabs = value;
745             }
746         }
747
748         public Dictionary<string, TabClass>.KeyCollection KeysTab
749         {
750             get
751             {
752                 return _tabs.Keys;
753             }
754         }
755
756         public void SortPosts()
757         {
758             foreach (var key in _tabs.Keys)
759             {
760                 _tabs[key].Sort();
761             }
762         }
763
764         public SortOrder SortOrder
765         {
766             get
767             {
768                 return _sorter.Order;
769             }
770             set
771             {
772                 _sorter.Order = value;
773                 foreach (var key in _tabs.Keys)
774                 {
775                     _tabs[key].Sorter.Order = value;
776                 }
777             }
778         }
779
780         public IdComparerClass.ComparerMode SortMode
781         {
782             get
783             {
784                 return _sorter.Mode;
785             }
786             set
787             {
788                 _sorter.Mode = value;
789                 foreach (var key in _tabs.Keys)
790                 {
791                     _tabs[key].Sorter.Mode = value;
792                 }
793             }
794         }
795
796         public SortOrder ToggleSortOrder(IdComparerClass.ComparerMode SortMode)
797         {
798             if (_sorter.Mode == SortMode)
799             {
800                 if (_sorter.Order == SortOrder.Ascending)
801                 {
802                     _sorter.Order = SortOrder.Descending;
803                 }
804                 else
805                 {
806                     _sorter.Order = SortOrder.Ascending;
807                 }
808                 foreach (var key in _tabs.Keys)
809                 {
810                     _tabs[key].Sorter.Order = _sorter.Order;
811                 }
812             }
813             else
814             {
815                 _sorter.Mode = SortMode;
816                 _sorter.Order = SortOrder.Ascending;
817                 foreach (var key in _tabs.Keys)
818                 {
819                     _tabs[key].Sorter.Mode = SortMode;
820                     _tabs[key].Sorter.Order = SortOrder.Ascending;
821                 }
822             }
823             this.SortPosts();
824             return _sorter.Order;
825         }
826
827     //    public PostClass RetweetSource(long Id)
828     //    {
829     //        get
830     //        {
831     //            if (_retweets.ContainsKey(Id))
832     //            {
833     //                return _retweets[Id];
834     //            }
835     //            else
836     //            {
837     //                return null;
838     //            }
839     //        }
840     //    }
841         public PostClass RetweetSource(long Id)
842         {
843             if (_retweets.ContainsKey(Id))
844             {
845                 return _retweets[Id];
846             }
847             else
848             {
849                 return null;
850             }
851         }
852
853         public void RemoveFavPost(long Id)
854         {
855             lock (LockObj)
856             {
857                 PostClass post = null;
858                 var tab = this.GetTabByType(MyCommon.TabUsageType.Favorites);
859                 var tn = tab.TabName;
860                 if (_statuses.ContainsKey(Id))
861                 {
862                     post = _statuses[Id];
863                     //指定タブから該当ID削除
864                     var tType = tab.TabType;
865                     if (tab.Contains(Id))
866                     {
867                         if (tab.UnreadManage && !post.IsRead)    //未読管理
868                         {
869                             lock (LockUnread)
870                             {
871                                 tab.UnreadCount--;
872                                 this.SetNextUnreadId(Id, tab);
873                             }
874                         }
875                         tab.Remove(Id);
876                     }
877                     //FavタブからRetweet発言を削除する場合は、他の同一参照Retweetも削除
878                     if (tType == MyCommon.TabUsageType.Favorites && post.RetweetedId > 0)
879                     {
880                         for (int i = 0; i < tab.AllCount; i++)
881                         {
882                             PostClass rPost = null;
883                             try
884                             {
885                                 rPost = this[tn, i];
886                             }
887                             catch (ArgumentOutOfRangeException)
888                             {
889                                 break;
890                             }
891                             if (rPost.RetweetedId > 0 && rPost.RetweetedId == post.RetweetedId)
892                             {
893                                 if (tab.UnreadManage && !rPost.IsRead)    //未読管理
894                                 {
895                                     lock (LockUnread)
896                                     {
897                                         tab.UnreadCount--;
898                                         this.SetNextUnreadId(rPost.StatusId, tab);
899                                     }
900                                 }
901                                 tab.Remove(rPost.StatusId);
902                             }
903                         }
904                     }
905                 }
906                 //TabType=PublicSearchの場合(Postの保存先がTabClass内)
907                 //if (tab.Contains(StatusId) &&
908                 //   (tab.TabType = MyCommon.TabUsageType.PublicSearch || tab.TabType = MyCommon.TabUsageType.DirectMessage))
909                 //{
910                 //    post = tab.Posts[StatusId];
911                 //    if (tab.UnreadManage && !post.IsRead)    //未読管理
912                 //    {
913                 //        lock (LockUnread)
914                 //        {
915                 //            tab.UnreadCount--;
916                 //            this.SetNextUnreadId(StatusId, tab);
917                 //        }
918                 //    }
919                 //    tab.Remove(StatusId);
920                 //}
921             }
922         }
923
924         public void ScrubGeoReserve(long id, long upToStatusId)
925         {
926             lock (LockObj)
927             {
928                 //this._scrubGeo.Add(new ScrubGeoInfo With {.UserId = id, .UpToStatusId = upToStatusId});
929                 this.ScrubGeo(id, upToStatusId);
930             }
931         }
932
933         private void ScrubGeo(long userId, long upToStatusId)
934         {
935             lock (LockObj)
936             {
937                 var userPosts = from post in this._statuses.Values
938                                 where post.UserId == userId && post.UserId <= upToStatusId
939                                 select post;
940
941                 foreach (var p in userPosts)
942                 {
943                     p.PostGeo = new PostClass.StatusGeo();
944                 }
945
946                 var userPosts2 = from tb in this.GetTabsInnerStorageType()
947                                  from post in tb.Posts.Values
948                                  where post.UserId == userId && post.UserId <= upToStatusId
949                                  select post;
950
951                 foreach (var p in userPosts2)
952                 {
953                     p.PostGeo = new PostClass.StatusGeo();
954                 }
955             }
956         }
957
958         public void RemovePostReserve(long id)
959         {
960             lock (LockObj)
961             {
962                 this._deletedIds.Add(id);
963                 this.DeletePost(id);   //UI選択行がずれるため、RemovePostは使用しない
964             }
965         }
966
967         public void RemovePost(long Id)
968         {
969             lock (LockObj)
970             {
971                 PostClass post = null;
972                 //if (_statuses.ContainsKey(Id))
973                 //各タブから該当ID削除
974                 foreach (var key in _tabs.Keys)
975                 {
976                     var tab = _tabs[key];
977                     if (tab.Contains(Id))
978                     {
979                         if (!tab.IsInnerStorageTabType)
980                         {
981                             post = _statuses[Id];
982                             if (tab.UnreadManage && !post.IsRead)    //未読管理
983                             {
984                                 lock (LockUnread)
985                                 {
986                                     tab.UnreadCount--;
987                                     this.SetNextUnreadId(Id, tab);
988                                 }
989                             }
990                         }
991                         else //未読数がずれる可能性があるためtab.Postsの未読も確認する
992                         {
993                             if (tab.UnreadManage && !tab.Posts[Id].IsRead)    //未読管理
994                             {
995                                 lock (LockUnread)
996                                 {
997                                     tab.UnreadCount--;
998                                     this.SetNextUnreadId(Id, tab);
999                                 }
1000                             }
1001                         }
1002                         tab.Remove(Id);
1003                     }
1004                 }
1005                 if (_statuses.ContainsKey(Id)) _statuses.Remove(Id);
1006             }
1007         }
1008
1009         private void DeletePost(long Id)
1010         {
1011             lock (LockObj)
1012             {
1013                 PostClass post = null;
1014                 if (_statuses.ContainsKey(Id))
1015                 {
1016                     post = _statuses[Id];
1017                     post.IsDeleted = true;
1018                 }
1019                 foreach (var tb in this.GetTabsInnerStorageType())
1020                 {
1021                     if (tb.Contains(Id))
1022                     {
1023                         post = tb.Posts[Id];
1024                         post.IsDeleted = true;
1025                     }
1026                 }
1027             }
1028         }
1029
1030         public int GetOldestUnreadIndex(string TabName)
1031         {
1032             var tb = _tabs[TabName];
1033             if (tb.OldestUnreadId > -1 &&
1034                 tb.Contains(tb.OldestUnreadId) &&
1035                 tb.UnreadCount > 0)
1036             {
1037                 //未読アイテムへ
1038                 bool isRead;
1039                 if (!tb.IsInnerStorageTabType)
1040                 {
1041                     isRead = _statuses[tb.OldestUnreadId].IsRead;
1042                 }
1043                 else
1044                 {
1045                     isRead = tb.Posts[tb.OldestUnreadId].IsRead;
1046                 }
1047                 if (isRead)
1048                 {
1049                     //状態不整合(最古未読IDが実は既読)
1050                     lock (LockUnread)
1051                     {
1052                         this.SetNextUnreadId(-1, tb);  //頭から探索
1053                     }
1054                     if (tb.OldestUnreadId == -1)
1055                     {
1056                         return -1;
1057                     }
1058                     else
1059                     {
1060                         return tb.IndexOf(tb.OldestUnreadId);
1061                     }
1062                 }
1063                 else
1064                 {
1065                     return tb.IndexOf(tb.OldestUnreadId);    //最短経路;
1066                 }
1067             }
1068             else
1069             {
1070                 //一見未読なさそうだが、未読カウントはあるので探索
1071                 //if (tb.UnreadCount > 0)
1072                 if (!(tb.UnreadManage && AppendSettingDialog.Instance.UnreadManage)) return -1;
1073                 lock (LockUnread)
1074                 {
1075                     this.SetNextUnreadId(-1, tb);
1076                 }
1077                 if (tb.OldestUnreadId == -1)
1078                 {
1079                     return -1;
1080                 }
1081                 else
1082                 {
1083                     return tb.IndexOf(tb.OldestUnreadId);
1084                 }
1085                 //else
1086                 //{
1087                 //    return -1;
1088                 //}
1089             }
1090         }
1091
1092         private void SetNextUnreadId(long CurrentId, TabClass Tab)
1093         {
1094             //CurrentID:今既読にしたID(OldestIDの可能性あり)
1095             //最古未読が設定されていて、既読の場合(1発言以上存在)
1096             try
1097             {
1098                 Dictionary<long, PostClass> posts;
1099                 if (!Tab.IsInnerStorageTabType)
1100                 {
1101                     posts = _statuses;
1102                 }
1103                 else
1104                 {
1105                     posts = Tab.Posts;
1106                 }
1107                 if (Tab.OldestUnreadId > -1 &&
1108                     posts.ContainsKey(Tab.OldestUnreadId) &&
1109                     posts[Tab.OldestUnreadId].IsRead &&
1110                     _sorter.Mode == IdComparerClass.ComparerMode.Id)     //次の未読探索
1111                 {
1112                     if (Tab.UnreadCount == 0)
1113                     {
1114                         //未読数0→最古未読なし
1115                         Tab.OldestUnreadId = -1;
1116                     }
1117                     else if (Tab.OldestUnreadId == CurrentId && CurrentId > -1)
1118                     {
1119                         //最古IDを既読にしたタイミング→次のIDから続けて探索
1120                         var idx = Tab.IndexOf(CurrentId);
1121                         if (idx > -1)
1122                         {
1123                             //続きから探索
1124                             FindUnreadId(idx, Tab);
1125                         }
1126                         else
1127                         {
1128                             //頭から探索
1129                             FindUnreadId(-1, Tab);
1130                         }
1131                     }
1132                     else
1133                     {
1134                         //頭から探索
1135                         FindUnreadId(-1, Tab);
1136                     }
1137                 }
1138                 else
1139                 {
1140                     //頭から探索
1141                     FindUnreadId(-1, Tab);
1142                 }
1143             }
1144             catch (KeyNotFoundException)
1145             {
1146                 //頭から探索
1147                 FindUnreadId(-1, Tab);
1148             }
1149         }
1150
1151         private void FindUnreadId(int StartIdx, TabClass Tab)
1152         {
1153             if (Tab.AllCount == 0)
1154             {
1155                 Tab.OldestUnreadId = -1;
1156                 Tab.UnreadCount = 0;
1157                 return;
1158             }
1159             var toIdx = 0;
1160             var stp = 1;
1161             Tab.OldestUnreadId = -1;
1162             if (_sorter.Order == SortOrder.Ascending)
1163             {
1164                 if (StartIdx == -1)
1165                 {
1166                     StartIdx = 0;
1167                 }
1168                 else
1169                 {
1170                     //StartIdx++;
1171                     if (StartIdx > Tab.AllCount - 1) StartIdx = Tab.AllCount - 1; //念のため
1172                 }
1173                 toIdx = Tab.AllCount - 1;
1174                 if (toIdx < 0) toIdx = 0; //念のため
1175                 stp = 1;
1176             }
1177             else
1178             {
1179                 if (StartIdx == -1)
1180                 {
1181                     StartIdx = Tab.AllCount - 1;
1182                 }
1183                 else
1184                 {
1185                     //StartIdx--;
1186                 }
1187                 if (StartIdx < 0) StartIdx = 0; //念のため
1188                 toIdx = 0;
1189                 stp = -1;
1190             }
1191
1192             Dictionary<long, PostClass> posts;
1193             if (!Tab.IsInnerStorageTabType)
1194             {
1195                 posts = _statuses;
1196             }
1197             else
1198             {
1199                 posts = Tab.Posts;
1200             }
1201
1202             for (int i = StartIdx; ; i+= stp)
1203             {
1204                 var id = Tab.GetId(i);
1205                 if (id > -1 && !posts[id].IsRead)
1206                 {
1207                     Tab.OldestUnreadId = id;
1208                     break;
1209                 }
1210
1211                 if (i == toIdx) break;
1212             }
1213         }
1214
1215         public int DistributePosts()
1216         {
1217             lock (LockObj)
1218             {
1219                 //戻り値は追加件数
1220                 //if (_addedIds == null) return 0;
1221                 //if (_addedIds.Count == 0) return 0;
1222
1223                 if (_addedIds == null) _addedIds = new List<long>();
1224                 if (_notifyPosts == null) _notifyPosts = new List<PostClass>();
1225                 try
1226                 {
1227                     this.Distribute();    //タブに仮振分
1228                 }
1229                 catch (KeyNotFoundException)
1230                 {
1231                     //タブ変更により振分が失敗した場合
1232                 }
1233                 var retCnt = _addedIds.Count;
1234                 _addCount += retCnt;
1235                 _addedIds.Clear();
1236                 _addedIds = null;     //後始末
1237                 return retCnt;     //件数
1238             }
1239         }
1240
1241         public int SubmitUpdate(ref string soundFile,
1242                                 ref PostClass[] notifyPosts,
1243                                 ref bool isMentionIncluded,
1244                                 ref bool isDeletePost,
1245                                 bool isUserStream)
1246         {
1247             //注:メインスレッドから呼ぶこと
1248             lock (LockObj)
1249             {
1250                 if (_notifyPosts == null)
1251                 {
1252                     soundFile = "";
1253                     notifyPosts = null;
1254                     return 0;
1255                 }
1256
1257                 foreach (var tb in _tabs.Values)
1258                 {
1259                     if (tb.IsInnerStorageTabType)
1260                     {
1261                         _addCount += tb.GetTemporaryCount();
1262                     }
1263                     tb.AddSubmit(ref isMentionIncluded);  //振分確定(各タブに反映)
1264                 }
1265                 ////UserStreamで反映間隔10秒以下だったら、30秒ごとにソートする
1266                 ////10秒以上だったら毎回ソート
1267                 //static DateTime lastSort = DateTime.Now;
1268                 //if (AppendSettingDialog.Instance.UserstreamPeriodInt < 10 && isUserStream)
1269                 //{
1270                 //    if (Now.Subtract(lastSort) > TimeSpan.FromSeconds(30))
1271                 //    {
1272                 //        lastSort = DateTime.Now;
1273                 //        isUserStream = false;
1274                 //    }
1275                 //}
1276                 //else
1277                 //{
1278                 //    isUserStream = false;
1279                 //}
1280                 if (!isUserStream || this.SortMode != IdComparerClass.ComparerMode.Id)
1281                 {
1282                     this.SortPosts();
1283                 }
1284                 if (isUserStream)
1285                 {
1286                     isDeletePost = this._deletedIds.Count > 0;
1287                     foreach (var id in this._deletedIds)
1288                     {
1289                         //this.DeletePost(StatusId)
1290                         this.RemovePost(id);
1291                     }
1292                     this._deletedIds.Clear();
1293                 }
1294
1295                 soundFile = _soundFile;
1296                 _soundFile = "";
1297                 notifyPosts = _notifyPosts.ToArray();
1298                 _notifyPosts.Clear();
1299                 _notifyPosts = null;
1300                 var retCnt = _addCount;
1301                 _addCount = 0;
1302                 return retCnt;    //件数(EndUpdateの戻り値と同じ)
1303             }
1304         }
1305
1306         private void Distribute()
1307         {
1308             //各タブのフィルターと照合。合致したらタブにID追加
1309             //通知メッセージ用に、表示必要な発言リストと再生サウンドを返す
1310             //notifyPosts = new List<PostClass>();
1311             var homeTab = GetTabByType(MyCommon.TabUsageType.Home);
1312             var replyTab = GetTabByType(MyCommon.TabUsageType.Mentions);
1313             var dmTab = GetTabByType(MyCommon.TabUsageType.DirectMessage);
1314             var favTab = GetTabByType(MyCommon.TabUsageType.Favorites);
1315             foreach (var id in _addedIds)
1316             {
1317                 var post = _statuses[id];
1318                 var add = false;  //通知リスト追加フラグ
1319                 var mv = false;   //移動フラグ(Recent追加有無)
1320                 var rslt = MyCommon.HITRESULT.None;
1321                 post.IsExcludeReply = false;
1322                 foreach (var tn in _tabs.Keys)
1323                 {
1324                     rslt = _tabs[tn].AddFiltered(post);
1325                     if (rslt != MyCommon.HITRESULT.None && rslt != MyCommon.HITRESULT.Exclude)
1326                     {
1327                         if (rslt == MyCommon.HITRESULT.CopyAndMark) post.IsMark = true; //マークあり
1328                         if (rslt == MyCommon.HITRESULT.Move)
1329                         {
1330                             mv = true; //移動
1331                             post.IsMark = false;
1332                         }
1333                         if (_tabs[tn].Notify) add = true; //通知あり
1334                         if (_tabs[tn].SoundFile != "" && _soundFile == "")
1335                         {
1336                             _soundFile = _tabs[tn].SoundFile; //wavファイル(未設定の場合のみ)
1337                         }
1338                         post.FilterHit = true;
1339                     }
1340                     else
1341                     {
1342                         if (rslt == MyCommon.HITRESULT.Exclude && _tabs[tn].TabType == MyCommon.TabUsageType.Mentions)
1343                         {
1344                             post.IsExcludeReply = true;
1345                         }
1346                         post.FilterHit = false;
1347                     }
1348                 }
1349                 if (!mv)  //移動されなかったらRecentに追加
1350                 {
1351                     homeTab.Add(post.StatusId, post.IsRead, true);
1352                     if (homeTab.SoundFile != "" && _soundFile == "") _soundFile = homeTab.SoundFile;
1353                     if (homeTab.Notify) add = true;
1354                 }
1355                 if (post.IsReply && !post.IsExcludeReply)    //除外ルール適用のないReplyならReplyタブに追加
1356                 {
1357                     replyTab.Add(post.StatusId, post.IsRead, true);
1358                     if (replyTab.SoundFile != "") _soundFile = replyTab.SoundFile;
1359                     if (replyTab.Notify) add = true;
1360                 }
1361                 if (post.IsFav)    //Fav済み発言だったらFavoritesタブに追加
1362                 {
1363                     if (favTab.Contains(post.StatusId))
1364                     {
1365                         //取得済みなら非通知
1366                         //_soundFile = "";
1367                         add = false;
1368                     }
1369                     else
1370                     {
1371                         favTab.Add(post.StatusId, post.IsRead, true);
1372                         if (!string.IsNullOrEmpty(favTab.SoundFile) && string.IsNullOrEmpty(_soundFile)) _soundFile = favTab.SoundFile;
1373                         if (favTab.Notify) add = true;
1374                     }
1375                 }
1376                 if (add) _notifyPosts.Add(post);
1377             }
1378             foreach (var tb in _tabs.Values)
1379             {
1380                 if (tb.IsInnerStorageTabType)
1381                 {
1382                     if (tb.Notify)
1383                     {
1384                         if (tb.GetTemporaryCount() > 0)
1385                         {
1386                             foreach (var post in tb.GetTemporaryPosts())
1387                             {
1388                                 var exist = false;
1389                                 foreach (var npost in _notifyPosts)
1390                                 {
1391                                     if (npost.StatusId == post.StatusId)
1392                                     {
1393                                         exist = true;
1394                                         break;
1395                                     }
1396                                 }
1397                                 if (!exist) _notifyPosts.Add(post);
1398                             }
1399                             if (tb.SoundFile != "")
1400                             {
1401                                 if (tb.TabType == MyCommon.TabUsageType.DirectMessage || _soundFile == "")
1402                                 {
1403                                     _soundFile = tb.SoundFile;
1404                                 }
1405                             }
1406                         }
1407                     }
1408                 }
1409             }
1410         }
1411
1412         public void AddPost(PostClass Item)
1413         {
1414             lock (LockObj)
1415             {
1416                 if (Item.RelTabName == "")
1417                 {
1418                     if (!Item.IsDm)
1419                     {
1420                         if (_statuses.ContainsKey(Item.StatusId))
1421                         {
1422                             if (Item.IsFav)
1423                             {
1424                                 if (Item.RetweetedId == 0)
1425                                 {
1426                                     _statuses[Item.StatusId].IsFav = true;
1427                                 }
1428                                 else
1429                                 {
1430                                     Item.IsFav = false;
1431                                 }
1432                             }
1433                             else
1434                             {
1435                                 return;        //追加済みなら何もしない
1436                             }
1437                         }
1438                         else
1439                         {
1440                             if (Item.IsFav && Item.RetweetedId > 0) Item.IsFav = false;
1441                             //既に持っている公式RTは捨てる
1442                             if (AppendSettingDialog.Instance.HideDuplicatedRetweets &&
1443                                 !Item.IsMe &&
1444                                 this._retweets.ContainsKey(Item.RetweetedId) &&
1445                                 this._retweets[Item.RetweetedId].RetweetedCount > 0) return;
1446                             if (BlockIds.Contains(Item.UserId)) return;
1447                             _statuses.Add(Item.StatusId, Item);
1448                         }
1449                         if (Item.RetweetedId > 0)
1450                         {
1451                             this.AddRetweet(Item);
1452                         }
1453                         if (Item.IsFav && _retweets.ContainsKey(Item.StatusId))
1454                         {
1455                             return;    //Fav済みのRetweet元発言は追加しない
1456                         }
1457                         if (_addedIds == null) _addedIds = new List<long>(); //タブ追加用IDコレクション準備
1458                         _addedIds.Add(Item.StatusId);
1459                     }
1460                     else
1461                     {
1462                         //DM
1463                         var tb = this.GetTabByType(MyCommon.TabUsageType.DirectMessage);
1464                         if (tb.Contains(Item.StatusId)) return;
1465                         tb.AddPostToInnerStorage(Item);
1466                     }
1467                 }
1468                 else
1469                 {
1470                     //公式検索、リスト、関連発言の場合
1471                     TabClass tb;
1472                     if (this.Tabs.ContainsKey(Item.RelTabName))
1473                     {
1474                         tb = this.Tabs[Item.RelTabName];
1475                     }
1476                     else
1477                     {
1478                         return;
1479                     }
1480                     if (tb == null) return;
1481                     if (tb.Contains(Item.StatusId)) return;
1482                     //tb.Add(Item.StatusId, Item.IsRead, true);
1483                     tb.AddPostToInnerStorage(Item);
1484                 }
1485             }
1486         }
1487
1488         private void AddRetweet(PostClass item)
1489         {
1490             //true:追加、False:保持済み
1491             if (_retweets.ContainsKey(item.RetweetedId))
1492             {
1493                 _retweets[item.RetweetedId].RetweetedCount++;
1494                 if (_retweets[item.RetweetedId].RetweetedCount > 10)
1495                 {
1496                     _retweets[item.RetweetedId].RetweetedCount = 0;
1497                 }
1498                 return;
1499             }
1500
1501             _retweets.Add(
1502                         item.RetweetedId,
1503                         new PostClass(
1504                             item.Nickname,
1505                             item.TextFromApi,
1506                             item.Text,
1507                             item.ImageUrl,
1508                             item.ScreenName,
1509                             item.CreatedAt,
1510                             item.RetweetedId,
1511                             item.IsFav,
1512                             item.IsRead,
1513                             item.IsReply,
1514                             item.IsExcludeReply,
1515                             item.IsProtect,
1516                             item.IsOwl,
1517                             item.IsMark,
1518                             item.InReplyToUser,
1519                             item.InReplyToStatusId,
1520                             item.Source,
1521                             item.SourceHtml,
1522                             item.ReplyToList,
1523                             item.IsMe,
1524                             item.IsDm,
1525                             item.UserId,
1526                             item.FilterHit,
1527                             "",
1528                             0,
1529                             item.PostGeo
1530                         )
1531                     );
1532             _retweets[item.RetweetedId].RetweetedCount++;
1533         }
1534
1535         public void SetReadAllTab(bool Read, string TabName, int Index)
1536         {
1537             //Read:true=既読へ false=未読へ
1538             var tb = _tabs[TabName];
1539
1540             if (tb.UnreadManage == false) return; //未読管理していなければ終了
1541
1542             var Id = tb.GetId(Index);
1543             if (Id < 0) return;
1544             PostClass post;
1545             if (!tb.IsInnerStorageTabType)
1546             {
1547                 post = _statuses[Id];
1548             }
1549             else
1550             {
1551                 post = tb.Posts[Id];
1552             }
1553
1554             if (post.IsRead == Read) return; //状態変更なければ終了
1555
1556             post.IsRead = Read;
1557
1558             lock (LockUnread)
1559             {
1560                 if (Read)
1561                 {
1562                     tb.UnreadCount--;
1563                     this.SetNextUnreadId(Id, tb);  //次の未読セット
1564                     //他タブの最古未読IDはタブ切り替え時に。
1565                     if (tb.IsInnerStorageTabType)
1566                     {
1567                         //一般タブ
1568                         if (_statuses.ContainsKey(Id) && !_statuses[Id].IsRead)
1569                         {
1570                             foreach (var key in _tabs.Keys)
1571                             {
1572                                 if (_tabs[key].UnreadManage &&
1573                                     _tabs[key].Contains(Id) &&
1574                                     !_tabs[key].IsInnerStorageTabType)
1575                                 {
1576                                     _tabs[key].UnreadCount--;
1577                                     if (_tabs[key].OldestUnreadId == Id) _tabs[key].OldestUnreadId = -1;
1578                                 }
1579                             }
1580                             _statuses[Id].IsRead = true;
1581                         }
1582                     }
1583                     else
1584                     {
1585                         //一般タブ
1586                         foreach (var key in _tabs.Keys)
1587                         {
1588                             if (key != TabName &&
1589                                 _tabs[key].UnreadManage &&
1590                                 _tabs[key].Contains(Id) &&
1591                                 !_tabs[key].IsInnerStorageTabType)
1592                             {
1593                                 _tabs[key].UnreadCount--;
1594                                 if (_tabs[key].OldestUnreadId == Id) _tabs[key].OldestUnreadId = -1;
1595                             }
1596                         }
1597                     }
1598                     //内部保存タブ
1599                     foreach (var key in _tabs.Keys)
1600                     {
1601                         if (key != TabName &&
1602                             _tabs[key].Contains(Id) &&
1603                             _tabs[key].IsInnerStorageTabType &&
1604                             !_tabs[key].Posts[Id].IsRead)
1605                         {
1606                             if (_tabs[key].UnreadManage)
1607                             {
1608                                 _tabs[key].UnreadCount--;
1609                                 if (_tabs[key].OldestUnreadId == Id) _tabs[key].OldestUnreadId = -1;
1610                             }
1611                             _tabs[key].Posts[Id].IsRead = true;
1612                         }
1613                     }
1614                 }
1615                 else
1616                 {
1617                     tb.UnreadCount++;
1618                     //if (tb.OldestUnreadId > Id || tb.OldestUnreadId = -1) tb.OldestUnreadId = Id
1619                     if (tb.OldestUnreadId > Id) tb.OldestUnreadId = Id;
1620                     if (tb.IsInnerStorageTabType)
1621                     {
1622                         //一般タブ
1623                         if (_statuses.ContainsKey(Id) && _statuses[Id].IsRead)
1624                         {
1625                             foreach (var key in _tabs.Keys)
1626                             {
1627                                 if (_tabs[key].UnreadManage &&
1628                                    _tabs[key].Contains(Id) &&
1629                                    !_tabs[key].IsInnerStorageTabType)
1630                                     _tabs[key].UnreadCount++;
1631                                 {
1632                                     if (_tabs[key].OldestUnreadId > Id) _tabs[key].OldestUnreadId = Id;
1633                                 }
1634                             }
1635                             _statuses[Id].IsRead = false;
1636                         }
1637                     }
1638                     else
1639                     {
1640                         //一般タブ
1641                         foreach (var key in _tabs.Keys)
1642                         {
1643                             if (key != TabName &&
1644                                _tabs[key].UnreadManage &&
1645                                _tabs[key].Contains(Id) &&
1646                                !_tabs[key].IsInnerStorageTabType)
1647                             {
1648                                 _tabs[key].UnreadCount++;
1649                                 if (_tabs[key].OldestUnreadId > Id) _tabs[key].OldestUnreadId = Id;
1650                             }
1651                         }
1652                     }
1653                     //内部保存タブ
1654                     foreach (var key in _tabs.Keys)
1655                     {
1656                         if (key != TabName &&
1657                             _tabs[key].Contains(Id) &&
1658                             _tabs[key].IsInnerStorageTabType &&
1659                             _tabs[key].Posts[Id].IsRead)
1660                         {
1661                             if (_tabs[key].UnreadManage)
1662                             {
1663                                 _tabs[key].UnreadCount++;
1664                                 if (_tabs[key].OldestUnreadId > Id) _tabs[key].OldestUnreadId = Id;
1665                             }
1666                             _tabs[key].Posts[Id].IsRead = false;
1667                         }
1668                     }
1669                 }
1670             }
1671         }
1672
1673         // TODO: パフォーマンスを勘案して、戻すか決める
1674         public void SetRead(bool Read, string TabName, int Index)
1675         {
1676             //Read:true=既読へ false=未読へ
1677             var tb = _tabs[TabName];
1678
1679             if (tb.UnreadManage == false) return; //未読管理していなければ終了
1680
1681             var Id = tb.GetId(Index);
1682             if (Id < 0) return;
1683             PostClass post;
1684             if (!tb.IsInnerStorageTabType)
1685             {
1686                 post = _statuses[Id];
1687             }
1688             else
1689             {
1690                 post = tb.Posts[Id];
1691             }
1692
1693             if (post.IsRead = Read) return; //状態変更なければ終了
1694
1695             post.IsRead = Read; //指定の状態に変更
1696
1697             lock (LockUnread)
1698             {
1699                 if (Read)
1700                 {
1701                     tb.UnreadCount--;
1702                     this.SetNextUnreadId(Id, tb);  //次の未読セット
1703                     //他タブの最古未読IDはタブ切り替え時に。
1704                     if (tb.IsInnerStorageTabType) return;
1705                     foreach (var key in _tabs.Keys)
1706                     {
1707                         if (key != TabName &&
1708                             _tabs[key].UnreadManage &&
1709                             _tabs[key].Contains(Id) &&
1710                             !_tabs[key].IsInnerStorageTabType)
1711                         {
1712                             _tabs[key].UnreadCount--;
1713                             if (_tabs[key].OldestUnreadId == Id) _tabs[key].OldestUnreadId = -1;
1714                         }
1715                     }
1716                 }
1717                 else
1718                 {
1719                     tb.UnreadCount++;
1720                     //if (tb.OldestUnreadId > Id || tb.OldestUnreadId == -1) tb.OldestUnreadId = Id;
1721                     if (tb.OldestUnreadId > Id) tb.OldestUnreadId = Id;
1722                     if (tb.IsInnerStorageTabType) return;
1723                     foreach (var key in _tabs.Keys)
1724                     {
1725                         if (key != TabName &&
1726                             _tabs[key].UnreadManage &&
1727                             _tabs[key].Contains(Id) &&
1728                             !_tabs[key].IsInnerStorageTabType)
1729                         {
1730                             _tabs[key].UnreadCount++;
1731                             if (_tabs[key].OldestUnreadId > Id) _tabs[key].OldestUnreadId = Id;
1732                         }
1733                     }
1734                 }
1735             }
1736         }
1737
1738         public void SetRead()
1739         {
1740             var tb = GetTabByType(MyCommon.TabUsageType.Home);
1741             if (tb.UnreadManage == false) return;
1742
1743             lock (LockObj)
1744             {
1745                 for (int i = 0; i < tb.AllCount - 1; i++)
1746                 {
1747                     var id = tb.GetId(i);
1748                     if (id < 0) return;
1749                     if (!_statuses[id].IsReply &&
1750                         !_statuses[id].IsRead &&
1751                         !_statuses[id].FilterHit)
1752                     {
1753                         _statuses[id].IsRead = true;
1754                         this.SetNextUnreadId(id, tb);  //次の未読セット
1755                         foreach (var key in _tabs.Keys)
1756                         {
1757                             if (_tabs[key].UnreadManage &&
1758                                 _tabs[key].Contains(id))
1759                             {
1760                                 _tabs[key].UnreadCount--;
1761                                 if (_tabs[key].OldestUnreadId == id) _tabs[key].OldestUnreadId = -1;
1762                             }
1763                         }
1764                     }
1765                 }
1766             }
1767         }
1768
1769         public PostClass this[long ID]
1770         {
1771             get
1772             {
1773                 if (_statuses.ContainsKey(ID)) return _statuses[ID];
1774                 foreach (var tb in this.GetTabsInnerStorageType())
1775                 {
1776                     if (tb.Contains(ID))
1777                     {
1778                         return tb.Posts[ID];
1779                     }
1780                 }
1781                 return null;
1782             }
1783         }
1784
1785         public PostClass this[string TabName, int Index]
1786         {
1787             get
1788             {
1789                 if (!_tabs.ContainsKey(TabName)) throw new ArgumentException("TabName=" + TabName + " is not contained.");
1790                 var id = _tabs[TabName].GetId(Index);
1791                 if (id < 0) throw new ArgumentException("Index can//t find. Index=" + Index.ToString() + "/TabName=" + TabName);
1792                 try
1793                 {
1794                     if (_tabs[TabName].IsInnerStorageTabType)
1795                     {
1796                         return _tabs[TabName].Posts[_tabs[TabName].GetId(Index)];
1797                     }
1798                     else
1799                     {
1800                         return _statuses[_tabs[TabName].GetId(Index)];
1801                     }
1802                 }
1803                 catch (Exception ex)
1804                 {
1805                     throw new Exception("Index=" + Index.ToString() + "/TabName=" + TabName, ex);
1806                 }
1807             }
1808         }
1809
1810         public PostClass[] this[string TabName, int StartIndex, int EndIndex]
1811         {
1812             get
1813             {
1814                 var length = EndIndex - StartIndex + 1;
1815                 var posts = new PostClass[length];
1816                 if (_tabs[TabName].IsInnerStorageTabType)
1817                 {
1818                     for (int i = 0; i < length; i++)
1819                     {
1820                         posts[i] = _tabs[TabName].Posts[_tabs[TabName].GetId(StartIndex + i)];
1821                     }
1822                 }
1823                 else
1824                 {
1825                     for (int i = 0; i < length; i++)
1826                     {
1827                         posts[i] = _statuses[_tabs[TabName].GetId(StartIndex + i)];
1828                     }
1829                 }
1830                 return posts;
1831             }
1832         }
1833
1834         //public ReadOnly int ItemCount
1835         //{
1836         //    get
1837         //    {
1838         //        lock (LockObj)
1839         //    {
1840         //            return _statuses.Count   //DM,公式検索は除く
1841         //        }
1842         //    }
1843         //}
1844
1845         public bool ContainsKey(long Id)
1846         {
1847             //DM,公式検索は非対応
1848             lock (LockObj)
1849             {
1850                 return _statuses.ContainsKey(Id);
1851             }
1852         }
1853
1854         public bool ContainsKey(long Id, string TabName)
1855         {
1856             //DM,公式検索は対応版
1857             lock (LockObj)
1858             {
1859                 if (_tabs.ContainsKey(TabName))
1860                 {
1861                     return _tabs[TabName].Contains(Id);
1862                 }
1863                 else
1864                 {
1865                     return false;
1866                 }
1867             }
1868         }
1869
1870         public void SetUnreadManage(bool Manage)
1871         {
1872             if (Manage)
1873             {
1874                 foreach (var key in _tabs.Keys)
1875                 {
1876                     var tb = _tabs[key];
1877                     if (tb.UnreadManage)
1878                     {
1879                         lock (LockUnread)
1880                         {
1881                             var cnt = 0;
1882                             var oldest = long.MaxValue;
1883                             Dictionary<long, PostClass> posts;
1884                             if (!tb.IsInnerStorageTabType)
1885                             {
1886                                 posts = _statuses;
1887                             }
1888                             else
1889                             {
1890                                 posts = tb.Posts;
1891                             }
1892                             foreach (var id in tb.BackupIds)
1893                             {
1894                                 if (!posts[id].IsRead)
1895                                 {
1896                                     cnt++;
1897                                     if (oldest > id) oldest = id;
1898                                 }
1899                             }
1900                             if (oldest == long.MaxValue) oldest = -1;
1901                             tb.OldestUnreadId = oldest;
1902                             tb.UnreadCount = cnt;
1903                         }
1904                     }
1905                 }
1906             }
1907             else
1908             {
1909                 foreach (var key in _tabs.Keys)
1910                 {
1911                     var tb = _tabs[key];
1912                     if (tb.UnreadManage && tb.UnreadCount > 0)
1913                     {
1914                         lock (LockUnread)
1915                         {
1916                             tb.UnreadCount = 0;
1917                             tb.OldestUnreadId = -1;
1918                         }
1919                     }
1920                 }
1921             }
1922         }
1923
1924         public void RenameTab(string Original, string NewName)
1925         {
1926             var tb = _tabs[Original];
1927             _tabs.Remove(Original);
1928             tb.TabName = NewName;
1929             _tabs.Add(NewName, tb);
1930         }
1931
1932         public void FilterAll()
1933         {
1934             lock (LockObj)
1935             {
1936                 var tbr = GetTabByType(MyCommon.TabUsageType.Home);
1937                 var replyTab = GetTabByType(MyCommon.TabUsageType.Mentions);
1938                 foreach (var tb in _tabs.Values.ToArray())
1939                 {
1940                     if (tb.FilterModified)
1941                     {
1942                         tb.FilterModified = false;
1943                         var orgIds = tb.BackupIds;
1944                         tb.ClearIDs();
1945                         //////////////フィルター前のIDsを退避。どのタブにも含まれないidはrecentへ追加
1946                         //////////////moveフィルターにヒットした際、recentに該当あればrecentから削除
1947                         foreach (var id in _statuses.Keys)
1948                         {
1949                             var post = _statuses[id];
1950                             if (post.IsDm) continue;
1951                             var rslt = MyCommon.HITRESULT.None;
1952                             rslt = tb.AddFiltered(post);
1953                             switch (rslt)
1954                             {
1955                                 case MyCommon.HITRESULT.CopyAndMark:
1956                                 post.IsMark = true; //マークあり
1957                                 post.FilterHit = true;
1958                                 break;
1959                                 case MyCommon.HITRESULT.Move:
1960                                 tbr.Remove(post.StatusId, post.IsRead);
1961                                 post.IsMark = false;
1962                                 post.FilterHit = true;
1963                                 break;
1964                             case MyCommon.HITRESULT.Copy:
1965                                 post.IsMark = false;
1966                                 post.FilterHit = true;
1967                                 break;
1968                             case MyCommon.HITRESULT.Exclude:
1969                                 if (tb.TabName == replyTab.TabName && post.IsReply) post.IsExcludeReply = true;
1970                                 if (post.IsFav) GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, true);
1971                                 post.FilterHit = false;
1972                                 break;
1973                             case MyCommon.HITRESULT.None:
1974                                 if (tb.TabName == replyTab.TabName && post.IsReply) replyTab.Add(post.StatusId, post.IsRead, true);
1975                                 if (post.IsFav) GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, true);
1976                                 post.FilterHit = false;
1977                                 break;
1978                             }
1979                         }
1980                         tb.AddSubmit();  //振分確定
1981                         foreach (var id in orgIds)
1982                         {
1983                             var hit = false;
1984                             foreach (var tbTemp in _tabs.Values.ToArray())
1985                             {
1986                                 if (tbTemp.Contains(id))
1987                                 {
1988                                     hit = true;
1989                                     break;
1990                                 }
1991                             }
1992                             if (!hit) tbr.Add(id, _statuses[id].IsRead, false);
1993                         }
1994                     }
1995                 }
1996                 this.SortPosts();
1997             }
1998         }
1999
2000         public long[] GetId(string TabName, ListView.SelectedIndexCollection IndexCollection)
2001         {
2002             if (IndexCollection.Count == 0) return null;
2003
2004             var tb = _tabs[TabName];
2005             var Ids = new long[IndexCollection.Count];
2006             for (int i = 0; i < Ids.Length; i++)
2007             {
2008                 Ids[i] = tb.GetId(IndexCollection[i]);
2009             }
2010             return Ids;
2011         }
2012
2013         public long GetId(string TabName, int Index)
2014         {
2015             return _tabs[TabName].GetId(Index);
2016         }
2017
2018         public int[] IndexOf(string TabName, long[] Ids)
2019         {
2020             if (Ids == null) return null;
2021             var idx = new int[Ids.Length];
2022             var tb = _tabs[TabName];
2023             for (int i = 0; i < Ids.Length; i++)
2024             {
2025                 idx[i] = tb.IndexOf(Ids[i]);
2026             }
2027             return idx;
2028         }
2029
2030         public int IndexOf(string TabName, long Id)
2031         {
2032             return _tabs[TabName].IndexOf(Id);
2033         }
2034
2035         public void ClearTabIds(string TabName)
2036         {
2037             //不要なPostを削除
2038             lock (LockObj)
2039             {
2040                 if (!_tabs[TabName].IsInnerStorageTabType)
2041                 {
2042                     foreach (var Id in _tabs[TabName].BackupIds)
2043                     {
2044                         var Hit = false;
2045                         foreach (var tb in _tabs.Values)
2046                         {
2047                             if (tb.Contains(Id))
2048                             {
2049                                 Hit = true;
2050                                 break;
2051                             }
2052                         }
2053                         if (!Hit) _statuses.Remove(Id);
2054                     }
2055                 }
2056
2057                 //指定タブをクリア
2058                 _tabs[TabName].ClearIDs();
2059             }
2060         }
2061
2062         public void SetTabUnreadManage(string TabName, bool Manage)
2063         {
2064             var tb = _tabs[TabName];
2065             lock (LockUnread)
2066             {
2067                 if (Manage)
2068                 {
2069                     var cnt = 0;
2070                     var oldest = long.MaxValue;
2071                     Dictionary<long, PostClass> posts;
2072                     if (!tb.IsInnerStorageTabType)
2073                     {
2074                         posts = _statuses;
2075                     }
2076                     else
2077                     {
2078                         posts = tb.Posts;
2079                     }
2080                     foreach (var id in tb.BackupIds)
2081                     {
2082                         if (!posts[id].IsRead)
2083                         {
2084                             cnt++;
2085                             if (oldest > id) oldest = id;
2086                         }
2087                     }
2088                     if (oldest == long.MaxValue) oldest = -1;
2089                     tb.OldestUnreadId = oldest;
2090                     tb.UnreadCount = cnt;
2091                 }
2092                 else
2093                 {
2094                     tb.OldestUnreadId = -1;
2095                     tb.UnreadCount = 0;
2096                 }
2097             }
2098             tb.UnreadManage = Manage;
2099         }
2100
2101         public void RefreshOwl(List<long> follower)
2102         {
2103             lock (LockObj)
2104             {
2105                 if (follower.Count > 0)
2106                 {
2107                     foreach (var post in _statuses.Values)
2108                     {
2109                         //if (post.UserId = 0 || post.IsDm) Continue For
2110                         if (post.IsMe)
2111                         {
2112                             post.IsOwl = false;
2113                         }
2114                         else
2115                         {
2116                             post.IsOwl = !follower.Contains(post.UserId);
2117                         }
2118                     }
2119                 }
2120                 else
2121                 {
2122                     foreach (var id in _statuses.Keys)
2123                     {
2124                         _statuses[id].IsOwl = false;
2125                     }
2126                 }
2127             }
2128         }
2129
2130         public TabClass GetTabByType(MyCommon.TabUsageType tabType)
2131         {
2132             //Home,Mentions,DM,Favは1つに制限する
2133             //その他のタイプを指定されたら、最初に合致したものを返す
2134             //合致しなければnullを返す
2135             lock (LockObj)
2136             {
2137                 foreach (var tb in _tabs.Values)
2138                 {
2139                     if (tb != null && tb.TabType == tabType) return tb;
2140                 }
2141                 return null;
2142             }
2143         }
2144
2145         public List<TabClass> GetTabsByType(MyCommon.TabUsageType tabType)
2146         {
2147             //合致したタブをListで返す
2148             //合致しなければ空のListを返す
2149             lock (LockObj)
2150             {
2151                 var tbs = new List<TabClass>();
2152                 foreach (var tb in _tabs.Values)
2153                 {
2154                     if ((tabType & tb.TabType) == tb.TabType) tbs.Add(tb);
2155                 }
2156                 return tbs;
2157             }
2158         }
2159
2160         public List<TabClass> GetTabsInnerStorageType()
2161         {
2162             //合致したタブをListで返す
2163             //合致しなければ空のListを返す
2164             lock (LockObj)
2165             {
2166                 var tbs = new List<TabClass>();
2167                 foreach (var tb in _tabs.Values)
2168                 {
2169                     if (tb.IsInnerStorageTabType) tbs.Add(tb);
2170                 }
2171                 return tbs;
2172             }
2173         }
2174
2175         public TabClass GetTabByName(string tabName)
2176         {
2177             lock (LockObj)
2178             {
2179                 if (_tabs.ContainsKey(tabName)) return _tabs[tabName];
2180                 return null;
2181             }
2182         }
2183
2184         // デフォルトタブの判定処理
2185         public bool IsDefaultTab(string tabName)
2186         {
2187             if (tabName != null &&
2188                _tabs.ContainsKey(tabName) &&
2189                (_tabs[tabName].TabType == MyCommon.TabUsageType.Home ||
2190                _tabs[tabName].TabType == MyCommon.TabUsageType.Mentions ||
2191                _tabs[tabName].TabType == MyCommon.TabUsageType.DirectMessage ||
2192                _tabs[tabName].TabType == MyCommon.TabUsageType.Favorites))
2193             {
2194                 return true;
2195             }
2196             else
2197             {
2198                 return false;
2199             }
2200         }
2201
2202         //振り分け可能タブの判定処理
2203         public bool IsDistributableTab(string tabName)
2204         {
2205             return tabName != null &&
2206                 this._tabs.ContainsKey(tabName) &&
2207                 (_tabs[tabName].TabType == MyCommon.TabUsageType.Mentions ||
2208                  _tabs[tabName].TabType == MyCommon.TabUsageType.UserDefined);
2209         }
2210
2211         public string GetUniqueTabName()
2212         {
2213             var tabNameTemp = "MyTab" + (_tabs.Count + 1).ToString();
2214             for (int i = 2; i <= 100; i++)
2215             {
2216                 if (_tabs.ContainsKey(tabNameTemp))
2217                 {
2218                     tabNameTemp = "MyTab" + (_tabs.Count + i).ToString();
2219                 }
2220                 else
2221                 {
2222                     break;
2223                 }
2224             }
2225             return tabNameTemp;
2226         }
2227
2228         public Dictionary<long, PostClass> Posts
2229         {
2230             get
2231             {
2232                 return _statuses;
2233             }
2234         }
2235     }
2236
2237     [Serializable]
2238     public sealed class TabClass
2239     {
2240         private bool _unreadManage = false;
2241         private List<FiltersClass> _filters;
2242         private int _unreadCount = 0;
2243         private List<long> _ids;
2244         private List<TemporaryId> _tmpIds = new List<TemporaryId>();
2245         private MyCommon.TabUsageType _tabType = MyCommon.TabUsageType.Undefined;
2246         private IdComparerClass _sorter = new IdComparerClass();
2247
2248         private readonly object _lockObj = new object();
2249
2250         public string User { get; set; }
2251
2252 #region "検索"
2253         //Search query
2254         private string _searchLang = "";
2255         private string _searchWords = "";
2256         private string _nextPageQuery = "";
2257
2258         public string SearchLang
2259         {
2260             get
2261             {
2262                 return _searchLang;
2263             }
2264             set
2265             {
2266                 SinceId = 0;
2267                 _searchLang = value;
2268             }
2269         }
2270         public string SearchWords
2271         {
2272             get
2273             {
2274                 return _searchWords;
2275             }
2276             set
2277             {
2278                 SinceId = 0;
2279                 _searchWords = value.Trim();
2280             }
2281         }
2282
2283         public string NextPageQuery
2284         {
2285             get
2286             {
2287                 return _nextPageQuery;
2288             }
2289             set
2290             {
2291                 _nextPageQuery = value;
2292             }
2293         }
2294
2295         public int GetSearchPage(int count)
2296         {
2297             return ((_ids.Count / count) + 1);
2298         }
2299         private Dictionary<string, string> _beforeQuery = new Dictionary<string, string>();
2300         public void SaveQuery(bool more)
2301         {
2302             var qry = new Dictionary<string, string>();
2303             if (string.IsNullOrEmpty(_searchWords))
2304             {
2305                 _beforeQuery = qry;
2306                 return;
2307             }
2308             qry.Add("q", _searchWords);
2309             if (!string.IsNullOrEmpty(_searchLang)) qry.Add("lang", _searchLang);
2310             _beforeQuery = qry;
2311         }
2312
2313         public bool IsQueryChanged()
2314         {
2315             var qry = new Dictionary<string, string>();
2316             if (!string.IsNullOrEmpty(_searchWords))
2317             {
2318                 qry.Add("q", _searchWords);
2319                 if (!string.IsNullOrEmpty(_searchLang)) qry.Add("lang", _searchLang);
2320             }
2321             if (qry.Count != _beforeQuery.Count) return true;
2322
2323             foreach (var kvp in qry)
2324             {
2325                 if (!_beforeQuery.ContainsKey(kvp.Key) || _beforeQuery[kvp.Key] != kvp.Value)
2326                 {
2327                     return true;
2328                 }
2329             }
2330             return false;
2331         }
2332 #endregion
2333
2334 #region "リスト"
2335         private ListElement _listInfo;
2336         public ListElement ListInfo
2337         {
2338             get
2339             {
2340                 return _listInfo;
2341             }
2342             set
2343             {
2344                 _listInfo = value;
2345             }
2346         }
2347 #endregion
2348
2349         [XmlIgnore]
2350         public PostClass RelationTargetPost { get; set; }
2351
2352         [XmlIgnore]
2353         public long OldestId = long.MaxValue;
2354
2355         [XmlIgnore]
2356         public long SinceId { get; set; }
2357
2358         [XmlIgnore]
2359         public Dictionary<long, PostClass> Posts { get; set; }
2360
2361         public PostClass[] GetTemporaryPosts()
2362         {
2363             var tempPosts = new List<PostClass>();
2364             if (_tmpIds.Count == 0) return tempPosts.ToArray();
2365             foreach (var tempId in _tmpIds)
2366             {
2367                 tempPosts.Add(Posts[tempId.Id]);
2368             }
2369             return tempPosts.ToArray();
2370         }
2371
2372         public int GetTemporaryCount()
2373         {
2374             return _tmpIds.Count;
2375         }
2376
2377         private struct TemporaryId
2378         {
2379             public long Id;
2380             public bool Read;
2381
2382             public TemporaryId(long argId, bool argRead)
2383             {
2384                 Id = argId;
2385                 Read = argRead;
2386             }
2387         }
2388
2389         public TabClass()
2390         {
2391             Posts = new Dictionary<long, PostClass>();
2392             SoundFile = "";
2393             OldestUnreadId = -1;
2394             TabName = "";
2395             _filters = new List<FiltersClass>();
2396             Notify = true;
2397             SoundFile = "";
2398             _unreadManage = true;
2399             _ids = new List<long>();
2400             this.OldestUnreadId = -1;
2401             _tabType = MyCommon.TabUsageType.Undefined;
2402             _listInfo = null;
2403         }
2404
2405         public TabClass(string TabName, MyCommon.TabUsageType TabType, ListElement list)
2406         {
2407             Posts = new Dictionary<long, PostClass>();
2408             SoundFile = "";
2409             OldestUnreadId = -1;
2410             this.TabName = TabName;
2411             _filters = new List<FiltersClass>();
2412             Notify = true;
2413             SoundFile = "";
2414             _unreadManage = true;
2415             _ids = new List<long>();
2416             this.OldestUnreadId = -1;
2417             _tabType = TabType;
2418             this.ListInfo = list;
2419             if (this.IsInnerStorageTabType)
2420             {
2421                 _sorter.posts = Posts;
2422             }
2423             else
2424             {
2425                 _sorter.posts = TabInformations.GetInstance().Posts;
2426             }
2427         }
2428
2429         public void Sort()
2430         {
2431             if (_sorter.Mode == IdComparerClass.ComparerMode.Id)
2432             {
2433                 _ids.Sort(_sorter.CmpMethod());
2434                 return;
2435             }
2436             long[] ar = null;
2437             if (_sorter.Order == SortOrder.Ascending)
2438             {
2439                 switch (_sorter.Mode)
2440                 {
2441                     case IdComparerClass.ComparerMode.Data:
2442                         ar = _ids.OrderBy(n => _sorter.posts[n].TextFromApi).ToArray();
2443                         break;
2444                     case IdComparerClass.ComparerMode.Name:
2445                         ar = _ids.OrderBy(n => _sorter.posts[n].ScreenName).ToArray();
2446                         break;
2447                     case IdComparerClass.ComparerMode.Nickname:
2448                         ar = _ids.OrderBy(n => _sorter.posts[n].Nickname).ToArray();
2449                         break;
2450                     case IdComparerClass.ComparerMode.Source:
2451                         ar = _ids.OrderBy(n => _sorter.posts[n].Source).ToArray();
2452                         break;
2453                 }
2454             }
2455             else
2456             {
2457                 switch (_sorter.Mode)
2458                 {
2459                     case IdComparerClass.ComparerMode.Data:
2460                         ar = _ids.OrderByDescending(n => _sorter.posts[n].TextFromApi).ToArray();
2461                         break;
2462                     case IdComparerClass.ComparerMode.Name:
2463                         ar = _ids.OrderByDescending(n => _sorter.posts[n].ScreenName).ToArray();
2464                         break;
2465                     case IdComparerClass.ComparerMode.Nickname:
2466                         ar = _ids.OrderByDescending(n => _sorter.posts[n].Nickname).ToArray();
2467                         break;
2468                     case IdComparerClass.ComparerMode.Source:
2469                         ar = _ids.OrderByDescending(n => _sorter.posts[n].Source).ToArray();
2470                         break;
2471                 }
2472             }
2473             _ids = new List<long>(ar);
2474         }
2475
2476         public IdComparerClass Sorter
2477         {
2478             get
2479             {
2480                 return _sorter;
2481             }
2482         }
2483
2484         //無条件に追加
2485         private void Add(long ID, bool Read)
2486         {
2487             if (this._ids.Contains(ID)) return;
2488
2489             if (this.Sorter.Mode == IdComparerClass.ComparerMode.Id)
2490             {
2491                 if (this.Sorter.Order == SortOrder.Ascending)
2492                 {
2493                     this._ids.Add(ID);
2494                 }
2495                 else
2496                 {
2497                     this._ids.Insert(0, ID);
2498                 }
2499             }
2500             else
2501             {
2502                 this._ids.Add(ID);
2503             }
2504
2505             if (!Read && this._unreadManage)
2506             {
2507                 this._unreadCount++;
2508                 if (ID < this.OldestUnreadId) this.OldestUnreadId = ID;
2509                 //if (this.OldestUnreadId == -1)
2510                 //{
2511                 //    this.OldestUnreadId = ID;
2512                 //}
2513                 //else
2514                 //{
2515                 //    if (ID < this.OldestUnreadId) this.OldestUnreadId = ID;
2516                 //}
2517             }
2518         }
2519
2520         public void Add(long ID, bool Read, bool Temporary)
2521         {
2522             if (!Temporary)
2523             {
2524                 this.Add(ID, Read);
2525             }
2526             else
2527             {
2528                 _tmpIds.Add(new TemporaryId(ID, Read));
2529             }
2530         }
2531
2532         //フィルタに合致したら追加
2533         public MyCommon.HITRESULT AddFiltered(PostClass post)
2534         {
2535             if (this.IsInnerStorageTabType) return MyCommon.HITRESULT.None;
2536
2537             var rslt = MyCommon.HITRESULT.None;
2538             //全フィルタ評価(優先順位あり)
2539             lock (this._lockObj)
2540             {
2541                 foreach (var ft in _filters)
2542                 {
2543                     try
2544                     {
2545                         switch (ft.IsHit(post))   //フィルタクラスでヒット判定
2546                         {
2547                             case MyCommon.HITRESULT.None:
2548                                 break;
2549                             case MyCommon.HITRESULT.Copy:
2550                                 if (rslt != MyCommon.HITRESULT.CopyAndMark) rslt = MyCommon.HITRESULT.Copy;
2551                                 break;
2552                             case MyCommon.HITRESULT.CopyAndMark:
2553                                 rslt = MyCommon.HITRESULT.CopyAndMark;
2554                                 break;
2555                             case MyCommon.HITRESULT.Move:
2556                                 rslt = MyCommon.HITRESULT.Move;
2557                                 break;
2558                             case MyCommon.HITRESULT.Exclude:
2559                                 rslt = MyCommon.HITRESULT.Exclude;
2560                                 goto exit_for;
2561                         }
2562                     }
2563                     catch (NullReferenceException)
2564                     {
2565                         //IsHitでNullRef出る場合あり。暫定対応
2566                         MyCommon.TraceOut("IsHitでNullRef: " + ft.ToString());
2567                         rslt = MyCommon.HITRESULT.None;
2568                     }
2569                 }
2570             exit_for:
2571                 ;
2572             }
2573
2574             if (rslt != MyCommon.HITRESULT.None && rslt != MyCommon.HITRESULT.Exclude)
2575             {
2576                 _tmpIds.Add(new TemporaryId(post.StatusId, post.IsRead));
2577             }
2578
2579             return rslt; //マーク付けは呼び出し元で行うこと
2580         }
2581
2582         //検索結果の追加
2583         public void AddPostToInnerStorage(PostClass Post)
2584         {
2585             if (Posts.ContainsKey(Post.StatusId)) return;
2586             Posts.Add(Post.StatusId, Post);
2587             _tmpIds.Add(new TemporaryId(Post.StatusId, Post.IsRead));
2588         }
2589
2590         public void AddSubmit(ref bool isMentionIncluded)
2591         {
2592             if (_tmpIds.Count == 0) return;
2593             _tmpIds.Sort((x, y) => x.Id.CompareTo(y.Id));
2594             foreach (var tId in _tmpIds)
2595             {
2596                 if (this.TabType == MyCommon.TabUsageType.Mentions && TabInformations.GetInstance()[tId.Id].IsReply) isMentionIncluded = true;
2597                 this.Add(tId.Id, tId.Read);
2598             }
2599             _tmpIds.Clear();
2600         }
2601
2602         public void AddSubmit()
2603         {
2604             bool mention = false;
2605             AddSubmit(ref mention);
2606         }
2607
2608         public void Remove(long Id)
2609         {
2610             if (!this._ids.Contains(Id)) return;
2611             this._ids.Remove(Id);
2612             if (this.IsInnerStorageTabType) Posts.Remove(Id);
2613         }
2614
2615         public void Remove(long Id, bool Read)
2616         {
2617             if (!this._ids.Contains(Id)) return;
2618
2619             if (!Read && this._unreadManage)
2620             {
2621                 this._unreadCount--;
2622                 this.OldestUnreadId = -1;
2623             }
2624
2625             this._ids.Remove(Id);
2626             if (this.IsInnerStorageTabType) Posts.Remove(Id);
2627         }
2628
2629         public bool UnreadManage
2630         {
2631             get
2632             {
2633                 return _unreadManage;
2634             }
2635             set
2636             {
2637                 this._unreadManage = value;
2638                 if (!value)
2639                 {
2640                     this.OldestUnreadId = -1;
2641                     this._unreadCount = 0;
2642                 }
2643             }
2644         }
2645
2646         public bool Notify { get; set; }
2647
2648         public string SoundFile { get; set; }
2649
2650         [XmlIgnore]
2651         public long OldestUnreadId { get; set; }
2652
2653         [XmlIgnore]
2654         public int UnreadCount
2655         {
2656             get
2657             {
2658                 return this.UnreadManage && AppendSettingDialog.Instance.UnreadManage ? _unreadCount : 0;
2659             }
2660             set
2661             {
2662                 if (value < 0) value = 0;
2663                 _unreadCount = value;
2664             }
2665         }
2666
2667         public int AllCount
2668         {
2669             get
2670             {
2671                 return this._ids.Count;
2672             }
2673         }
2674
2675         public FiltersClass[] GetFilters()
2676         {
2677             lock (this._lockObj)
2678             {
2679                 return _filters.ToArray();
2680             }
2681         }
2682
2683         public void RemoveFilter(FiltersClass filter)
2684         {
2685             lock (this._lockObj)
2686             {
2687                 _filters.Remove(filter);
2688                 this.FilterModified = true;
2689             }
2690         }
2691
2692         public bool AddFilter(FiltersClass filter)
2693         {
2694             lock (this._lockObj)
2695             {
2696                 if (_filters.Contains(filter)) return false;
2697                 _filters.Add(filter);
2698                 this.FilterModified = true;
2699                 return true;
2700             }
2701         }
2702
2703         public void EditFilter(FiltersClass original, FiltersClass modified)
2704         {
2705             original.BodyFilter = modified.BodyFilter;
2706             original.NameFilter = modified.NameFilter;
2707             original.SearchBoth = modified.SearchBoth;
2708             original.SearchUrl = modified.SearchUrl;
2709             original.UseRegex = modified.UseRegex;
2710             original.CaseSensitive = modified.CaseSensitive;
2711             original.IsRt = modified.IsRt;
2712             original.UseLambda = modified.UseLambda;
2713             original.Source = modified.Source;
2714             original.ExBodyFilter = modified.ExBodyFilter;
2715             original.ExNameFilter = modified.ExNameFilter;
2716             original.ExSearchBoth = modified.ExSearchBoth;
2717             original.ExSearchUrl = modified.ExSearchUrl;
2718             original.ExUseRegex = modified.ExUseRegex;
2719             original.ExCaseSensitive = modified.ExCaseSensitive;
2720             original.IsExRt = modified.IsExRt;
2721             original.ExUseLambda = modified.ExUseLambda;
2722             original.ExSource = modified.ExSource;
2723             original.MoveFrom = modified.MoveFrom;
2724             original.SetMark = modified.SetMark;
2725             this.FilterModified = true;
2726         }
2727
2728         [XmlIgnore]
2729         public List<FiltersClass> Filters
2730         {
2731             get
2732             {
2733                 lock (this._lockObj)
2734                 {
2735                     return _filters;
2736                 }
2737             }
2738             set
2739             {
2740                 lock (this._lockObj)
2741                 {
2742                     _filters = value;
2743                 }
2744             }
2745         }
2746
2747         public FiltersClass[] FilterArray
2748         {
2749             get
2750             {
2751                 lock (this._lockObj)
2752                 {
2753                     return _filters.ToArray();
2754                 }
2755             }
2756             set
2757             {
2758                 lock (this._lockObj)
2759                 {
2760                     foreach (var filters in value)
2761                     {
2762                         _filters.Add(filters);
2763                     }
2764                 }
2765             }
2766         }
2767         public bool Contains(long ID)
2768         {
2769             return _ids.Contains(ID);
2770         }
2771
2772         public void ClearIDs()
2773         {
2774             _ids.Clear();
2775             _tmpIds.Clear();
2776             _unreadCount = 0;
2777             this.OldestUnreadId = -1;
2778             if (Posts != null)
2779             {
2780                 Posts.Clear();
2781             }
2782         }
2783
2784         public long GetId(int Index)
2785         {
2786             return Index < _ids.Count ? _ids[Index] : -1;
2787         }
2788
2789         public int IndexOf(long ID)
2790         {
2791             return _ids.IndexOf(ID);
2792         }
2793
2794         [XmlIgnore]
2795         public bool FilterModified { get; set; }
2796
2797         public long[] BackupIds
2798         {
2799             get
2800             {
2801                 return _ids.ToArray();
2802             }
2803         }
2804
2805         public string TabName { get; set; }
2806
2807         public MyCommon.TabUsageType TabType
2808         {
2809             get
2810             {
2811                 return _tabType;
2812             }
2813             set
2814             {
2815                 _tabType = value;
2816                 if (this.IsInnerStorageTabType)
2817                 {
2818                     _sorter.posts = Posts;
2819                 }
2820                 else
2821                 {
2822                     _sorter.posts = TabInformations.GetInstance().Posts;
2823                 }
2824             }
2825         }
2826
2827         public bool IsInnerStorageTabType
2828         {
2829             get
2830             {
2831                 if (_tabType == MyCommon.TabUsageType.PublicSearch ||
2832                     _tabType == MyCommon.TabUsageType.DirectMessage ||
2833                     _tabType == MyCommon.TabUsageType.Lists ||
2834                     _tabType == MyCommon.TabUsageType.UserTimeline ||
2835                     _tabType == MyCommon.TabUsageType.Related)
2836                 {
2837                     return true;
2838                 }
2839                 else
2840                 {
2841                     return false;
2842                 }
2843             }
2844         }
2845     }
2846
2847     [Serializable]
2848     public sealed class FiltersClass : System.IEquatable<FiltersClass>
2849     {
2850         private string _name = "";
2851         private List<string> _body = new List<string>();
2852         private bool _searchBoth = true;
2853         private bool _searchUrl = false;
2854         private bool _caseSensitive = false;
2855         private bool _useRegex = false;
2856         private bool _isRt = false;
2857         private string _source = "";
2858         private string _exname = "";
2859         private List<string> _exbody = new List<string>();
2860         private bool _exsearchBoth = true;
2861         private bool _exsearchUrl = false;
2862         private bool _exuseRegex = false;
2863         private bool _excaseSensitive = false;
2864         private bool _isExRt = false;
2865         private string _exSource = "";
2866         private bool _moveFrom = false;
2867         private bool _setMark = true;
2868         private bool _useLambda = false;
2869         private bool _exuseLambda = false;
2870
2871         public FiltersClass() {}
2872
2873         //フィルタ一覧に表示する文言生成
2874         private string MakeSummary()
2875         {
2876             var fs = new StringBuilder();
2877             if (!string.IsNullOrEmpty(_name) || _body.Count > 0 || _isRt || !string.IsNullOrEmpty(_source))
2878             {
2879                 if (_searchBoth)
2880                 {
2881                     if (!string.IsNullOrEmpty(_name))
2882                     {
2883                         fs.AppendFormat(Properties.Resources.SetFiltersText1, _name);
2884                     }
2885                     else
2886                     {
2887                         fs.Append(Properties.Resources.SetFiltersText2);
2888                     }
2889                 }
2890                 if (_body.Count > 0)
2891                 {
2892                     fs.Append(Properties.Resources.SetFiltersText3);
2893                     foreach (var bf in _body)
2894                     {
2895                         fs.Append(bf);
2896                         fs.Append(" ");
2897                     }
2898                     fs.Length--;
2899                     fs.Append(Properties.Resources.SetFiltersText4);
2900                 }
2901                 fs.Append("(");
2902                 if (_searchBoth)
2903                 {
2904                     fs.Append(Properties.Resources.SetFiltersText5);
2905                 }
2906                 else
2907                 {
2908                     fs.Append(Properties.Resources.SetFiltersText6);
2909                 }
2910                 if (_useRegex)
2911                 {
2912                     fs.Append(Properties.Resources.SetFiltersText7);
2913                 }
2914                 if (_searchUrl)
2915                 {
2916                     fs.Append(Properties.Resources.SetFiltersText8);
2917                 }
2918                 if (_caseSensitive)
2919                 {
2920                     fs.Append(Properties.Resources.SetFiltersText13);
2921                 }
2922                 if (_isRt)
2923                 {
2924                     fs.Append("RT/");
2925                 }
2926                 if (_useLambda)
2927                 {
2928                     fs.Append("LambdaExp/");
2929                 }
2930                 if (!string.IsNullOrEmpty(_source))
2931                 {
2932                     fs.AppendFormat("Src…{0}/", _source);
2933                 }
2934                 fs.Length--;
2935                 fs.Append(")");
2936             }
2937             if (!string.IsNullOrEmpty(_exname) || _exbody.Count > 0 || _isExRt || !string.IsNullOrEmpty(_exSource))
2938             {
2939                 //除外
2940                 fs.Append(Properties.Resources.SetFiltersText12);
2941                 if (_exsearchBoth)
2942                 {
2943                     if (!string.IsNullOrEmpty(_exname))
2944                     {
2945                         fs.AppendFormat(Properties.Resources.SetFiltersText1, _exname);
2946                     }
2947                     else
2948                     {
2949                         fs.Append(Properties.Resources.SetFiltersText2);
2950                     }
2951                 }
2952                 if (_exbody.Count > 0)
2953                 {
2954                     fs.Append(Properties.Resources.SetFiltersText3);
2955                     foreach (var bf in _exbody)
2956                     {
2957                         fs.Append(bf);
2958                         fs.Append(" ");
2959                     }
2960                     fs.Length--;
2961                     fs.Append(Properties.Resources.SetFiltersText4);
2962                 }
2963                 fs.Append("(");
2964                 if (_exsearchBoth)
2965                 {
2966                     fs.Append(Properties.Resources.SetFiltersText5);
2967                 }
2968                 else
2969                 {
2970                     fs.Append(Properties.Resources.SetFiltersText6);
2971                 }
2972                 if (_exuseRegex)
2973                 {
2974                     fs.Append(Properties.Resources.SetFiltersText7);
2975                 }
2976                 if (_exsearchUrl)
2977                 {
2978                     fs.Append(Properties.Resources.SetFiltersText8);
2979                 }
2980                 if (_excaseSensitive)
2981                 {
2982                     fs.Append(Properties.Resources.SetFiltersText13);
2983                 }
2984                 if (_isExRt)
2985                 {
2986                     fs.Append("RT/");
2987                 }
2988                 if (_exuseLambda)
2989                 {
2990                     fs.Append("LambdaExp/");
2991                 }
2992                 if (!string.IsNullOrEmpty(_exSource))
2993                 {
2994                     fs.AppendFormat("Src…{0}/", _exSource);
2995                 }
2996                 fs.Length--;
2997                 fs.Append(")");
2998             }
2999
3000             fs.Append("(");
3001             if (_moveFrom)
3002             {
3003                 fs.Append(Properties.Resources.SetFiltersText9);
3004             }
3005             else
3006             {
3007                 fs.Append(Properties.Resources.SetFiltersText11);
3008             }
3009             if (!_moveFrom && _setMark)
3010             {
3011                 fs.Append(Properties.Resources.SetFiltersText10);
3012             }
3013             else if (!_moveFrom)
3014             {
3015                 fs.Length--;
3016             }
3017
3018             fs.Append(")");
3019
3020             return fs.ToString();
3021         }
3022
3023         public string NameFilter
3024         {
3025             get
3026             {
3027                 return _name;
3028             }
3029             set
3030             {
3031                 _name = value;
3032             }
3033         }
3034
3035         public string ExNameFilter
3036         {
3037             get
3038             {
3039                 return _exname;
3040             }
3041             set
3042             {
3043                 _exname = value;
3044             }
3045         }
3046
3047         [XmlIgnore]
3048         public List<string> BodyFilter
3049         {
3050             get
3051             {
3052                 return _body;
3053             }
3054             set
3055             {
3056                 _body = value;
3057             }
3058         }
3059
3060         public string[] BodyFilterArray
3061         {
3062             get
3063             {
3064                 return _body.ToArray();
3065             }
3066             set
3067             {
3068                 _body = new List<string>();
3069                 foreach (var filter in value)
3070                 {
3071                     _body.Add(filter);
3072                 }
3073             }
3074         }
3075
3076         [XmlIgnore]
3077         public List<string> ExBodyFilter
3078         {
3079             get
3080             {
3081                 return _exbody;
3082             }
3083             set
3084             {
3085                 _exbody = value;
3086             }
3087         }
3088
3089         public string[] ExBodyFilterArray
3090         {
3091             get
3092             {
3093                 return _exbody.ToArray();
3094             }
3095             set
3096             {
3097                 _exbody = new List<string>();
3098                 foreach (var filter in value)
3099                 {
3100                     _exbody.Add(filter);
3101                 }
3102             }
3103         }
3104
3105         public bool SearchBoth
3106         {
3107             get
3108             {
3109                 return _searchBoth;
3110             }
3111             set
3112             {
3113                 _searchBoth = value;
3114             }
3115         }
3116
3117         public bool ExSearchBoth
3118         {
3119             get
3120             {
3121                 return _exsearchBoth;
3122             }
3123             set
3124             {
3125                 _exsearchBoth = value;
3126             }
3127         }
3128
3129         public bool MoveFrom
3130         {
3131             get
3132             {
3133                 return _moveFrom;
3134             }
3135             set
3136             {
3137                 _moveFrom = value;
3138             }
3139         }
3140
3141         public bool SetMark
3142         {
3143             get
3144             {
3145                 return _setMark;
3146             }
3147             set
3148             {
3149                 _setMark = value;
3150             }
3151         }
3152
3153         public bool SearchUrl
3154         {
3155             get
3156             {
3157                 return _searchUrl;
3158             }
3159             set
3160             {
3161                 _searchUrl = value;
3162             }
3163         }
3164
3165         public bool ExSearchUrl
3166         {
3167             get
3168             {
3169                 return _exsearchUrl;
3170             }
3171             set
3172             {
3173                 _exsearchUrl = value;
3174             }
3175         }
3176
3177         public bool CaseSensitive
3178         {
3179             get
3180             {
3181                 return _caseSensitive;
3182             }
3183             set
3184             {
3185                 _caseSensitive = value;
3186             }
3187         }
3188
3189         public bool ExCaseSensitive
3190         {
3191             get
3192             {
3193                 return _excaseSensitive;
3194             }
3195             set
3196             {
3197                 _excaseSensitive = value;
3198             }
3199         }
3200
3201         public bool UseLambda
3202         {
3203             get
3204             {
3205                 return _useLambda;
3206             }
3207             set
3208             {
3209                 _useLambda = value;
3210             }
3211         }
3212
3213         public bool ExUseLambda
3214         {
3215             get
3216             {
3217                 return _exuseLambda;
3218             }
3219             set
3220             {
3221                 _exuseLambda = value;
3222             }
3223         }
3224
3225         public bool UseRegex
3226         {
3227             get
3228             {
3229                 return _useRegex;
3230             }
3231             set
3232             {
3233                 _useRegex = value;
3234             }
3235         }
3236
3237         public bool ExUseRegex
3238         {
3239             get
3240             {
3241                 return _exuseRegex;
3242             }
3243             set
3244             {
3245                 _exuseRegex = value;
3246             }
3247         }
3248
3249         public bool IsRt
3250         {
3251             get
3252             {
3253                 return _isRt;
3254             }
3255             set
3256             {
3257                 _isRt = value;
3258             }
3259         }
3260
3261         public bool IsExRt
3262         {
3263             get
3264             {
3265                 return _isExRt;
3266             }
3267             set
3268             {
3269                 _isExRt = value;
3270             }
3271         }
3272
3273         public string Source
3274         {
3275             get
3276             {
3277                 return _source;
3278             }
3279             set
3280             {
3281                 _source = value;
3282             }
3283         }
3284
3285         public string ExSource
3286         {
3287             get
3288             {
3289                 return _exSource;
3290             }
3291             set
3292             {
3293                 _exSource = value;
3294             }
3295         }
3296
3297         public override string ToString()
3298         {
3299             return MakeSummary();
3300         }
3301
3302         public bool ExecuteLambdaExpression(string expr, PostClass post)
3303         {
3304             return false;
3305             // TODO DynamicQuery相当のGPLv3互換なライブラリで置換する
3306         }
3307
3308         public bool ExecuteExLambdaExpression(string expr, PostClass post)
3309         {
3310             return false;
3311             // TODO DynamicQuery相当のGPLv3互換なライブラリで置換する
3312         }
3313
3314         public MyCommon.HITRESULT IsHit(PostClass post)
3315         {
3316             var bHit = true;
3317             string tBody;
3318             string tSource;
3319             if (_searchUrl)
3320             {
3321                 tBody = post.Text;
3322                 tSource = post.SourceHtml;
3323             }
3324             else
3325             {
3326                 tBody = post.TextFromApi;
3327                 tSource = post.Source;
3328             }
3329             //検索オプション
3330             System.StringComparison compOpt;
3331             System.Text.RegularExpressions.RegexOptions rgOpt;
3332             if (_caseSensitive)
3333             {
3334                 compOpt = StringComparison.Ordinal;
3335                 rgOpt = RegexOptions.None;
3336             }
3337             else
3338             {
3339                 compOpt = StringComparison.OrdinalIgnoreCase;
3340                 rgOpt = RegexOptions.IgnoreCase;
3341             }
3342             if (_searchBoth)
3343             {
3344                 if (string.IsNullOrEmpty(_name) ||
3345                     (!_useRegex &&
3346                      (post.ScreenName.Equals(_name, compOpt) ||
3347                       post.RetweetedBy.Equals(_name, compOpt)
3348                      )
3349                     ) ||
3350                     (_useRegex &&
3351                      (Regex.IsMatch(post.ScreenName, _name, rgOpt) ||
3352                       (!string.IsNullOrEmpty(post.RetweetedBy) && Regex.IsMatch(post.RetweetedBy, _name, rgOpt))
3353                      )
3354                     ))
3355                 {
3356                     if (_useLambda)
3357                     {
3358                         if (!ExecuteLambdaExpression(_body[0], post)) bHit = false;
3359                     }
3360                     else
3361                     {
3362                         foreach (var fs in _body)
3363                         {
3364                             if (_useRegex)
3365                             {
3366                                 if (!Regex.IsMatch(tBody, fs, rgOpt)) bHit = false;
3367                             }
3368                             else
3369                             {
3370                                 if (_caseSensitive)
3371                                 {
3372                                     if (!tBody.Contains(fs)) bHit = false;
3373                                 }
3374                                 else
3375                                 {
3376                                     if (!tBody.ToLower().Contains(fs.ToLower())) bHit = false;
3377                                 }
3378                             }
3379                             if (!bHit) break;
3380                         }
3381                     }
3382                 }
3383                 else
3384                 {
3385                     bHit = false;
3386                 }
3387             }
3388             else
3389             {
3390                 if (_useLambda)
3391                 {
3392                     if (!ExecuteLambdaExpression(_body[0], post)) bHit = false;
3393                 }
3394                 else
3395                 {
3396                     foreach (var fs in _body)
3397                     {
3398                         if (_useRegex)
3399                         {
3400                             if (!(Regex.IsMatch(post.ScreenName, fs, rgOpt) ||
3401                                  (!string.IsNullOrEmpty(post.RetweetedBy) && Regex.IsMatch(post.RetweetedBy, fs, rgOpt)) ||
3402                                  Regex.IsMatch(tBody, fs, rgOpt))) bHit = false;
3403                         }
3404                         else
3405                         {
3406                             if (_caseSensitive)
3407                             {
3408                                 if (!(post.ScreenName.Contains(fs) ||
3409                                     post.RetweetedBy.Contains(fs) ||
3410                                     tBody.Contains(fs))) bHit = false;
3411                             }
3412                             else
3413                             {
3414                                 if (!(post.ScreenName.ToLower().Contains(fs.ToLower()) ||
3415                                     post.RetweetedBy.ToLower().Contains(fs.ToLower()) ||
3416                                     tBody.ToLower().Contains(fs.ToLower()))) bHit = false;
3417                             }
3418                         }
3419                         if (!bHit) break;
3420                     }
3421                 }
3422             }
3423             if (_isRt)
3424             {
3425                 if (post.RetweetedId == 0) bHit = false;
3426             }
3427             if (!string.IsNullOrEmpty(_source))
3428             {
3429                 if (_useRegex)
3430                 {
3431                     if (!Regex.IsMatch(tSource, _source, rgOpt)) bHit = false;
3432                 }
3433                 else
3434                 {
3435                     if (!tSource.Equals(_source, compOpt)) bHit = false;
3436                 }
3437             }
3438             if (bHit)
3439             {
3440                 //除外判定
3441                 if (_exsearchUrl)
3442                 {
3443                     tBody = post.Text;
3444                     tSource = post.SourceHtml;
3445                 }
3446                 else
3447                 {
3448                     tBody = post.TextFromApi;
3449                     tSource = post.Source;
3450                 }
3451
3452                 var exFlag = false;
3453                 if (!string.IsNullOrEmpty(_exname) || _exbody.Count > 0)
3454                 {
3455                     if (_excaseSensitive)
3456                     {
3457                         compOpt = StringComparison.Ordinal;
3458                         rgOpt = RegexOptions.None;
3459                     }
3460                     else
3461                     {
3462                         compOpt = StringComparison.OrdinalIgnoreCase;
3463                         rgOpt = RegexOptions.IgnoreCase;
3464                     }
3465                     if (_exsearchBoth)
3466                     {
3467                         if (string.IsNullOrEmpty(_exname) ||
3468                             (!_exuseRegex &&
3469                              (post.ScreenName.Equals(_exname, compOpt) ||
3470                               post.RetweetedBy.Equals(_exname, compOpt)
3471                              )
3472                             ) ||
3473                             (_exuseRegex &&
3474                                 (Regex.IsMatch(post.ScreenName, _exname, rgOpt) ||
3475                                  (!string.IsNullOrEmpty(post.RetweetedBy) && Regex.IsMatch(post.RetweetedBy, _exname, rgOpt))
3476                                 )
3477                             ))
3478                         {
3479                             if (_exbody.Count > 0)
3480                             {
3481                                 if (_exuseLambda)
3482                                 {
3483                                     if (ExecuteExLambdaExpression(_exbody[0], post)) exFlag = true;
3484                                 }
3485                                 else
3486                                 {
3487                                     foreach (var fs in _exbody)
3488                                     {
3489                                         if (_exuseRegex)
3490                                         {
3491                                             if (Regex.IsMatch(tBody, fs, rgOpt)) exFlag = true;
3492                                         }
3493                                         else
3494                                         {
3495                                             if (_excaseSensitive)
3496                                             {
3497                                                 if (tBody.Contains(fs)) exFlag = true;
3498                                             }
3499                                             else
3500                                             {
3501                                                 if (tBody.ToLower().Contains(fs.ToLower())) exFlag = true;
3502                                             }
3503                                         }
3504                                         if (exFlag) break;
3505                                     }
3506                                 }
3507                             }
3508                             else
3509                             {
3510                                 exFlag = true;
3511                             }
3512                         }
3513                     }
3514                     else
3515                     {
3516                         if (_exuseLambda)
3517                         {
3518                             if (ExecuteExLambdaExpression(_exbody[0], post)) exFlag = true;
3519                         }
3520                         else
3521                         {
3522                             foreach (var fs in _exbody)
3523                             {
3524                                 if (_exuseRegex)
3525                                 {
3526                                     if (Regex.IsMatch(post.ScreenName, fs, rgOpt) ||
3527                                        (!string.IsNullOrEmpty(post.RetweetedBy) && Regex.IsMatch(post.RetweetedBy, fs, rgOpt)) ||
3528                                        Regex.IsMatch(tBody, fs, rgOpt)) exFlag = true;
3529                                 }
3530                                 else
3531                                 {
3532                                     if (_excaseSensitive)
3533                                     {
3534                                         if (post.ScreenName.Contains(fs) ||
3535                                            post.RetweetedBy.Contains(fs) ||
3536                                            tBody.Contains(fs)) exFlag = true;
3537                                     }
3538                                     else
3539                                     {
3540                                         if (post.ScreenName.ToLower().Contains(fs.ToLower()) ||
3541                                            post.RetweetedBy.ToLower().Contains(fs.ToLower()) ||
3542                                            tBody.ToLower().Contains(fs.ToLower())) exFlag = true;
3543                                     }
3544                                 }
3545                                 if (exFlag) break;
3546                             }
3547                         }
3548                     }
3549                 }
3550                 if (_isExRt)
3551                 {
3552                     if (post.RetweetedId > 0) exFlag = true;
3553                 }
3554                 if (!string.IsNullOrEmpty(_exSource))
3555                 {
3556                     if (_exuseRegex)
3557                     {
3558                         if (Regex.IsMatch(tSource, _exSource, rgOpt)) exFlag = true;
3559                     }
3560                     else
3561                     {
3562                         if (tSource.Equals(_exSource, compOpt)) exFlag = true;
3563                     }
3564                 }
3565
3566                 if (string.IsNullOrEmpty(_name) && _body.Count == 0 && !_isRt && string.IsNullOrEmpty(_source))
3567                 {
3568                     bHit = false;
3569                 }
3570                 if (bHit)
3571                 {
3572                     if (!exFlag)
3573                     {
3574                         if (_moveFrom)
3575                         {
3576                             return MyCommon.HITRESULT.Move;
3577                         }
3578                         else
3579                         {
3580                             if (_setMark)
3581                             {
3582                                 return MyCommon.HITRESULT.CopyAndMark;
3583                             }
3584                             return MyCommon.HITRESULT.Copy;
3585                         }
3586                     }
3587                     else
3588                     {
3589                         return MyCommon.HITRESULT.Exclude;
3590                     }
3591                 }
3592                 else
3593                 {
3594                     if (exFlag)
3595                     {
3596                         return MyCommon.HITRESULT.Exclude;
3597                     }
3598                     else
3599                     {
3600                         return MyCommon.HITRESULT.None;
3601                     }
3602                 }
3603             }
3604             else
3605             {
3606                 return MyCommon.HITRESULT.None;
3607             }
3608         }
3609
3610         public bool Equals(FiltersClass other)
3611         {
3612             if (this.BodyFilter.Count != other.BodyFilter.Count) return false;
3613             if (this.ExBodyFilter.Count != other.ExBodyFilter.Count) return false;
3614             for (int i = 0; i < this.BodyFilter.Count; i++)
3615             {
3616                 if (this.BodyFilter[i] != other.BodyFilter[i]) return false;
3617             }
3618             for (int i = 0; i < this.ExBodyFilter.Count; i++)
3619             {
3620                 if (this.ExBodyFilter[i] != other.ExBodyFilter[i]) return false;
3621             }
3622
3623             return (this.MoveFrom == other.MoveFrom) &
3624                    (this.SetMark == other.SetMark) &
3625                    (this.NameFilter == other.NameFilter) &
3626                    (this.SearchBoth == other.SearchBoth) &
3627                    (this.SearchUrl == other.SearchUrl) &
3628                    (this.UseRegex == other.UseRegex) &
3629                    (this.ExNameFilter == other.ExNameFilter) &
3630                    (this.ExSearchBoth == other.ExSearchBoth) &
3631                    (this.ExSearchUrl == other.ExSearchUrl) &
3632                    (this.ExUseRegex == other.ExUseRegex) &
3633                    (this.IsRt == other.IsRt) &
3634                    (this.Source == other.Source) &
3635                    (this.IsExRt == other.IsExRt) &
3636                    (this.ExSource == other.ExSource) &
3637                    (this.UseLambda == other.UseLambda) &
3638                    (this.ExUseLambda == other.ExUseLambda);
3639         }
3640
3641         public FiltersClass CopyTo(FiltersClass destination)
3642         {
3643             if (this.BodyFilter.Count > 0)
3644             {
3645                 foreach (var flt in this.BodyFilter)
3646                 {
3647                     destination.BodyFilter.Add(string.Copy(flt));
3648                 }
3649             }
3650
3651             if (this.ExBodyFilter.Count > 0)
3652             {
3653                 foreach (var flt in this.ExBodyFilter)
3654                 {
3655                     destination.ExBodyFilter.Add(string.Copy(flt));
3656                 }
3657             }
3658
3659             destination.MoveFrom = this.MoveFrom;
3660             destination.SetMark = this.SetMark;
3661             destination.NameFilter = this.NameFilter;
3662             destination.SearchBoth = this.SearchBoth;
3663             destination.SearchUrl = this.SearchUrl;
3664             destination.UseRegex = this.UseRegex;
3665             destination.ExNameFilter = this.ExNameFilter;
3666             destination.ExSearchBoth = this.ExSearchBoth;
3667             destination.ExSearchUrl = this.ExSearchUrl;
3668             destination.ExUseRegex = this.ExUseRegex;
3669             destination.IsRt = this.IsRt;
3670             destination.Source = this.Source;
3671             destination.IsExRt = this.IsExRt;
3672             destination.ExSource = this.ExSource;
3673             destination.UseLambda = this.UseLambda;
3674             destination.ExUseLambda = this.ExUseLambda;
3675             return destination;
3676         }
3677
3678         public override bool Equals(object obj)
3679         {
3680             if (obj == null || this.GetType() != obj.GetType()) return false;
3681             return this.Equals((FiltersClass)obj);
3682         }
3683
3684         public override int GetHashCode()
3685         {
3686             return this.MoveFrom.GetHashCode() ^
3687                    this.SetMark.GetHashCode() ^
3688                    this.BodyFilter.GetHashCode() ^
3689                    this.NameFilter.GetHashCode() ^
3690                    this.SearchBoth.GetHashCode() ^
3691                    this.SearchUrl.GetHashCode() ^
3692                    this.UseRegex.GetHashCode() ^
3693                    this.ExBodyFilter.GetHashCode() ^
3694                    this.ExNameFilter.GetHashCode() ^
3695                    this.ExSearchBoth.GetHashCode() ^
3696                    this.ExSearchUrl.GetHashCode() ^
3697                    this.ExUseRegex.GetHashCode() ^
3698                    this.IsRt.GetHashCode() ^
3699                    this.Source.GetHashCode() ^
3700                    this.IsExRt.GetHashCode() ^
3701                    this.ExSource.GetHashCode() ^
3702                    this.UseLambda.GetHashCode() ^
3703                    this.ExUseLambda.GetHashCode();
3704         }
3705     }
3706
3707     //ソート比較クラス:ID比較のみ
3708     public sealed class IdComparerClass : IComparer<long>
3709     {
3710         /// <summary>
3711         /// 比較する方法
3712         /// </summary>
3713         public enum ComparerMode
3714         {
3715             Id,
3716             Data,
3717             Name,
3718             Nickname,
3719             Source,
3720         }
3721
3722         private SortOrder _order;
3723         private ComparerMode _mode;
3724         private Dictionary<long, PostClass> _statuses;
3725         private Comparison<long> _CmpMethod;
3726
3727         /// <summary>
3728         /// 昇順か降順か Setの際は同時に比較関数の切り替えを行う
3729         /// </summary>
3730         public SortOrder Order
3731         {
3732             get
3733             {
3734                 return _order;
3735             }
3736             set
3737             {
3738                 _order = value;
3739                 SetCmpMethod(_mode, _order);
3740             }
3741         }
3742
3743         /// <summary>
3744         /// 並び替えの方法 Setの際は同時に比較関数の切り替えを行う
3745         /// </summary>
3746         public ComparerMode Mode
3747         {
3748             get
3749             {
3750                 return _mode;
3751             }
3752             set
3753             {
3754                 _mode = value;
3755                 SetCmpMethod(_mode, _order);
3756             }
3757         }
3758
3759         /// <summary>
3760         /// ListViewItemComparerクラスのコンストラクタ(引数付は未使用)
3761         /// </summary>
3762         /// <param name="col">並び替える列番号</param>
3763         /// <param name="ord">昇順か降順か</param>
3764         /// <param name="cmod">並び替えの方法</param>
3765
3766         public IdComparerClass()
3767         {
3768             _order = SortOrder.Ascending;
3769             _mode = ComparerMode.Id;
3770             SetCmpMethod(_mode, _order);
3771         }
3772
3773         public Dictionary<long, PostClass> posts
3774         {
3775             set
3776             {
3777                 _statuses = value;
3778             }
3779             get
3780             {
3781                 return _statuses;
3782             }
3783         }
3784
3785         // 指定したソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
3786         public Comparison<long> CmpMethod(ComparerMode _sortmode, SortOrder _sortorder)
3787         {
3788             //get
3789             {
3790                 Comparison<long> _method = null;
3791                 if (_sortorder == SortOrder.Ascending)
3792                 {
3793                     // 昇順
3794                     switch (_sortmode)
3795                     {
3796                     case ComparerMode.Data:
3797                         _method = Compare_ModeData_Ascending;
3798                         break;
3799                     case ComparerMode.Id:
3800                         _method = Compare_ModeId_Ascending;
3801                         break;
3802                     case ComparerMode.Name:
3803                         _method = Compare_ModeName_Ascending;
3804                         break;
3805                     case ComparerMode.Nickname:
3806                         _method = Compare_ModeNickName_Ascending;
3807                         break;
3808                     case ComparerMode.Source:
3809                         _method = Compare_ModeSource_Ascending;
3810                         break;
3811                     }
3812                 }
3813                 else
3814                 {
3815                     // 降順
3816                     switch (_sortmode)
3817                     {
3818                     case ComparerMode.Data:
3819                         _method = Compare_ModeData_Descending;
3820                         break;
3821                     case ComparerMode.Id:
3822                         _method = Compare_ModeId_Descending;
3823                         break;
3824                     case ComparerMode.Name:
3825                         _method = Compare_ModeName_Descending;
3826                         break;
3827                     case ComparerMode.Nickname:
3828                         _method = Compare_ModeNickName_Descending;
3829                         break;
3830                     case ComparerMode.Source:
3831                         _method = Compare_ModeSource_Descending;
3832                         break;
3833                     }
3834                 }
3835                 return _method;
3836             }
3837         }
3838
3839         // ソートモードとソートオーダーに従い使用する比較関数のアドレスを返す
3840         // (overload 現在の使用中の比較関数のアドレスを返す)
3841         public Comparison<long> CmpMethod()
3842         {
3843             //get
3844             {
3845                 return _CmpMethod;
3846             }
3847         }
3848
3849         // ソートモードとソートオーダーに従い比較関数のアドレスを切り替え
3850         private void SetCmpMethod(ComparerMode mode, SortOrder order)
3851         {
3852             _CmpMethod = this.CmpMethod(mode, order);
3853         }
3854
3855         //xがyより小さいときはマイナスの数、大きいときはプラスの数、
3856         //同じときは0を返す (こちらは未使用 一応比較関数群呼び出しの形のまま残しておく)
3857         int IComparer<long>.Compare(long x, long y)
3858         {
3859             return _CmpMethod(x, y);
3860         }
3861
3862         // 比較用関数群 いずれもステータスIDの順序を考慮する
3863         // 本文比較 昇順
3864         public int Compare_ModeData_Ascending(long x, long y)
3865         {
3866             var result = string.Compare(_statuses[x].TextFromApi, _statuses[y].TextFromApi);
3867             if (result == 0) result = x.CompareTo(y);
3868             return result;
3869         }
3870
3871         // 本文比較 降順
3872         public int Compare_ModeData_Descending(long x, long y)
3873         {
3874             var result = string.Compare(_statuses[y].TextFromApi, _statuses[x].TextFromApi);
3875             if (result == 0) result = y.CompareTo(x);
3876             return result;
3877         }
3878
3879         // ステータスID比較 昇順
3880         public int Compare_ModeId_Ascending(long x, long y)
3881         {
3882             return x.CompareTo(y);
3883         }
3884
3885         // ステータスID比較 降順
3886         public int Compare_ModeId_Descending(long x, long y)
3887         {
3888             return y.CompareTo(x);
3889         }
3890
3891         // 表示名比較 昇順
3892         public int Compare_ModeName_Ascending(long x, long y)
3893         {
3894             var result = string.Compare(_statuses[x].ScreenName, _statuses[y].ScreenName);
3895             if (result == 0) result = x.CompareTo(y);
3896             return result;
3897         }
3898
3899         // 表示名比較 降順
3900         public int Compare_ModeName_Descending(long x, long y)
3901         {
3902             var result = string.Compare(_statuses[y].ScreenName, _statuses[x].ScreenName);
3903             if (result == 0) result = y.CompareTo(x);
3904             return result;
3905         }
3906
3907         // ユーザー名比較 昇順
3908         public int Compare_ModeNickName_Ascending(long x, long y)
3909         {
3910             var result = string.Compare(_statuses[x].Nickname, _statuses[y].Nickname);
3911             if (result == 0) result = x.CompareTo(y);
3912             return result;
3913         }
3914
3915         // ユーザー名比較 降順
3916         public int Compare_ModeNickName_Descending(long x, long y)
3917         {
3918             var result = string.Compare(_statuses[y].Nickname, _statuses[x].Nickname);
3919             if (result == 0) result = y.CompareTo(x);
3920             return result;
3921         }
3922
3923         // Source比較 昇順
3924         public int Compare_ModeSource_Ascending(long x, long y)
3925         {
3926             var result = string.Compare(_statuses[x].Source, _statuses[y].Source);
3927             if (result == 0) result = x.CompareTo(y);
3928             return result;
3929         }
3930
3931         // Source比較 降順
3932         public int Compare_ModeSource_Descending(long x, long y)
3933         {
3934             var result = string.Compare(_statuses[y].Source, _statuses[x].Source);
3935             if (result == 0) result = y.CompareTo(x);
3936             return result;
3937         }
3938     }
3939 }