OSDN Git Service

eebdbc440664393aa09a7c49c82bbaf78166bd75
[opentween/open-tween.git] / OpenTween / Tween.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      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
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 //コンパイル後コマンド
28 //"c:\Program Files\Microsoft.NET\SDK\v2.0\Bin\sgen.exe" /f /a:"$(TargetPath)"
29 //"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\sgen.exe" /f /a:"$(TargetPath)"
30
31 using System;
32 using System.Collections.Generic;
33 using System.ComponentModel;
34 using System.Diagnostics;
35 using System.Drawing;
36 using System.IO;
37 using System.Linq;
38 using System.Media;
39 using System.Net;
40 using System.Net.Http;
41 using System.Reflection;
42 using System.Text;
43 using System.Text.RegularExpressions;
44 using System.Threading;
45 using System.Threading.Tasks;
46 using System.Windows.Forms;
47 using OpenTween.Api;
48 using OpenTween.Connection;
49 using OpenTween.OpenTweenCustomControl;
50 using OpenTween.Thumbnail;
51
52 namespace OpenTween
53 {
54     public partial class TweenMain : OTBaseForm
55     {
56         //各種設定
57         private Size _mySize;           //画面サイズ
58         private Point _myLoc;           //画面位置
59         private int _mySpDis;           //区切り位置
60         private int _mySpDis2;          //発言欄区切り位置
61         private int _mySpDis3;          //プレビュー区切り位置
62         private int _iconSz;            //アイコンサイズ(現在は16、24、48の3種類。将来直接数字指定可能とする 注:24x24の場合に26と指定しているのはMSゴシック系フォントのための仕様)
63         private bool _iconCol;          //1列表示の時true(48サイズのとき)
64
65         //雑多なフラグ類
66         private bool _initial;         //true:起動時処理中
67         private bool _initialLayout = true;
68         private bool _ignoreConfigSave;         //true:起動時処理中
69         private bool _tabDrag;           //タブドラッグ中フラグ(DoDragDropを実行するかの判定用)
70         private TabPage _beforeSelectedTab; //タブが削除されたときに前回選択されていたときのタブを選択する為に保持
71         private Point _tabMouseDownPoint;
72         private string _rclickTabName;      //右クリックしたタブの名前(Tabコントロール機能不足対応)
73         private readonly object _syncObject = new object();    //ロック用
74         private const string detailHtmlFormatMono1 = "<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\"><style type=\"text/css\"><!-- pre {font-family: \"";
75         private const string detailHtmlFormat2 = "\", sans-serif; font-size: ";
76         private const string detailHtmlFormat3 = "pt; margin: 0; word-wrap: break-word; white-space: pre-wrap; color:rgb(";
77         private const string detailHtmlFormat4 = ");} a:link, a:visited, a:active, a:hover {color:rgb(";
78         private const string detailHtmlFormat5 = "); } --></style></head><body style=\"margin:0px; background-color:rgb(";
79         private const string detailHtmlFormatMono6 = ");\"><pre>";
80         private const string detailHtmlFormatMono7 = "</pre></body></html>";
81         private const string detailHtmlFormat1 = "<html><head><meta http-equiv=\"X-UA-Compatible\" content=\"IE=8\"><style type=\"text/css\"><!-- p {font-family: \"";
82         private const string detailHtmlFormat6 = ");\"><p><span style=\"vertical-align:text-bottom\">";
83         private const string detailHtmlFormat7 = "</span></p></body></html>";
84         private string detailHtmlFormatHeader;
85         private string detailHtmlFormatFooter;
86         private bool _myStatusError = false;
87         private bool _myStatusOnline = false;
88         private bool soundfileListup = false;
89         private FormWindowState _formWindowState = FormWindowState.Normal; // フォームの状態保存用 通知領域からアイコンをクリックして復帰した際に使用する
90
91         //設定ファイル関連
92         //private SettingToConfig _cfg; //旧
93         private SettingLocal _cfgLocal;
94         private SettingCommon _cfgCommon;
95         private bool _modifySettingLocal = false;
96         private bool _modifySettingCommon = false;
97         private bool _modifySettingAtId = false;
98
99         //twitter解析部
100         private Twitter tw = new Twitter();
101
102         //Growl呼び出し部
103         private GrowlHelper gh = new GrowlHelper(Application.ProductName);
104
105         //サブ画面インスタンス
106         private AppendSettingDialog SettingDialog = AppendSettingDialog.Instance;       //設定画面インスタンス
107         private SearchWordDialog SearchDialog = new SearchWordDialog();     //検索画面インスタンス
108         private FilterDialog fltDialog = new FilterDialog(); //フィルター編集画面
109         private OpenURL UrlDialog = new OpenURL();
110         public AtIdSupplement AtIdSupl;     //@id補助
111         public AtIdSupplement HashSupl;    //Hashtag補助
112         public HashtagManage HashMgr;
113         private EventViewerDialog evtDialog;
114
115         //表示フォント、色、アイコン
116         private Font _fntUnread;            //未読用フォント
117         private Color _clUnread;            //未読用文字色
118         private Font _fntReaded;            //既読用フォント
119         private Color _clReaded;            //既読用文字色
120         private Color _clFav;               //Fav用文字色
121         private Color _clOWL;               //片思い用文字色
122         private Color _clRetweet;               //Retweet用文字色
123         private Color _clHighLight = Color.FromKnownColor(KnownColor.HighlightText);         //選択中の行用文字色
124         private Font _fntDetail;            //発言詳細部用フォント
125         private Color _clDetail;              //発言詳細部用色
126         private Color _clDetailLink;          //発言詳細部用リンク文字色
127         private Color _clDetailBackcolor;     //発言詳細部用背景色
128         private Color _clSelf;              //自分の発言用背景色
129         private Color _clAtSelf;            //自分宛返信用背景色
130         private Color _clTarget;            //選択発言者の他の発言用背景色
131         private Color _clAtTarget;          //選択発言中の返信先用背景色
132         private Color _clAtFromTarget;      //選択発言者への返信発言用背景色
133         private Color _clAtTo;              //選択発言の唯一@先
134         private Color _clListBackcolor;       //リスト部通常発言背景色
135         private Color _clInputBackcolor;      //入力欄背景色
136         private Color _clInputFont;           //入力欄文字色
137         private Font _fntInputFont;           //入力欄フォント
138         private ImageCache IconCache;        //アイコン画像リスト
139         private Icon NIconAt;               //At.ico             タスクトレイアイコン:通常時
140         private Icon NIconAtRed;            //AtRed.ico          タスクトレイアイコン:通信エラー時
141         private Icon NIconAtSmoke;          //AtSmoke.ico        タスクトレイアイコン:オフライン時
142         private Icon[] NIconRefresh = new Icon[4];       //Refresh.ico        タスクトレイアイコン:更新中(アニメーション用に4種類を保持するリスト)
143         private Icon TabIcon;               //Tab.ico            未読のあるタブ用アイコン
144         private Icon MainIcon;              //Main.ico           画面左上のアイコン
145         private Icon ReplyIcon;               //5g
146         private Icon ReplyIconBlink;          //6g
147
148         private ImageList _listViewImageList = new ImageList();    //ListViewItemの高さ変更用
149
150         private PostClass _anchorPost;
151         private bool _anchorFlag;        //true:関連発言移動中(関連移動以外のオペレーションをするとfalseへ。trueだとリスト背景色をアンカー発言選択中として描画)
152
153         private List<PostingStatus> _history = new List<PostingStatus>();   //発言履歴
154         private int _hisIdx;                  //発言履歴カレントインデックス
155
156         //発言投稿時のAPI引数(発言編集時に設定。手書きreplyでは設定されない)
157         private long? _reply_to_id;     // リプライ先のステータスID 0の場合はリプライではない 注:複数あてのものはリプライではない
158         private string _reply_to_name;    // リプライ先ステータスの書き込み者の名前
159
160         //時速表示用
161         private List<DateTime> _postTimestamps = new List<DateTime>();
162         private List<DateTime> _favTimestamps = new List<DateTime>();
163         private Dictionary<DateTime, int> _tlTimestamps = new Dictionary<DateTime, int>();
164         private int _tlCount;
165
166         // 以下DrawItem関連
167         private SolidBrush _brsHighLight = new SolidBrush(Color.FromKnownColor(KnownColor.Highlight));
168         private SolidBrush _brsBackColorMine;
169         private SolidBrush _brsBackColorAt;
170         private SolidBrush _brsBackColorYou;
171         private SolidBrush _brsBackColorAtYou;
172         private SolidBrush _brsBackColorAtFromTarget;
173         private SolidBrush _brsBackColorAtTo;
174         private SolidBrush _brsBackColorNone;
175         private SolidBrush _brsDeactiveSelection = new SolidBrush(Color.FromKnownColor(KnownColor.ButtonFace)); //Listにフォーカスないときの選択行の背景色
176         private StringFormat sfTab = new StringFormat();
177
178         //////////////////////////////////////////////////////////////////////////////////////////////////////////
179         private ToolStripAPIGauge _apiGauge;
180         private TabInformations _statuses;
181
182         // ListViewItem のキャッシュ関連
183         private int _itemCacheIndex;
184         private ListViewItem[] _itemCache;
185         private PostClass[] _postCache;
186         private ReaderWriterLockSlim itemCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
187
188         private TabPage _curTab;
189         private int _curItemIndex;
190         private DetailsListView _curList;
191         private PostClass _curPost;
192         private bool _isColumnChanged = false;
193         private bool _waitTimeline = false;
194         private bool _waitReply = false;
195         private bool _waitDm = false;
196         private bool _waitFav = false;
197         private bool _waitPubSearch = false;
198         private bool _waitUserTimeline = false;
199         private bool _waitLists = false;
200         private BackgroundWorker[] _bw = new BackgroundWorker[20];
201         private BackgroundWorker _bwFollower;
202
203         private int UnreadCounter = -1;
204         private int UnreadAtCounter = -1;
205
206         private string[] ColumnOrgText = new string[9];
207         private string[] ColumnText = new string[9];
208
209         private bool _DoFavRetweetFlags = false;
210         private bool osResumed = false;
211
212         //////////////////////////////////////////////////////////////////////////////////////////////////////////
213         private string _postBrowserStatusText = "";
214
215         private bool _colorize = false;
216
217         private System.Timers.Timer TimerTimeline = new System.Timers.Timer();
218
219         private ImageListViewItem displayItem;
220
221         /// <summary>
222         /// デザイン時の DPI (96dpi) と実際の表示時の DPI との比を表します
223         /// </summary>
224         protected SizeF currentScaleFactor = new SizeF();
225
226         //URL短縮のUndo用
227         private struct urlUndo
228         {
229             public string Before;
230             public string After;
231         }
232
233         private List<urlUndo> urlUndoBuffer = null;
234
235         private struct ReplyChain
236         {
237             public long OriginalId;
238             public long InReplyToId;
239             public TabPage OriginalTab;
240
241             public ReplyChain(long originalId, long inReplyToId, TabPage originalTab)
242             {
243                 this.OriginalId = originalId;
244                 this.InReplyToId = inReplyToId;
245                 this.OriginalTab = originalTab;
246             }
247         }
248
249         private Stack<ReplyChain> replyChains; //[, ]でのリプライ移動の履歴
250         private Stack<Tuple<TabPage, PostClass>> selectPostChains = new Stack<Tuple<TabPage, PostClass>>(); //ポスト選択履歴
251
252         //Backgroundworkerの処理結果通知用引数構造体
253         private class GetWorkerResult
254         {
255             public string retMsg = "";                     //処理結果詳細メッセージ。エラー時に値がセットされる
256             public MyCommon.WORKERTYPE type;                   //処理種別
257             public string tName = "";                  //Fav追加・削除時のタブ名
258             public List<long> sIds = null;                  //Fav追加・削除成功分のID
259             public bool newDM = false;
260             public int addCount;
261             public PostingStatus status;
262         }
263
264         //Backgroundworkerへ処理内容を通知するための引数用構造体
265         private class GetWorkerArg
266         {
267             public int page;                      //処理対象ページ番号
268             public MyCommon.WORKERTYPE type;                   //処理種別
269             public PostingStatus status = new PostingStatus();          //発言POST時の発言内容
270             public List<long> ids;               //Fav追加・削除時のItemIndex
271             public List<long> sIds;              //Fav追加・削除成功分のItemIndex
272             public string tName = "";            //Fav追加・削除時のタブ名
273         }
274
275         //検索処理タイプ
276         private enum SEARCHTYPE
277         {
278             DialogSearch,
279             NextSearch,
280             PrevSearch,
281         }
282
283         private class PostingStatus
284         {
285             public string status = "";
286             public long? inReplyToId = null;
287             public string inReplyToName = null;
288             public string imageService = "";      //画像投稿サービス名
289             public string[] imagePath = null;
290             public PostingStatus()
291             {
292             }
293             public PostingStatus(string status, long? replyToId, string replyToName)
294             {
295                 this.status = status;
296                 this.inReplyToId = replyToId;
297                 this.inReplyToName = replyToName;
298             }
299         }
300
301         private void TweenMain_Activated(object sender, EventArgs e)
302         {
303             //画面がアクティブになったら、発言欄の背景色戻す
304             if (StatusText.Focused)
305             {
306                 this.StatusText_Enter(this.StatusText, System.EventArgs.Empty);
307             }
308         }
309
310         private bool disposed = false;
311
312         /// <summary>
313         /// 使用中のリソースをすべてクリーンアップします。
314         /// </summary>
315         /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
316         protected override void Dispose(bool disposing)
317         {
318             base.Dispose(disposing);
319
320             if (this.disposed)
321                 return;
322
323             if (disposing)
324             {
325                 if (this.components != null)
326                     this.components.Dispose();
327
328                 //後始末
329                 SettingDialog.Dispose();
330                 SearchDialog.Dispose();
331                 fltDialog.Dispose();
332                 UrlDialog.Dispose();
333                 if (NIconAt != null) NIconAt.Dispose();
334                 if (NIconAtRed != null) NIconAtRed.Dispose();
335                 if (NIconAtSmoke != null) NIconAtSmoke.Dispose();
336                 if (NIconRefresh[0] != null) NIconRefresh[0].Dispose();
337                 if (NIconRefresh[1] != null) NIconRefresh[1].Dispose();
338                 if (NIconRefresh[2] != null) NIconRefresh[2].Dispose();
339                 if (NIconRefresh[3] != null) NIconRefresh[3].Dispose();
340                 if (TabIcon != null) TabIcon.Dispose();
341                 if (MainIcon != null) MainIcon.Dispose();
342                 if (ReplyIcon != null) ReplyIcon.Dispose();
343                 if (ReplyIconBlink != null) ReplyIconBlink.Dispose();
344                 _listViewImageList.Dispose();
345                 _brsHighLight.Dispose();
346                 if (_brsBackColorMine != null) _brsBackColorMine.Dispose();
347                 if (_brsBackColorAt != null) _brsBackColorAt.Dispose();
348                 if (_brsBackColorYou != null) _brsBackColorYou.Dispose();
349                 if (_brsBackColorAtYou != null) _brsBackColorAtYou.Dispose();
350                 if (_brsBackColorAtFromTarget != null) _brsBackColorAtFromTarget.Dispose();
351                 if (_brsBackColorAtTo != null) _brsBackColorAtTo.Dispose();
352                 if (_brsBackColorNone != null) _brsBackColorNone.Dispose();
353                 if (_brsDeactiveSelection != null) _brsDeactiveSelection.Dispose();
354                 //sf.Dispose();
355                 sfTab.Dispose();
356                 foreach (BackgroundWorker bw in _bw)
357                 {
358                     if (bw != null)
359                         bw.Dispose();
360                 }
361                 if (_bwFollower != null)
362                 {
363                     _bwFollower.Dispose();
364                 }
365                 this._apiGauge.Dispose();
366                 if (IconCache != null)
367                 {
368                     this.IconCache.CancelAsync();
369                     this.IconCache.Dispose();
370                 }
371
372                 if (this.thumbnailTokenSource != null)
373                     this.thumbnailTokenSource.Dispose();
374
375                 this.itemCacheLock.Dispose();
376                 this.tw.Dispose();
377                 this._hookGlobalHotkey.Dispose();
378             }
379
380             // 終了時にRemoveHandlerしておかないとメモリリークする
381             // http://msdn.microsoft.com/ja-jp/library/microsoft.win32.systemevents.powermodechanged.aspx
382             Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
383
384             this.disposed = true;
385         }
386
387         private void LoadIcon(ref Icon IconInstance, string FileName)
388         {
389             string dir = Application.StartupPath;
390             if (File.Exists(Path.Combine(dir, FileName)))
391             {
392                 try
393                 {
394                     IconInstance = new Icon(Path.Combine(dir, FileName));
395                 }
396                 catch (Exception)
397                 {
398                 }
399             }
400         }
401
402         private void LoadIcons()
403         {
404             //着せ替えアイコン対応
405             //タスクトレイ通常時アイコン
406             string dir = Application.StartupPath;
407
408             NIconAt = Properties.Resources.At;
409             NIconAtRed = Properties.Resources.AtRed;
410             NIconAtSmoke = Properties.Resources.AtSmoke;
411             NIconRefresh[0] = Properties.Resources.Refresh;
412             NIconRefresh[1] = Properties.Resources.Refresh2;
413             NIconRefresh[2] = Properties.Resources.Refresh3;
414             NIconRefresh[3] = Properties.Resources.Refresh4;
415             TabIcon = Properties.Resources.TabIcon;
416             MainIcon = Properties.Resources.MIcon;
417             ReplyIcon = Properties.Resources.Reply;
418             ReplyIconBlink = Properties.Resources.ReplyBlink;
419
420             if (!Directory.Exists(Path.Combine(dir, "Icons")))
421                 return;
422
423             LoadIcon(ref NIconAt, "Icons\\At.ico");
424
425             //タスクトレイエラー時アイコン
426             LoadIcon(ref NIconAtRed, "Icons\\AtRed.ico");
427
428             //タスクトレイオフライン時アイコン
429             LoadIcon(ref NIconAtSmoke, "Icons\\AtSmoke.ico");
430
431             //タスクトレイ更新中アイコン
432             //アニメーション対応により4種類読み込み
433             LoadIcon(ref NIconRefresh[0], "Icons\\Refresh.ico");
434             LoadIcon(ref NIconRefresh[1], "Icons\\Refresh2.ico");
435             LoadIcon(ref NIconRefresh[2], "Icons\\Refresh3.ico");
436             LoadIcon(ref NIconRefresh[3], "Icons\\Refresh4.ico");
437
438             //タブ見出し未読表示アイコン
439             LoadIcon(ref TabIcon, "Icons\\Tab.ico");
440
441             //画面のアイコン
442             LoadIcon(ref MainIcon, "Icons\\MIcon.ico");
443
444             //Replyのアイコン
445             LoadIcon(ref ReplyIcon, "Icons\\Reply.ico");
446
447             //Reply点滅のアイコン
448             LoadIcon(ref ReplyIconBlink, "Icons\\ReplyBlink.ico");
449         }
450
451         private void InitColumns(ListView list, bool startup)
452         {
453             ColumnHeader _colHd1 = new ColumnHeader();  //アイコン
454             ColumnHeader _colHd2 = new ColumnHeader();  //ニックネーム
455             ColumnHeader _colHd3 = new ColumnHeader();  //本文
456             ColumnHeader _colHd4 = new ColumnHeader();  //日付
457             ColumnHeader _colHd5 = new ColumnHeader();  //ユーザID
458             ColumnHeader _colHd6 = new ColumnHeader();  //未読
459             ColumnHeader _colHd7 = new ColumnHeader();  //マーク&プロテクト
460             ColumnHeader _colHd8 = new ColumnHeader();  //ソース
461
462             if (!_iconCol)
463             {
464                 list.Columns.AddRange(new ColumnHeader[] { _colHd1, _colHd2, _colHd3, _colHd4, _colHd5, _colHd6, _colHd7, _colHd8 });
465             }
466             else
467             {
468                 list.Columns.AddRange(new ColumnHeader[] { _colHd1, _colHd3 });
469             }
470
471             InitColumnText();
472             _colHd1.Text = ColumnText[0];
473             _colHd1.Width = 48;
474             _colHd2.Text = ColumnText[1];
475             _colHd2.Width = 80;
476             _colHd3.Text = ColumnText[2];
477             _colHd3.Width = 300;
478             _colHd4.Text = ColumnText[3];
479             _colHd4.Width = 50;
480             _colHd5.Text = ColumnText[4];
481             _colHd5.Width = 50;
482             _colHd6.Text = ColumnText[5];
483             _colHd6.Width = 16;
484             _colHd7.Text = ColumnText[6];
485             _colHd7.Width = 16;
486             _colHd8.Text = ColumnText[7];
487             _colHd8.Width = 50;
488
489             int[] dispOrder = new int[8];
490             if (!startup)
491             {
492                 for (int i = 0; i < _curList.Columns.Count; i++)
493                 {
494                     for (int j = 0; j < _curList.Columns.Count; j++)
495                     {
496                         if (_curList.Columns[j].DisplayIndex == i)
497                         {
498                             dispOrder[i] = j;
499                             break;
500                         }
501                     }
502                 }
503                 for (int i = 0; i < _curList.Columns.Count; i++)
504                 {
505                     list.Columns[i].Width = _curList.Columns[i].Width;
506                     list.Columns[dispOrder[i]].DisplayIndex = i;
507                 }
508             }
509             else
510             {
511                 if (_iconCol)
512                 {
513                     list.Columns[0].Width = _cfgLocal.Width1;
514                     list.Columns[1].Width = _cfgLocal.Width3;
515                     list.Columns[0].DisplayIndex = 0;
516                     list.Columns[1].DisplayIndex = 1;
517                 }
518                 else
519                 {
520                     for (int i = 0; i <= 7; i++)
521                     {
522                         if (_cfgLocal.DisplayIndex1 == i)
523                             dispOrder[i] = 0;
524                         else if (_cfgLocal.DisplayIndex2 == i)
525                             dispOrder[i] = 1;
526                         else if (_cfgLocal.DisplayIndex3 == i)
527                             dispOrder[i] = 2;
528                         else if (_cfgLocal.DisplayIndex4 == i)
529                             dispOrder[i] = 3;
530                         else if (_cfgLocal.DisplayIndex5 == i)
531                             dispOrder[i] = 4;
532                         else if (_cfgLocal.DisplayIndex6 == i)
533                             dispOrder[i] = 5;
534                         else if (_cfgLocal.DisplayIndex7 == i)
535                             dispOrder[i] = 6;
536                         else if (_cfgLocal.DisplayIndex8 == i)
537                             dispOrder[i] = 7;
538                     }
539                     list.Columns[0].Width = _cfgLocal.Width1;
540                     list.Columns[1].Width = _cfgLocal.Width2;
541                     list.Columns[2].Width = _cfgLocal.Width3;
542                     list.Columns[3].Width = _cfgLocal.Width4;
543                     list.Columns[4].Width = _cfgLocal.Width5;
544                     list.Columns[5].Width = _cfgLocal.Width6;
545                     list.Columns[6].Width = _cfgLocal.Width7;
546                     list.Columns[7].Width = _cfgLocal.Width8;
547                     for (int i = 0; i <= 7; i++)
548                     {
549                         list.Columns[dispOrder[i]].DisplayIndex = i;
550                     }
551                 }
552             }
553         }
554
555         private void InitColumnText()
556         {
557             ColumnText[0] = "";
558             ColumnText[1] = Properties.Resources.AddNewTabText2;
559             ColumnText[2] = Properties.Resources.AddNewTabText3;
560             ColumnText[3] = Properties.Resources.AddNewTabText4_2;
561             ColumnText[4] = Properties.Resources.AddNewTabText5;
562             ColumnText[5] = "";
563             ColumnText[6] = "";
564             ColumnText[7] = "Source";
565
566             ColumnOrgText[0] = "";
567             ColumnOrgText[1] = Properties.Resources.AddNewTabText2;
568             ColumnOrgText[2] = Properties.Resources.AddNewTabText3;
569             ColumnOrgText[3] = Properties.Resources.AddNewTabText4_2;
570             ColumnOrgText[4] = Properties.Resources.AddNewTabText5;
571             ColumnOrgText[5] = "";
572             ColumnOrgText[6] = "";
573             ColumnOrgText[7] = "Source";
574
575             int c = 0;
576             switch (_statuses.SortMode)
577             {
578                 case IdComparerClass.ComparerMode.Nickname:  //ニックネーム
579                     c = 1;
580                     break;
581                 case IdComparerClass.ComparerMode.Data:  //本文
582                     c = 2;
583                     break;
584                 case IdComparerClass.ComparerMode.Id:  //時刻=発言Id
585                     c = 3;
586                     break;
587                 case IdComparerClass.ComparerMode.Name:  //名前
588                     c = 4;
589                     break;
590                 case IdComparerClass.ComparerMode.Source:  //Source
591                     c = 7;
592                     break;
593             }
594
595             if (_iconCol)
596             {
597                 if (_statuses.SortOrder == SortOrder.Descending)
598                 {
599                     // U+25BE BLACK DOWN-POINTING SMALL TRIANGLE
600                     ColumnText[2] = ColumnOrgText[2] + "▾";
601                 }
602                 else
603                 {
604                     // U+25B4 BLACK UP-POINTING SMALL TRIANGLE
605                     ColumnText[2] = ColumnOrgText[2] + "▴";
606                 }
607             }
608             else
609             {
610                 if (_statuses.SortOrder == SortOrder.Descending)
611                 {
612                     // U+25BE BLACK DOWN-POINTING SMALL TRIANGLE
613                     ColumnText[c] = ColumnOrgText[c] + "▾";
614                 }
615                 else
616                 {
617                     // U+25B4 BLACK UP-POINTING SMALL TRIANGLE
618                     ColumnText[c] = ColumnOrgText[c] + "▴";
619                 }
620             }
621         }
622
623         private void InitializeTraceFrag()
624         {
625 #if DEBUG
626             TraceOutToolStripMenuItem.Checked = true;
627             MyCommon.TraceFlag = true;
628 #endif
629             if (!MyCommon.FileVersion.EndsWith("0"))
630             {
631                 TraceOutToolStripMenuItem.Checked = true;
632                 MyCommon.TraceFlag = true;
633             }
634         }
635
636         private void TweenMain_Load(object sender, EventArgs e)
637         {
638             _ignoreConfigSave = true;
639             this.Visible = false;
640
641             //Win32Api.SetProxy(HttpConnection.ProxyType.Specified, "127.0.0.1", 8080, "user", "pass")
642
643             new InternetSecurityManager(PostBrowser);
644             this.PostBrowser.AllowWebBrowserDrop = false;  // COMException を回避するため、ActiveX の初期化が終わってから設定する
645
646             MyCommon.TwitterApiInfo.AccessLimitUpdated += TwitterApiStatus_AccessLimitUpdated;
647             Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
648
649             if (MyApplication.StartupOptions.ContainsKey("d"))
650                 MyCommon.TraceFlag = true;
651
652             Regex.CacheSize = 100;
653
654             InitializeTraceFrag();
655             LoadIcons(); // アイコン読み込み
656
657             //発言保持クラス
658             _statuses = TabInformations.GetInstance();
659
660             //アイコン設定
661             this.Icon = MainIcon;              //メインフォーム(TweenMain)
662             NotifyIcon1.Icon = NIconAt;      //タスクトレイ
663             TabImage.Images.Add(TabIcon);    //タブ見出し
664
665             SettingDialog.Owner = this;;
666             SearchDialog.Owner = this;
667             fltDialog.Owner = this;
668             UrlDialog.Owner = this;
669
670             _history.Add(new PostingStatus());
671             _hisIdx = 0;
672             _reply_to_id = null;
673             _reply_to_name = null;
674
675             //<<<<<<<<<設定関連>>>>>>>>>
676             //設定コンバージョン
677             //ConvertConfig();
678
679             ////設定読み出し
680             LoadConfig();
681
682             ThumbnailGenerator.InitializeGenerator();
683
684             var imgazyobizinet = ThumbnailGenerator.ImgAzyobuziNetInstance;
685             imgazyobizinet.Enabled = this._cfgCommon.EnableImgAzyobuziNet;
686             imgazyobizinet.DisabledInDM = this._cfgCommon.ImgAzyobuziNetDisabledInDM;
687
688             Thumbnail.Services.TonTwitterCom.InitializeOAuthToken = x =>
689                 x.Initialize(ApplicationSettings.TwitterConsumerKey, ApplicationSettings.TwitterConsumerSecret,
690                     this.tw.AccessToken, this.tw.AccessTokenSecret, "", "");
691
692             //新着バルーン通知のチェック状態設定
693             NewPostPopMenuItem.Checked = _cfgCommon.NewAllPop;
694             this.NotifyFileMenuItem.Checked = NewPostPopMenuItem.Checked;
695
696             //フォント&文字色&背景色保持
697             _fntUnread = _cfgLocal.FontUnread;
698             _clUnread = _cfgLocal.ColorUnread;
699             _fntReaded = _cfgLocal.FontRead;
700             _clReaded = _cfgLocal.ColorRead;
701             _clFav = _cfgLocal.ColorFav;
702             _clOWL = _cfgLocal.ColorOWL;
703             _clRetweet = _cfgLocal.ColorRetweet;
704             _fntDetail = _cfgLocal.FontDetail;
705             _clDetail = _cfgLocal.ColorDetail;
706             _clDetailLink = _cfgLocal.ColorDetailLink;
707             _clDetailBackcolor = _cfgLocal.ColorDetailBackcolor;
708             _clSelf = _cfgLocal.ColorSelf;
709             _clAtSelf = _cfgLocal.ColorAtSelf;
710             _clTarget = _cfgLocal.ColorTarget;
711             _clAtTarget = _cfgLocal.ColorAtTarget;
712             _clAtFromTarget = _cfgLocal.ColorAtFromTarget;
713             _clAtTo = _cfgLocal.ColorAtTo;
714             _clListBackcolor = _cfgLocal.ColorListBackcolor;
715             _clInputBackcolor = _cfgLocal.ColorInputBackcolor;
716             _clInputFont = _cfgLocal.ColorInputFont;
717             _fntInputFont = _cfgLocal.FontInputFont;
718
719             var fontUIGlobal = this._cfgLocal.FontUIGlobal;
720             if (fontUIGlobal != null)
721             {
722                 OTBaseForm.GlobalFont = fontUIGlobal;
723                 this.Font = fontUIGlobal;
724             }
725
726             // StringFormatオブジェクトへの事前設定
727             //sf.Alignment = StringAlignment.Near;             // Textを近くへ配置(左から右の場合は左寄せ)
728             //sf.LineAlignment = StringAlignment.Near;         // Textを近くへ配置(上寄せ)
729             //sf.FormatFlags = StringFormatFlags.LineLimit;    // 
730             sfTab.Alignment = StringAlignment.Center;
731             sfTab.LineAlignment = StringAlignment.Center;
732
733             //不正値チェック
734             if (!MyApplication.StartupOptions.ContainsKey("nolimit"))
735             {
736                 if (this._cfgCommon.TimelinePeriod < 15 && this._cfgCommon.TimelinePeriod > 0)
737                     this._cfgCommon.TimelinePeriod = 15;
738
739                 if (this._cfgCommon.ReplyPeriod < 15 && this._cfgCommon.ReplyPeriod > 0)
740                     this._cfgCommon.ReplyPeriod = 15;
741
742                 if (this._cfgCommon.DMPeriod < 15 && this._cfgCommon.DMPeriod > 0)
743                     this._cfgCommon.DMPeriod = 15;
744
745                 if (this._cfgCommon.PubSearchPeriod < 30 && this._cfgCommon.PubSearchPeriod > 0)
746                     this._cfgCommon.PubSearchPeriod = 30;
747
748                 if (this._cfgCommon.UserTimelinePeriod < 15 && this._cfgCommon.UserTimelinePeriod > 0)
749                     this._cfgCommon.UserTimelinePeriod = 15;
750
751                 if (this._cfgCommon.ListsPeriod < 15 && this._cfgCommon.ListsPeriod > 0)
752                     this._cfgCommon.ListsPeriod = 15;
753             }
754
755             //設定画面への反映
756             this.SettingDialog.LoadConfig(this._cfgCommon, this._cfgLocal);
757             HttpTwitter.TwitterUrl = _cfgCommon.TwitterUrl;
758             SettingDialog.TwitterApiUrl = _cfgCommon.TwitterUrl;
759
760             //認証関連
761             if (string.IsNullOrEmpty(_cfgCommon.Token)) _cfgCommon.UserName = "";
762             tw.Initialize(_cfgCommon.Token, _cfgCommon.TokenSecret, _cfgCommon.UserName, _cfgCommon.UserId);
763
764             SettingDialog.UserAccounts = _cfgCommon.UserAccounts;
765
766             //新着取得時のリストスクロールをするか。trueならスクロールしない
767             ListLockMenuItem.Checked = _cfgCommon.ListLock;
768             this.LockListFileMenuItem.Checked = _cfgCommon.ListLock;
769             //未読管理。trueなら未読管理する
770             SettingDialog.UnreadManage = _cfgCommon.UnreadManage;
771             //サウンド再生(タブ別設定より優先)
772             SettingDialog.PlaySound = _cfgCommon.PlaySound;
773             PlaySoundMenuItem.Checked = SettingDialog.PlaySound;
774             this.PlaySoundFileMenuItem.Checked = SettingDialog.PlaySound;
775             //フォント&文字色&背景色
776             SettingDialog.FontUnread = _fntUnread;
777             SettingDialog.ColorUnread = _clUnread;
778             SettingDialog.FontReaded = _fntReaded;
779             SettingDialog.ColorReaded = _clReaded;
780             SettingDialog.ColorFav = _clFav;
781             SettingDialog.ColorOWL = _clOWL;
782             SettingDialog.ColorRetweet = _clRetweet;
783             SettingDialog.FontDetail = _fntDetail;
784             SettingDialog.ColorDetail = _clDetail;
785             SettingDialog.ColorDetailLink = _clDetailLink;
786             SettingDialog.ColorDetailBackcolor = _clDetailBackcolor;
787             SettingDialog.ColorSelf = _clSelf;
788             SettingDialog.ColorAtSelf = _clAtSelf;
789             SettingDialog.ColorTarget = _clTarget;
790             SettingDialog.ColorAtTarget = _clAtTarget;
791             SettingDialog.ColorAtFromTarget = _clAtFromTarget;
792             SettingDialog.ColorAtTo = _clAtTo;
793             SettingDialog.ColorListBackcolor = _clListBackcolor;
794             SettingDialog.ColorInputBackcolor = _clInputBackcolor;
795             SettingDialog.ColorInputFont = _clInputFont;
796             SettingDialog.FontInputFont = _fntInputFont;
797
798             SettingDialog.NameBalloon = _cfgCommon.NameBalloon;
799
800             SettingDialog.CountApi = _cfgCommon.CountApi;
801             SettingDialog.CountApiReply = _cfgCommon.CountApiReply;
802             if (SettingDialog.CountApi < 20 || SettingDialog.CountApi > 200) SettingDialog.CountApi = 60;
803             if (SettingDialog.CountApiReply < 20 || SettingDialog.CountApiReply > 200) SettingDialog.CountApiReply = 40;
804
805             SettingDialog.BrowserPath = _cfgLocal.BrowserPath;
806             SettingDialog.PostAndGet = _cfgCommon.PostAndGet;
807             SettingDialog.DispUsername = _cfgCommon.DispUsername;
808             SettingDialog.CloseToExit = _cfgCommon.CloseToExit;
809             SettingDialog.MinimizeToTray = _cfgCommon.MinimizeToTray;
810             SettingDialog.DispLatestPost = _cfgCommon.DispLatestPost;
811             SettingDialog.TinyUrlResolve = _cfgCommon.TinyUrlResolve;
812
813             SettingDialog.SelectedProxyType = _cfgLocal.ProxyType;
814             SettingDialog.ProxyAddress = _cfgLocal.ProxyAddress;
815             SettingDialog.ProxyPort = _cfgLocal.ProxyPort;
816             SettingDialog.ProxyUser = _cfgLocal.ProxyUser;
817             SettingDialog.ProxyPassword = _cfgLocal.ProxyPassword;
818
819             SettingDialog.RestrictFavCheck = _cfgCommon.RestrictFavCheck;
820             SettingDialog.AlwaysTop = _cfgCommon.AlwaysTop;
821             SettingDialog.UrlConvertAuto = false;
822             //SettingDialog.UrlConvertAuto = _cfgCommon.UrlConvertAuto;
823
824             SettingDialog.DefaultTimeOut = _cfgCommon.DefaultTimeOut;
825             SettingDialog.PlaySound = _cfgCommon.PlaySound;
826             SettingDialog.LimitBalloon = _cfgCommon.LimitBalloon;
827             SettingDialog.EventNotifyEnabled = _cfgCommon.EventNotifyEnabled;
828             SettingDialog.EventNotifyFlag = _cfgCommon.EventNotifyFlag;
829             SettingDialog.IsMyEventNotifyFlag = _cfgCommon.IsMyEventNotifyFlag;
830             SettingDialog.ForceEventNotify = _cfgCommon.ForceEventNotify;
831             SettingDialog.FavEventUnread = _cfgCommon.FavEventUnread;
832             SettingDialog.TranslateLanguage = _cfgCommon.TranslateLanguage;
833             SettingDialog.EventSoundFile = _cfgCommon.EventSoundFile;
834
835             //廃止サービスが選択されていた場合bit.lyへ読み替え
836             if (_cfgCommon.AutoShortUrlFirst < 0)
837                 _cfgCommon.AutoShortUrlFirst = MyCommon.UrlConverter.Uxnu;
838
839             SettingDialog.AutoShortUrlFirst = _cfgCommon.AutoShortUrlFirst;
840             SettingDialog.TabIconDisp = _cfgCommon.TabIconDisp;
841             SettingDialog.ReplyIconState = _cfgCommon.ReplyIconState;
842             SettingDialog.ReadOwnPost = _cfgCommon.ReadOwnPost;
843             SettingDialog.ReadOldPosts = _cfgCommon.ReadOldPosts;
844             SettingDialog.BitlyUser = _cfgCommon.BilyUser;
845             SettingDialog.BitlyPwd = _cfgCommon.BitlyPwd;
846             SettingDialog.Language = _cfgCommon.Language;
847             SettingDialog.PreviewEnable = _cfgCommon.PreviewEnable;
848             SettingDialog.StatusAreaAtBottom = _cfgCommon.StatusAreaAtBottom;
849             AtIdSupl = new AtIdSupplement(SettingAtIdList.Load().AtIdList, "@");
850
851             SettingDialog.IsMonospace = _cfgCommon.IsMonospace;
852
853             this.IdeographicSpaceToSpaceToolStripMenuItem.Checked = _cfgCommon.WideSpaceConvert;
854             this.ToolStripFocusLockMenuItem.Checked = _cfgCommon.FocusLockToStatusText;
855
856             //Regex statregex = new Regex("^0*");
857             SettingDialog.RecommendStatusText = " [TWNv" + Regex.Replace(MyCommon.FileVersion.Replace(".", ""), "^0*", "") + "]";
858
859             SettingDialog.Nicoms = _cfgCommon.Nicoms;
860             SettingDialog.HotkeyEnabled = _cfgCommon.HotkeyEnabled;
861             SettingDialog.HotkeyMod = _cfgCommon.HotkeyModifier;
862             SettingDialog.HotkeyKey = _cfgCommon.HotkeyKey;
863             SettingDialog.HotkeyValue = _cfgCommon.HotkeyValue;
864
865             SettingDialog.BlinkNewMentions = _cfgCommon.BlinkNewMentions;
866
867             SettingDialog.UseAdditionalCount = _cfgCommon.UseAdditionalCount;
868             SettingDialog.MoreCountApi = _cfgCommon.MoreCountApi;
869             SettingDialog.FirstCountApi = _cfgCommon.FirstCountApi;
870             SettingDialog.SearchCountApi = _cfgCommon.SearchCountApi;
871             SettingDialog.FavoritesCountApi = _cfgCommon.FavoritesCountApi;
872             SettingDialog.UserTimelineCountApi = _cfgCommon.UserTimelineCountApi;
873             SettingDialog.ListCountApi = _cfgCommon.ListCountApi;
874
875             SettingDialog.OpenUserTimeline = _cfgCommon.OpenUserTimeline;
876             SettingDialog.ListDoubleClickAction = _cfgCommon.ListDoubleClickAction;
877             SettingDialog.UserAppointUrl = _cfgCommon.UserAppointUrl;
878
879             SettingDialog.EnableImgAzyobuziNet = _cfgCommon.EnableImgAzyobuziNet;
880             SettingDialog.ImgAzyobuziNetDisabledInDM = _cfgCommon.ImgAzyobuziNetDisabledInDM;
881             SettingDialog.MapThumbnailProvider = _cfgCommon.MapThumbnailProvider;
882             SettingDialog.MapThumbnailHeight = _cfgCommon.MapThumbnailHeight;
883             SettingDialog.MapThumbnailWidth = _cfgCommon.MapThumbnailWidth;
884             SettingDialog.MapThumbnailZoom = _cfgCommon.MapThumbnailZoom;
885             SettingDialog.TabMouseLock = _cfgCommon.TabMouseLock;
886             SettingDialog.IsRemoveSameEvent = _cfgCommon.IsRemoveSameEvent;
887             SettingDialog.IsNotifyUseGrowl = _cfgCommon.IsUseNotifyGrowl;
888
889             //ハッシュタグ関連
890             HashSupl = new AtIdSupplement(_cfgCommon.HashTags, "#");
891             HashMgr = new HashtagManage(HashSupl,
892                                     _cfgCommon.HashTags.ToArray(),
893                                     _cfgCommon.HashSelected,
894                                     _cfgCommon.HashIsPermanent,
895                                     _cfgCommon.HashIsHead,
896                                     _cfgCommon.HashIsNotAddToAtReply);
897             if (!string.IsNullOrEmpty(HashMgr.UseHash) && HashMgr.IsPermanent) HashStripSplitButton.Text = HashMgr.UseHash;
898
899             _initial = true;
900
901             Networking.Initialize();
902
903             //アイコンリスト作成
904             this.IconCache = new ImageCache();
905
906             bool saveRequired = false;
907             bool firstRun = false;
908
909             //ユーザー名、パスワードが未設定なら設定画面を表示(初回起動時など)
910             if (string.IsNullOrEmpty(tw.Username))
911             {
912                 saveRequired = true;
913                 firstRun = true;
914                 SettingDialog.ShowInTaskbar = true;
915
916                 //設定せずにキャンセルされた場合はプログラム終了
917                 if (SettingDialog.ShowDialog(this) == DialogResult.Cancel)
918                 {
919                     Application.Exit();  //強制終了
920                     return;
921                 }
922                 //設定されたが、依然ユーザー名とパスワードが未設定ならプログラム終了
923                 if (string.IsNullOrEmpty(tw.Username))
924                 {
925                     Application.Exit();  //強制終了
926                     return;
927                 }
928                 SettingDialog.ShowInTaskbar = false;
929
930                 //新しい設定を反映
931                 //フォント&文字色&背景色保持
932                 _fntUnread = SettingDialog.FontUnread;
933                 _clUnread = SettingDialog.ColorUnread;
934                 _fntReaded = SettingDialog.FontReaded;
935                 _clReaded = SettingDialog.ColorReaded;
936                 _clFav = SettingDialog.ColorFav;
937                 _clOWL = SettingDialog.ColorOWL;
938                 _clRetweet = SettingDialog.ColorRetweet;
939                 _fntDetail = SettingDialog.FontDetail;
940                 _clDetail = SettingDialog.ColorDetail;
941                 _clDetailLink = SettingDialog.ColorDetailLink;
942                 _clDetailBackcolor = SettingDialog.ColorDetailBackcolor;
943                 _clSelf = SettingDialog.ColorSelf;
944                 _clAtSelf = SettingDialog.ColorAtSelf;
945                 _clTarget = SettingDialog.ColorTarget;
946                 _clAtTarget = SettingDialog.ColorAtTarget;
947                 _clAtFromTarget = SettingDialog.ColorAtFromTarget;
948                 _clAtTo = SettingDialog.ColorAtTo;
949                 _clListBackcolor = SettingDialog.ColorListBackcolor;
950                 _clInputBackcolor = SettingDialog.ColorInputBackcolor;
951                 _clInputFont = SettingDialog.ColorInputFont;
952                 _fntInputFont = SettingDialog.FontInputFont;
953
954                 //他の設定項目は、随時設定画面で保持している値を読み出して使用
955             }
956
957             _brsBackColorMine = new SolidBrush(_clSelf);
958             _brsBackColorAt = new SolidBrush(_clAtSelf);
959             _brsBackColorYou = new SolidBrush(_clTarget);
960             _brsBackColorAtYou = new SolidBrush(_clAtTarget);
961             _brsBackColorAtFromTarget = new SolidBrush(_clAtFromTarget);
962             _brsBackColorAtTo = new SolidBrush(_clAtTo);
963             //_brsBackColorNone = new SolidBrush(Color.FromKnownColor(KnownColor.Window));
964             _brsBackColorNone = new SolidBrush(_clListBackcolor);
965
966             InitDetailHtmlFormat();
967
968             if (SettingDialog.HotkeyEnabled)
969             {
970                 //////グローバルホットキーの登録
971                 HookGlobalHotkey.ModKeys modKey = HookGlobalHotkey.ModKeys.None;
972                 if ((SettingDialog.HotkeyMod & Keys.Alt) == Keys.Alt) modKey = modKey | HookGlobalHotkey.ModKeys.Alt;
973                 if ((SettingDialog.HotkeyMod & Keys.Control) == Keys.Control) modKey = modKey | HookGlobalHotkey.ModKeys.Ctrl;
974                 if ((SettingDialog.HotkeyMod & Keys.Shift) == Keys.Shift) modKey = modKey | HookGlobalHotkey.ModKeys.Shift;
975                 if ((SettingDialog.HotkeyMod & Keys.LWin) == Keys.LWin) modKey = modKey | HookGlobalHotkey.ModKeys.Win;
976
977                 _hookGlobalHotkey.RegisterOriginalHotkey(SettingDialog.HotkeyKey, SettingDialog.HotkeyValue, modKey);
978             }
979
980             //Twitter用通信クラス初期化
981             Networking.DefaultTimeout = TimeSpan.FromSeconds(this.SettingDialog.DefaultTimeOut);
982             Networking.SetWebProxy(this.SettingDialog.SelectedProxyType,
983                 this.SettingDialog.ProxyAddress, this.SettingDialog.ProxyPort,
984                 this.SettingDialog.ProxyUser, this.SettingDialog.ProxyPassword);
985
986             tw.RestrictFavCheck = SettingDialog.RestrictFavCheck;
987             tw.ReadOwnPost = SettingDialog.ReadOwnPost;
988             ShortUrl.Instance.DisableExpanding = !SettingDialog.TinyUrlResolve;
989             ShortUrl.Instance.BitlyId = SettingDialog.BitlyUser;
990             ShortUrl.Instance.BitlyKey = SettingDialog.BitlyPwd;
991             HttpTwitter.TwitterUrl = _cfgCommon.TwitterUrl;
992             tw.TrackWord = _cfgCommon.TrackWord;
993             TrackToolStripMenuItem.Checked = !String.IsNullOrEmpty(tw.TrackWord);
994             tw.AllAtReply = _cfgCommon.AllAtReply;
995             AllrepliesToolStripMenuItem.Checked = tw.AllAtReply;
996
997             //画像投稿サービス
998             ImageSelector.Initialize(tw, SettingDialog.TwitterConfiguration, _cfgCommon.UseImageServiceName, _cfgCommon.UseImageService);
999
1000             //ウィンドウ設定
1001             this.ClientSize = _cfgLocal.FormSize;
1002             _mySize = _cfgLocal.FormSize;                     //サイズ保持(最小化・最大化されたまま終了した場合の対応用)
1003             _myLoc = _cfgLocal.FormLocation;
1004             //タイトルバー領域
1005             if (this.WindowState != FormWindowState.Minimized)
1006             {
1007                 this.DesktopLocation = _cfgLocal.FormLocation;
1008                 Rectangle tbarRect = new Rectangle(this.Location, new Size(_mySize.Width, SystemInformation.CaptionHeight));
1009                 bool outOfScreen = true;
1010                 if (Screen.AllScreens.Length == 1)    //ハングするとの報告
1011                 {
1012                     foreach (Screen scr in Screen.AllScreens)
1013                     {
1014                         if (!Rectangle.Intersect(tbarRect, scr.Bounds).IsEmpty)
1015                         {
1016                             outOfScreen = false;
1017                             break;
1018                         }
1019                     }
1020                     if (outOfScreen)
1021                     {
1022                         this.DesktopLocation = new Point(0, 0);
1023                         _myLoc = this.DesktopLocation;
1024                     }
1025                 }
1026             }
1027             this.TopMost = SettingDialog.AlwaysTop;
1028             _mySpDis = _cfgLocal.SplitterDistance;
1029             _mySpDis2 = _cfgLocal.StatusTextHeight;
1030             _mySpDis3 = _cfgLocal.PreviewDistance;
1031             if (_mySpDis3 == -1)
1032             {
1033                 _mySpDis3 = _mySize.Width - 150;
1034                 if (_mySpDis3 < 1) _mySpDis3 = 50;
1035                 _cfgLocal.PreviewDistance = _mySpDis3;
1036             }
1037             MultiLineMenuItem.Checked = _cfgLocal.StatusMultiline;
1038             //this.Tween_ClientSizeChanged(this, null);
1039             PlaySoundMenuItem.Checked = SettingDialog.PlaySound;
1040             this.PlaySoundFileMenuItem.Checked = SettingDialog.PlaySound;
1041             //入力欄
1042             StatusText.Font = _fntInputFont;
1043             StatusText.ForeColor = _clInputFont;
1044
1045             // NameLabel のフォントを OTBaseForm.GlobalFont に変更
1046             this.NameLabel.Font = this.ReplaceToGlobalFont(this.NameLabel.Font);
1047
1048             // 必要であれば、発言一覧と発言詳細部・入力欄の上下を入れ替える
1049             SplitContainer1.IsPanelInverted = !SettingDialog.StatusAreaAtBottom;
1050
1051             //全新着通知のチェック状態により、Reply&DMの新着通知有効無効切り替え(タブ別設定にするため削除予定)
1052             if (SettingDialog.UnreadManage == false)
1053             {
1054                 ReadedStripMenuItem.Enabled = false;
1055                 UnreadStripMenuItem.Enabled = false;
1056             }
1057
1058             if (SettingDialog.IsNotifyUseGrowl) gh.RegisterGrowl();
1059
1060             //タイマー設定
1061             TimerTimeline.AutoReset = true;
1062             TimerTimeline.SynchronizingObject = this;
1063             //Recent取得間隔
1064             TimerTimeline.Interval = 1000;
1065             TimerTimeline.Enabled = true;
1066
1067             //更新中アイコンアニメーション間隔
1068             TimerRefreshIcon.Interval = 200;
1069             TimerRefreshIcon.Enabled = true;
1070
1071             //状態表示部の初期化(画面右下)
1072             StatusLabel.Text = "";
1073             StatusLabel.AutoToolTip = false;
1074             StatusLabel.ToolTipText = "";
1075             //文字カウンタ初期化
1076             lblLen.Text = GetRestStatusCount(true, false).ToString();
1077
1078             ////////////////////////////////////////////////////////////////////////////////
1079             _statuses.SortOrder = (SortOrder)_cfgCommon.SortOrder;
1080             IdComparerClass.ComparerMode mode = IdComparerClass.ComparerMode.Id;
1081             switch (_cfgCommon.SortColumn)
1082             {
1083                 case 0:    //0:アイコン,5:未読マーク,6:プロテクト・フィルターマーク
1084                 case 5:
1085                 case 6:
1086                     //ソートしない
1087                     mode = IdComparerClass.ComparerMode.Id;  //Idソートに読み替え
1088                     break;
1089                 case 1:  //ニックネーム
1090                     mode = IdComparerClass.ComparerMode.Nickname;
1091                     break;
1092                 case 2:  //本文
1093                     mode = IdComparerClass.ComparerMode.Data;
1094                     break;
1095                 case 3:  //時刻=発言Id
1096                     mode = IdComparerClass.ComparerMode.Id;
1097                     break;
1098                 case 4:  //名前
1099                     mode = IdComparerClass.ComparerMode.Name;
1100                     break;
1101                 case 7:  //Source
1102                     mode = IdComparerClass.ComparerMode.Source;
1103                     break;
1104             }
1105             _statuses.SortMode = mode;
1106             ////////////////////////////////////////////////////////////////////////////////
1107
1108             ApplyListViewIconSize(this._cfgCommon.IconSize);
1109
1110             StatusLabel.Text = Properties.Resources.Form1_LoadText1;       //画面右下の状態表示を変更
1111             StatusLabelUrl.Text = "";            //画面左下のリンク先URL表示部を初期化
1112             NameLabel.Text = "";                 //発言詳細部名前ラベル初期化
1113             DateTimeLabel.Text = "";             //発言詳細部日時ラベル初期化
1114             SourceLinkLabel.Text = "";           //Source部分初期化
1115
1116             //<<<<<<<<タブ関連>>>>>>>
1117             //デフォルトタブの存在チェック、ない場合には追加
1118             if (_statuses.GetTabByType(MyCommon.TabUsageType.Home) == null)
1119             {
1120                 TabClass tab;
1121                 if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.RECENT, out tab))
1122                 {
1123                     _statuses.AddTab(MyCommon.DEFAULTTAB.RECENT, MyCommon.TabUsageType.Home, null);
1124                 }
1125                 else
1126                 {
1127                     tab.TabType = MyCommon.TabUsageType.Home;
1128                 }
1129             }
1130             if (_statuses.GetTabByType(MyCommon.TabUsageType.Mentions) == null)
1131             {
1132                 TabClass tab;
1133                 if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.REPLY, out tab))
1134                 {
1135                     _statuses.AddTab(MyCommon.DEFAULTTAB.REPLY, MyCommon.TabUsageType.Mentions, null);
1136                 }
1137                 else
1138                 {
1139                     tab.TabType = MyCommon.TabUsageType.Mentions;
1140                 }
1141             }
1142             if (_statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage) == null)
1143             {
1144                 TabClass tab;
1145                 if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.DM, out tab))
1146                 {
1147                     _statuses.AddTab(MyCommon.DEFAULTTAB.DM, MyCommon.TabUsageType.DirectMessage, null);
1148                 }
1149                 else
1150                 {
1151                     tab.TabType = MyCommon.TabUsageType.DirectMessage;
1152                 }
1153             }
1154             if (_statuses.GetTabByType(MyCommon.TabUsageType.Favorites) == null)
1155             {
1156                 TabClass tab;
1157                 if (!_statuses.Tabs.TryGetValue(MyCommon.DEFAULTTAB.FAV, out tab))
1158                 {
1159                     _statuses.AddTab(MyCommon.DEFAULTTAB.FAV, MyCommon.TabUsageType.Favorites, null);
1160                 }
1161                 else
1162                 {
1163                     tab.TabType = MyCommon.TabUsageType.Favorites;
1164                 }
1165             }
1166             foreach (var tab in _statuses.Tabs.Values)
1167             {
1168                 if (tab.TabType == MyCommon.TabUsageType.Undefined)
1169                 {
1170                     tab.TabType = MyCommon.TabUsageType.UserDefined;
1171                 }
1172                 if (!AddNewTab(tab.TabName, true, tab.TabType, tab.ListInfo))
1173                     throw new TabException(Properties.Resources.TweenMain_LoadText1);
1174             }
1175
1176             this.JumpReadOpMenuItem.ShortcutKeyDisplayString = "Space";
1177             CopySTOTMenuItem.ShortcutKeyDisplayString = "Ctrl+C";
1178             CopyURLMenuItem.ShortcutKeyDisplayString = "Ctrl+Shift+C";
1179             CopyUserIdStripMenuItem.ShortcutKeyDisplayString = "Shift+Alt+C";
1180
1181             if (SettingDialog.MinimizeToTray == false || this.WindowState != FormWindowState.Minimized)
1182             {
1183                 this.Visible = true;
1184             }
1185             _curTab = ListTab.SelectedTab;
1186             _curItemIndex = -1;
1187             _curList = (DetailsListView)_curTab.Tag;
1188             SetMainWindowTitle();
1189             SetNotifyIconText();
1190
1191             if (SettingDialog.TabIconDisp)
1192             {
1193                 ListTab.DrawMode = TabDrawMode.Normal;
1194             }
1195             else
1196             {
1197                 ListTab.DrawMode = TabDrawMode.OwnerDrawFixed;
1198                 ListTab.DrawItem += ListTab_DrawItem;
1199                 ListTab.ImageList = null;
1200             }
1201
1202             _ignoreConfigSave = false;
1203             this.TweenMain_Resize(null, null);
1204             if (saveRequired) SaveConfigsAll(false);
1205
1206             if (tw.UserId == 0)
1207             {
1208                 tw.VerifyCredentials();
1209                 foreach (UserAccount ua in _cfgCommon.UserAccounts)
1210                 {
1211                     if (ua.Username.ToLower() == tw.Username.ToLower())
1212                     {
1213                         ua.UserId = tw.UserId;
1214                         break;
1215                     }
1216                 }
1217             }
1218             foreach (UserAccount ua in SettingDialog.UserAccounts)
1219             {
1220                 if (ua.UserId == 0 && ua.Username.ToLower() == tw.Username.ToLower())
1221                 {
1222                     ua.UserId = tw.UserId;
1223                     break;
1224                 }
1225             }
1226
1227             if (firstRun)
1228             {
1229                 // 初回起動時だけ右下のメニューを目立たせる
1230                 HashStripSplitButton.ShowDropDown();
1231             }
1232
1233             // タブの位置を調整する
1234             SetTabAlignment();
1235         }
1236
1237         private void InitDetailHtmlFormat()
1238         {
1239             if (SettingDialog.IsMonospace)
1240             {
1241                 detailHtmlFormatHeader = detailHtmlFormatMono1;
1242                 detailHtmlFormatFooter = detailHtmlFormatMono7;
1243             }
1244             else
1245             {
1246                 detailHtmlFormatHeader = detailHtmlFormat1;
1247                 detailHtmlFormatFooter = detailHtmlFormat7;
1248             }
1249             detailHtmlFormatHeader += _fntDetail.Name + detailHtmlFormat2 + _fntDetail.Size.ToString() + detailHtmlFormat3 + _clDetail.R.ToString() + "," + _clDetail.G.ToString() + "," + _clDetail.B.ToString() + detailHtmlFormat4 + _clDetailLink.R.ToString() + "," + _clDetailLink.G.ToString() + "," + _clDetailLink.B.ToString() + detailHtmlFormat5 + _clDetailBackcolor.R.ToString() + "," + _clDetailBackcolor.G.ToString() + "," + _clDetailBackcolor.B.ToString();
1250             if (SettingDialog.IsMonospace)
1251             {
1252                 detailHtmlFormatHeader += detailHtmlFormatMono6;
1253             }
1254             else
1255             {
1256                 detailHtmlFormatHeader += detailHtmlFormat6;
1257             }
1258         }
1259
1260         private void ListTab_DrawItem(object sender, DrawItemEventArgs e)
1261         {
1262             string txt;
1263             try
1264             {
1265                 txt = ListTab.TabPages[e.Index].Text;
1266             }
1267             catch (Exception)
1268             {
1269                 return;
1270             }
1271
1272             e.Graphics.FillRectangle(System.Drawing.SystemBrushes.Control, e.Bounds);
1273             if (e.State == DrawItemState.Selected)
1274             {
1275                 e.DrawFocusRectangle();
1276             }
1277             Brush fore;
1278             try
1279             {
1280                 if (_statuses.Tabs[txt].UnreadCount > 0)
1281                     fore = Brushes.Red;
1282                 else
1283                     fore = System.Drawing.SystemBrushes.ControlText;
1284             }
1285             catch (Exception)
1286             {
1287                 fore = System.Drawing.SystemBrushes.ControlText;
1288             }
1289             e.Graphics.DrawString(txt, e.Font, fore, e.Bounds, sfTab);
1290         }
1291
1292         private void LoadConfig()
1293         {
1294             _cfgCommon = SettingCommon.Load();
1295             SettingCommon.Instance = this._cfgCommon;
1296             if (_cfgCommon.UserAccounts == null || _cfgCommon.UserAccounts.Count == 0)
1297             {
1298                 _cfgCommon.UserAccounts = new List<UserAccount>();
1299                 if (!string.IsNullOrEmpty(_cfgCommon.UserName))
1300                 {
1301                     UserAccount account = new UserAccount();
1302                     account.Username = _cfgCommon.UserName;
1303                     account.UserId = _cfgCommon.UserId;
1304                     account.Token = _cfgCommon.Token;
1305                     account.TokenSecret = _cfgCommon.TokenSecret;
1306
1307                     _cfgCommon.UserAccounts.Add(account);
1308                 }
1309             }
1310             _cfgLocal = SettingLocal.Load();
1311             List<TabClass> tabs = SettingTabs.Load().Tabs;
1312             foreach (TabClass tb in tabs)
1313             {
1314                 try
1315                 {
1316                     _statuses.Tabs.Add(tb.TabName, tb);
1317                 }
1318                 catch (Exception)
1319                 {
1320                     tb.TabName = _statuses.GetUniqueTabName();
1321                     _statuses.Tabs.Add(tb.TabName, tb);
1322                 }
1323             }
1324             if (_statuses.Tabs.Count == 0)
1325             {
1326                 _statuses.AddTab(MyCommon.DEFAULTTAB.RECENT, MyCommon.TabUsageType.Home, null);
1327                 _statuses.AddTab(MyCommon.DEFAULTTAB.REPLY, MyCommon.TabUsageType.Mentions, null);
1328                 _statuses.AddTab(MyCommon.DEFAULTTAB.DM, MyCommon.TabUsageType.DirectMessage, null);
1329                 _statuses.AddTab(MyCommon.DEFAULTTAB.FAV, MyCommon.TabUsageType.Favorites, null);
1330             }
1331         }
1332
1333         private void TimerInterval_Changed(object sender, IntervalChangedEventArgs e) //Handles SettingDialog.IntervalChanged
1334         {
1335             if (!TimerTimeline.Enabled) return;
1336             ResetTimers = e;
1337         }
1338
1339         private IntervalChangedEventArgs ResetTimers = new IntervalChangedEventArgs();
1340
1341         private static int homeCounter = 0;
1342         private static int mentionCounter = 0;
1343         private static int dmCounter = 0;
1344         private static int pubSearchCounter = 0;
1345         private static int userTimelineCounter = 0;
1346         private static int listsCounter = 0;
1347         private static int usCounter = 0;
1348         private static int ResumeWait = 0;
1349         private static int refreshFollowers = 0;
1350
1351         private void TimerTimeline_Elapsed(object sender, EventArgs e)
1352         {
1353             if (homeCounter > 0) Interlocked.Decrement(ref homeCounter);
1354             if (mentionCounter > 0) Interlocked.Decrement(ref mentionCounter);
1355             if (dmCounter > 0) Interlocked.Decrement(ref dmCounter);
1356             if (pubSearchCounter > 0) Interlocked.Decrement(ref pubSearchCounter);
1357             if (userTimelineCounter > 0) Interlocked.Decrement(ref userTimelineCounter);
1358             if (listsCounter > 0) Interlocked.Decrement(ref listsCounter);
1359             if (usCounter > 0) Interlocked.Decrement(ref usCounter);
1360             Interlocked.Increment(ref refreshFollowers);
1361
1362             ////タイマー初期化
1363             if (ResetTimers.Timeline || homeCounter <= 0 && this._cfgCommon.TimelinePeriod > 0)
1364             {
1365                 Interlocked.Exchange(ref homeCounter, this._cfgCommon.TimelinePeriod);
1366                 if (!tw.IsUserstreamDataReceived && !ResetTimers.Timeline) GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
1367                 ResetTimers.Timeline = false;
1368             }
1369             if (ResetTimers.Reply || mentionCounter <= 0 && this._cfgCommon.ReplyPeriod > 0)
1370             {
1371                 Interlocked.Exchange(ref mentionCounter, this._cfgCommon.ReplyPeriod);
1372                 if (!tw.IsUserstreamDataReceived && !ResetTimers.Reply) GetTimeline(MyCommon.WORKERTYPE.Reply, 1, "");
1373                 ResetTimers.Reply = false;
1374             }
1375             if (ResetTimers.DirectMessage || dmCounter <= 0 && this._cfgCommon.DMPeriod > 0)
1376             {
1377                 Interlocked.Exchange(ref dmCounter, this._cfgCommon.DMPeriod);
1378                 if (!tw.IsUserstreamDataReceived && !ResetTimers.DirectMessage) GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
1379                 ResetTimers.DirectMessage = false;
1380             }
1381             if (ResetTimers.PublicSearch || pubSearchCounter <= 0 && this._cfgCommon.PubSearchPeriod > 0)
1382             {
1383                 Interlocked.Exchange(ref pubSearchCounter, this._cfgCommon.PubSearchPeriod);
1384                 if (!ResetTimers.PublicSearch) GetTimeline(MyCommon.WORKERTYPE.PublicSearch, 1, "");
1385                 ResetTimers.PublicSearch = false;
1386             }
1387             if (ResetTimers.UserTimeline || userTimelineCounter <= 0 && this._cfgCommon.UserTimelinePeriod > 0)
1388             {
1389                 Interlocked.Exchange(ref userTimelineCounter, this._cfgCommon.UserTimelinePeriod);
1390                 if (!ResetTimers.UserTimeline) GetTimeline(MyCommon.WORKERTYPE.UserTimeline, 1, "");
1391                 ResetTimers.UserTimeline = false;
1392             }
1393             if (ResetTimers.Lists || listsCounter <= 0 && this._cfgCommon.ListsPeriod > 0)
1394             {
1395                 Interlocked.Exchange(ref listsCounter, this._cfgCommon.ListsPeriod);
1396                 if (!ResetTimers.Lists) GetTimeline(MyCommon.WORKERTYPE.List, 1, "");
1397                 ResetTimers.Lists = false;
1398             }
1399             if (ResetTimers.UserStream || usCounter <= 0 && this._cfgCommon.UserstreamPeriod > 0)
1400             {
1401                 Interlocked.Exchange(ref usCounter, this._cfgCommon.UserstreamPeriod);
1402                 if (this._isActiveUserstream) RefreshTimeline(true);
1403                 ResetTimers.UserStream = false;
1404             }
1405             if (refreshFollowers > 6 * 3600)
1406             {
1407                 Interlocked.Exchange(ref refreshFollowers, 0);
1408                 doGetFollowersMenu();
1409                 GetTimeline(MyCommon.WORKERTYPE.NoRetweetIds, 0, "");
1410                 GetTimeline(MyCommon.WORKERTYPE.Configuration, 0, "");
1411             }
1412             if (osResumed)
1413             {
1414                 Interlocked.Increment(ref ResumeWait);
1415                 if (ResumeWait > 30)
1416                 {
1417                     osResumed = false;
1418                     Interlocked.Exchange(ref ResumeWait, 0);
1419                     GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
1420                     GetTimeline(MyCommon.WORKERTYPE.Reply, 1, "");
1421                     GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
1422                     GetTimeline(MyCommon.WORKERTYPE.PublicSearch, 1, "");
1423                     GetTimeline(MyCommon.WORKERTYPE.UserTimeline, 1, "");
1424                     GetTimeline(MyCommon.WORKERTYPE.List, 1, "");
1425                     doGetFollowersMenu();
1426                     GetTimeline(MyCommon.WORKERTYPE.Configuration, 0, "");
1427                 }
1428             }
1429         }
1430
1431         private void RefreshTimeline(bool isUserStream)
1432         {
1433             if (isUserStream) this.RefreshTasktrayIcon(true);
1434             //スクロール制御準備
1435             int smode = -1;    //-1:制御しない,-2:最新へ,その他:topitem使用
1436             long topId = GetScrollPos(ref smode);
1437             int befCnt = _curList.VirtualListSize;
1438
1439             //現在の選択状態を退避
1440             Dictionary<string, long[]> selId = new Dictionary<string, long[]>();
1441             Dictionary<string, long[]> focusedId = new Dictionary<string, long[]>();
1442             SaveSelectedStatus(selId, focusedId);
1443
1444             //mentionsの更新前件数を保持
1445             int dmCount = _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage).AllCount;
1446
1447             //更新確定
1448             PostClass[] notifyPosts = null;
1449             string soundFile = "";
1450             int addCount = 0;
1451             bool isMention = false;
1452             bool isDelete = false;
1453             addCount = _statuses.SubmitUpdate(ref soundFile, ref notifyPosts, ref isMention, ref isDelete, isUserStream);
1454
1455             if (MyCommon._endingFlag) return;
1456
1457             //リストに反映&選択状態復元
1458             try
1459             {
1460                 foreach (TabPage tab in ListTab.TabPages)
1461                 {
1462                     DetailsListView lst = (DetailsListView)tab.Tag;
1463                     TabClass tabInfo = _statuses.Tabs[tab.Text];
1464                     using (ControlTransaction.Update(lst))
1465                     {
1466                         if (isDelete || lst.VirtualListSize != tabInfo.AllCount)
1467                         {
1468                             if (lst.Equals(_curList))
1469                             {
1470                                 this.PurgeListViewItemCache();
1471                             }
1472                             try
1473                             {
1474                                 lst.VirtualListSize = tabInfo.AllCount; //リスト件数更新
1475                             }
1476                             catch (Exception)
1477                             {
1478                                 //アイコン描画不具合あり?
1479                             }
1480                             this.SelectListItem(lst,
1481                                                 tabInfo.IndexOf(selId[tab.Text]),
1482                                                 tabInfo.IndexOf(focusedId[tab.Text]));
1483                         }
1484                     }
1485                     if (tabInfo.UnreadCount > 0)
1486                         if (SettingDialog.TabIconDisp)
1487                             if (tab.ImageIndex == -1) tab.ImageIndex = 0; //タブアイコン
1488                 }
1489                 if (!SettingDialog.TabIconDisp) ListTab.Refresh();
1490             }
1491             catch (Exception)
1492             {
1493                 //ex.Data["Msg"] = "Ref1, UseAPI=" + SettingDialog.UseAPI.ToString();
1494                 //throw;
1495             }
1496
1497             //スクロール制御後処理
1498             if (smode != -1)
1499             {
1500                 try
1501                 {
1502                     if (befCnt != _curList.VirtualListSize)
1503                     {
1504                         switch (smode)
1505                         {
1506                             case -3:
1507                                 //最上行
1508                                 if (_curList.VirtualListSize > 0) _curList.EnsureVisible(0);
1509                                 break;
1510                             case -2:
1511                                 //最下行へ
1512                                 if (_curList.VirtualListSize > 0) _curList.EnsureVisible(_curList.VirtualListSize - 1);
1513                                 break;
1514                             case -1:
1515                                 //制御しない
1516                                 break;
1517                             default:
1518                                 //表示位置キープ
1519                                 if (_curList.VirtualListSize > 0 && _statuses.IndexOf(_curTab.Text, topId) > -1)
1520                                 {
1521                                     _curList.EnsureVisible(_curList.VirtualListSize - 1);
1522                                     _curList.EnsureVisible(_statuses.IndexOf(_curTab.Text, topId));
1523                                 }
1524                                 break;
1525                         }
1526                     }
1527                 }
1528                 catch (Exception ex)
1529                 {
1530                     ex.Data["Msg"] = "Ref2";
1531                     throw;
1532                 }
1533             }
1534
1535             //新着通知
1536             NotifyNewPosts(notifyPosts,
1537                            soundFile,
1538                            addCount,
1539                            isMention || dmCount != _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage).AllCount);
1540
1541             SetMainWindowTitle();
1542             if (!StatusLabelUrl.Text.StartsWith("http")) SetStatusLabelUrl();
1543
1544             HashSupl.AddRangeItem(tw.GetHashList());
1545
1546         }
1547
1548         private long GetScrollPos(ref int smode)
1549         {
1550             long topId = -1;
1551             if (_curList != null && _curTab != null && _curList.VirtualListSize > 0)
1552             {
1553                 if (_statuses.SortMode == IdComparerClass.ComparerMode.Id)
1554                 {
1555                     if (_statuses.SortOrder == SortOrder.Ascending)
1556                     {
1557                         //Id昇順
1558                         if (ListLockMenuItem.Checked)
1559                         {
1560                             //制御しない
1561                             smode = -1;
1562                             ////現在表示位置へ強制スクロール
1563                             //if (_curList.TopItem != null) topId = _statuses.GetId(_curTab.Text, _curList.TopItem.Index);
1564                             //smode = 0;
1565                         }
1566                         else
1567                         {
1568                             //最下行が表示されていたら、最下行へ強制スクロール。最下行が表示されていなかったら制御しない
1569                             ListViewItem _item;
1570                             _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1);   //一番下
1571                             if (_item == null) _item = _curList.Items[_curList.VirtualListSize - 1];
1572                             if (_item.Index == _curList.VirtualListSize - 1)
1573                             {
1574                                 smode = -2;
1575                             }
1576                             else
1577                             {
1578                                 smode = -1;
1579                                 //if (_curList.TopItem != null) topId = _statuses.GetId(_curTab.Text, _curList.TopItem.Index);
1580                                 //smode = 0;
1581                             }
1582                         }
1583                     }
1584                     else
1585                     {
1586                         //Id降順
1587                         if (ListLockMenuItem.Checked)
1588                         {
1589                             //現在表示位置へ強制スクロール
1590                             if (_curList.TopItem != null) topId = _statuses.GetId(_curTab.Text, _curList.TopItem.Index);
1591                             smode = 0;
1592                         }
1593                         else
1594                         {
1595                             //最上行が表示されていたら、制御しない。最上行が表示されていなかったら、現在表示位置へ強制スクロール
1596                             ListViewItem _item;
1597
1598                             _item = _curList.GetItemAt(0, 10);     //一番上
1599                             if (_item == null) _item = _curList.Items[0];
1600                             if (_item.Index == 0)
1601                             {
1602                                 smode = -3;  //最上行
1603                             }
1604                             else
1605                             {
1606                                 if (_curList.TopItem != null) topId = _statuses.GetId(_curTab.Text, _curList.TopItem.Index);
1607                                 smode = 0;
1608                             }
1609                         }
1610                     }
1611                 }
1612                 else
1613                 {
1614                     //現在表示位置へ強制スクロール
1615                     if (_curList.TopItem != null) topId = _statuses.GetId(_curTab.Text, _curList.TopItem.Index);
1616                     smode = 0;
1617                 }
1618             }
1619             else
1620             {
1621                 smode = -1;
1622             }
1623             return topId;
1624         }
1625
1626         private void SaveSelectedStatus(Dictionary<string, long[]> selId, Dictionary<string, long[]> focusedId)
1627         {
1628             if (MyCommon._endingFlag) return;
1629             foreach (TabPage tab in ListTab.TabPages)
1630             {
1631                 var lst = (DetailsListView)tab.Tag;
1632                 var tabInfo = _statuses.Tabs[tab.Text];
1633                 if (lst.SelectedIndices.Count > 0 && lst.SelectedIndices.Count < 61)
1634                 {
1635                     selId.Add(tab.Text, tabInfo.GetId(lst.SelectedIndices));
1636                 }
1637                 else
1638                 {
1639                     selId.Add(tab.Text, new long[1] {-2});
1640                 }
1641
1642                 var fIds = new long[2];  // 0 = focus, 1 = selection mark
1643                 var item = lst.FocusedItem;
1644                 fIds[0] = (item != null) ? tabInfo.GetId(item.Index) : -2;
1645                 var mIdx = lst.SelectionMark;
1646                 fIds[1] = (mIdx > -1) ? tabInfo.GetId(mIdx) : -2;
1647                 focusedId.Add(tab.Text, fIds);
1648             }
1649
1650         }
1651
1652         private bool BalloonRequired()
1653         {
1654             Twitter.FormattedEvent ev = new Twitter.FormattedEvent();
1655             ev.Eventtype = MyCommon.EVENTTYPE.None;
1656
1657             return BalloonRequired(ev);
1658         }
1659
1660         private bool IsEventNotifyAsEventType(MyCommon.EVENTTYPE type)
1661         {
1662             return SettingDialog.EventNotifyEnabled && (type & SettingDialog.EventNotifyFlag) != 0 || type == MyCommon.EVENTTYPE.None;
1663         }
1664
1665         private bool IsMyEventNotityAsEventType(Twitter.FormattedEvent ev)
1666         {
1667             return (ev.Eventtype & SettingDialog.IsMyEventNotifyFlag) != 0 ? true : !ev.IsMe;
1668         }
1669
1670         private bool BalloonRequired(Twitter.FormattedEvent ev)
1671         {
1672             if ((
1673                 IsEventNotifyAsEventType(ev.Eventtype) && IsMyEventNotityAsEventType(ev) &&
1674                 (NewPostPopMenuItem.Checked || (SettingDialog.ForceEventNotify && ev.Eventtype != MyCommon.EVENTTYPE.None)) &&
1675                 !_initial &&
1676                 (
1677                     (
1678                         SettingDialog.LimitBalloon &&
1679                         (
1680                             this.WindowState == FormWindowState.Minimized ||
1681                             !this.Visible ||
1682                             Form.ActiveForm == null
1683                             )
1684                         ) ||
1685                     !SettingDialog.LimitBalloon
1686                     )
1687                 ) &&
1688                 !NativeMethods.IsScreenSaverRunning())
1689             {
1690                 return true;
1691             }
1692             else
1693             {
1694                 return false;
1695             }
1696         }
1697
1698         private void NotifyNewPosts(PostClass[] notifyPosts, string soundFile, int addCount, bool newMentions)
1699         {
1700             if (notifyPosts != null &&
1701                 notifyPosts.Length > 0 &&
1702                 this.SettingDialog.ReadOwnPost &&
1703                 notifyPosts.All((post) => { return post.UserId == tw.UserId || post.ScreenName == tw.Username; }))
1704             {
1705                 return;
1706             }
1707
1708             //新着通知
1709             if (BalloonRequired())
1710             {
1711                 if (notifyPosts != null && notifyPosts.Length > 0)
1712                 {
1713                     //Growlは一個ずつばらして通知。ただし、3ポスト以上あるときはまとめる
1714                     if (SettingDialog.IsNotifyUseGrowl)
1715                     {
1716                         StringBuilder sb = new StringBuilder();
1717                         bool reply = false;
1718                         bool dm = false;
1719
1720                         foreach (PostClass post in notifyPosts)
1721                         {
1722                             if (!(notifyPosts.Length > 3))
1723                             {
1724                                 sb.Clear();
1725                                 reply = false;
1726                                 dm = false;
1727                             }
1728                             if (post.IsReply && !post.IsExcludeReply) reply = true;
1729                             if (post.IsDm) dm = true;
1730                             if (sb.Length > 0) sb.Append(System.Environment.NewLine);
1731                             switch (SettingDialog.NameBalloon)
1732                             {
1733                                 case MyCommon.NameBalloonEnum.UserID:
1734                                     sb.Append(post.ScreenName).Append(" : ");
1735                                     break;
1736                                 case MyCommon.NameBalloonEnum.NickName:
1737                                     sb.Append(post.Nickname).Append(" : ");
1738                                     break;
1739                             }
1740                             sb.Append(post.TextFromApi);
1741                             if (notifyPosts.Length > 3)
1742                             {
1743                                 if (notifyPosts.Last() != post) continue;
1744                             }
1745
1746                             StringBuilder title = new StringBuilder();
1747                             GrowlHelper.NotifyType nt;
1748                             if (SettingDialog.DispUsername)
1749                             {
1750                                 title.Append(tw.Username);
1751                                 title.Append(" - ");
1752                             }
1753                             else
1754                             {
1755                                 //title.Clear();
1756                             }
1757                             if (dm)
1758                             {
1759                                 //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
1760                                 //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [DM] " + Properties.Resources.RefreshDirectMessageText1 + " " + addCount.ToString() + Properties.Resources.RefreshDirectMessageText2;
1761                                 title.Append(Application.ProductName);
1762                                 title.Append(" [DM] ");
1763                                 title.Append(Properties.Resources.RefreshDirectMessageText1);
1764                                 title.Append(" ");
1765                                 title.Append(addCount);
1766                                 title.Append(Properties.Resources.RefreshDirectMessageText2);
1767                                 nt = GrowlHelper.NotifyType.DirectMessage;
1768                             }
1769                             else if (reply)
1770                             {
1771                                 //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
1772                                 //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [Reply!] " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2;
1773                                 title.Append(Application.ProductName);
1774                                 title.Append(" [Reply!] ");
1775                                 title.Append(Properties.Resources.RefreshTimelineText1);
1776                                 title.Append(" ");
1777                                 title.Append(addCount);
1778                                 title.Append(Properties.Resources.RefreshTimelineText2);
1779                                 nt = GrowlHelper.NotifyType.Reply;
1780                             }
1781                             else
1782                             {
1783                                 //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
1784                                 //NotifyIcon1.BalloonTipTitle += Application.ProductName + " " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2;
1785                                 title.Append(Application.ProductName);
1786                                 title.Append(" ");
1787                                 title.Append(Properties.Resources.RefreshTimelineText1);
1788                                 title.Append(" ");
1789                                 title.Append(addCount);
1790                                 title.Append(Properties.Resources.RefreshTimelineText2);
1791                                 nt = GrowlHelper.NotifyType.Notify;
1792                             }
1793                             string bText = sb.ToString();
1794                             if (string.IsNullOrEmpty(bText)) return;
1795
1796                             var image = this.IconCache.TryGetFromCache(post.ImageUrl);
1797                             gh.Notify(nt, post.StatusId.ToString(), title.ToString(), bText, image == null ? null : image.Image, post.ImageUrl);
1798                         }
1799                     }
1800                     else
1801                     {
1802                         StringBuilder sb = new StringBuilder();
1803                         bool reply = false;
1804                         bool dm = false;
1805                         foreach (PostClass post in notifyPosts)
1806                         {
1807                             if (post.IsReply && !post.IsExcludeReply) reply = true;
1808                             if (post.IsDm) dm = true;
1809                             if (sb.Length > 0) sb.Append(System.Environment.NewLine);
1810                             switch (SettingDialog.NameBalloon)
1811                             {
1812                                 case MyCommon.NameBalloonEnum.UserID:
1813                                     sb.Append(post.ScreenName).Append(" : ");
1814                                     break;
1815                                 case MyCommon.NameBalloonEnum.NickName:
1816                                     sb.Append(post.Nickname).Append(" : ");
1817                                     break;
1818                             }
1819                             sb.Append(post.TextFromApi);
1820
1821                         }
1822                         //if (SettingDialog.DispUsername) { NotifyIcon1.BalloonTipTitle = tw.Username + " - "; } else { NotifyIcon1.BalloonTipTitle = ""; }
1823                         StringBuilder title = new StringBuilder();
1824                         ToolTipIcon ntIcon;
1825                         if (SettingDialog.DispUsername)
1826                         {
1827                             title.Append(tw.Username);
1828                             title.Append(" - ");
1829                         }
1830                         else
1831                         {
1832                             //title.Clear();
1833                         }
1834                         if (dm)
1835                         {
1836                             //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
1837                             //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [DM] " + Properties.Resources.RefreshDirectMessageText1 + " " + addCount.ToString() + Properties.Resources.RefreshDirectMessageText2;
1838                             ntIcon = ToolTipIcon.Warning;
1839                             title.Append(Application.ProductName);
1840                             title.Append(" [DM] ");
1841                             title.Append(Properties.Resources.RefreshDirectMessageText1);
1842                             title.Append(" ");
1843                             title.Append(addCount);
1844                             title.Append(Properties.Resources.RefreshDirectMessageText2);
1845                         }
1846                         else if (reply)
1847                         {
1848                             //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
1849                             //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [Reply!] " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2;
1850                             ntIcon = ToolTipIcon.Warning;
1851                             title.Append(Application.ProductName);
1852                             title.Append(" [Reply!] ");
1853                             title.Append(Properties.Resources.RefreshTimelineText1);
1854                             title.Append(" ");
1855                             title.Append(addCount);
1856                             title.Append(Properties.Resources.RefreshTimelineText2);
1857                         }
1858                         else
1859                         {
1860                             //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
1861                             //NotifyIcon1.BalloonTipTitle += Application.ProductName + " " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2;
1862                             ntIcon = ToolTipIcon.Info;
1863                             title.Append(Application.ProductName);
1864                             title.Append(" ");
1865                             title.Append(Properties.Resources.RefreshTimelineText1);
1866                             title.Append(" ");
1867                             title.Append(addCount);
1868                             title.Append(Properties.Resources.RefreshTimelineText2);
1869                         }
1870                         string bText = sb.ToString();
1871                         if (string.IsNullOrEmpty(bText)) return;
1872                         //NotifyIcon1.BalloonTipText = sb.ToString();
1873                         //NotifyIcon1.ShowBalloonTip(500);
1874                         NotifyIcon1.BalloonTipTitle = title.ToString();
1875                         NotifyIcon1.BalloonTipText = bText;
1876                         NotifyIcon1.BalloonTipIcon = ntIcon;
1877                         NotifyIcon1.ShowBalloonTip(500);
1878                     }
1879                 }
1880             }
1881
1882             //サウンド再生
1883             if (!_initial && SettingDialog.PlaySound && !string.IsNullOrEmpty(soundFile))
1884             {
1885                 try
1886                 {
1887                     string dir = Application.StartupPath;
1888                     if (Directory.Exists(Path.Combine(dir, "Sounds")))
1889                     {
1890                         dir = Path.Combine(dir, "Sounds");
1891                     }
1892                     using (SoundPlayer player = new SoundPlayer(Path.Combine(dir, soundFile)))
1893                     {
1894                         player.Play();
1895                     }
1896                 }
1897                 catch (Exception)
1898                 {
1899                 }
1900             }
1901
1902             //mentions新着時に画面ブリンク
1903             if (!_initial && SettingDialog.BlinkNewMentions && newMentions && Form.ActiveForm == null)
1904             {
1905                 NativeMethods.FlashMyWindow(this.Handle, NativeMethods.FlashSpecification.FlashTray, 3);
1906             }
1907         }
1908
1909         private void MyList_SelectedIndexChanged(object sender, EventArgs e)
1910         {
1911             if (_curList == null || !_curList.Equals(sender) || _curList.SelectedIndices.Count != 1) return;
1912
1913             _curItemIndex = _curList.SelectedIndices[0];
1914             if (_curItemIndex > _curList.VirtualListSize - 1) return;
1915
1916             try
1917             {
1918                 _curPost = GetCurTabPost(_curItemIndex);
1919             }
1920             catch (ArgumentException)
1921             {
1922                 return;
1923             }
1924
1925             this.PushSelectPostChain();
1926
1927             if (SettingDialog.UnreadManage) _statuses.SetReadAllTab(true, _curTab.Text, _curItemIndex);
1928             //キャッシュの書き換え
1929             ChangeCacheStyleRead(true, _curItemIndex);   //既読へ(フォント、文字色)
1930
1931             ColorizeList();
1932             _colorize = true;
1933         }
1934
1935         private void ChangeCacheStyleRead(bool Read, int Index)
1936         {
1937             var tabInfo = _statuses.Tabs[_curTab.Text];
1938             //Read:true=既読 false=未読
1939             //未読管理していなかったら既読として扱う
1940             if (!tabInfo.UnreadManage ||
1941                !SettingDialog.UnreadManage) Read = true;
1942
1943             //対象の特定
1944             ListViewItem itm = null;
1945             PostClass post = null;
1946
1947             this.TryGetListViewItemCache(Index, out itm, out post);
1948
1949             if (itm == null || post == null)
1950             {
1951                 itm = ((DetailsListView)_curTab.Tag).Items[Index];
1952                 post = tabInfo[Index];
1953             }
1954
1955             ChangeItemStyleRead(Read, itm, post, ((DetailsListView)_curTab.Tag));
1956         }
1957
1958         private void ChangeItemStyleRead(bool Read, ListViewItem Item, PostClass Post, DetailsListView DList)
1959         {
1960             Font fnt;
1961             //フォント
1962             if (Read)
1963             {
1964                 fnt = _fntReaded;
1965                 Item.SubItems[5].Text = "";
1966             }
1967             else
1968             {
1969                 fnt = _fntUnread;
1970                 Item.SubItems[5].Text = "★";
1971             }
1972             //文字色
1973             Color cl;
1974             if (Post.IsFav)
1975                 cl = _clFav;
1976             else if (Post.RetweetedId != null)
1977                 cl = _clRetweet;
1978             else if (Post.IsOwl && (Post.IsDm || this._cfgCommon.OneWayLove))
1979                 cl = _clOWL;
1980             else if (Read || !this._cfgCommon.UseUnreadStyle)
1981                 cl = _clReaded;
1982             else
1983                 cl = _clUnread;
1984
1985             if (DList == null || Item.Index == -1)
1986             {
1987                 Item.ForeColor = cl;
1988                 if (this._cfgCommon.UseUnreadStyle)
1989                     Item.Font = fnt;
1990             }
1991             else
1992             {
1993                 DList.Update();
1994                 if (this._cfgCommon.UseUnreadStyle)
1995                     DList.ChangeItemFontAndColor(Item.Index, cl, fnt);
1996                 else
1997                     DList.ChangeItemForeColor(Item.Index, cl);
1998                 //if (_itemCache != null) DList.RedrawItems(_itemCacheIndex, _itemCacheIndex + _itemCache.Length - 1, false);
1999             }
2000         }
2001
2002         private void ColorizeList()
2003         {
2004             //Index:更新対象のListviewItem.Index。Colorを返す。
2005             //-1は全キャッシュ。Colorは返さない(ダミーを戻す)
2006             PostClass _post;
2007             if (_anchorFlag)
2008                 _post = _anchorPost;
2009             else
2010                 _post = _curPost;
2011
2012             if (_post == null) return;
2013
2014             var itemColors = new Color[] { };
2015             int itemIndex = -1;
2016
2017             this.itemCacheLock.EnterReadLock();
2018             try
2019             {
2020                 if (this._itemCache == null) return;
2021
2022                 var query = 
2023                     from i in Enumerable.Range(0, this._itemCache.Length)
2024                     select this.JudgeColor(_post, this._postCache[i]);
2025                 
2026                 itemColors = query.ToArray();
2027                 itemIndex = _itemCacheIndex;
2028             }
2029             finally { this.itemCacheLock.ExitReadLock(); }
2030
2031             if (itemIndex < 0) return;
2032
2033             foreach (var backColor in itemColors)
2034             {
2035                 // この処理中に MyList_CacheVirtualItems が呼ばれることがあるため、
2036                 // 同一スレッド内での二重ロックを避けるためにロックの外で実行する必要がある
2037                 _curList.ChangeItemBackColor(itemIndex++, backColor);
2038             }
2039         }
2040
2041         private void ColorizeList(ListViewItem Item, int Index)
2042         {
2043             //Index:更新対象のListviewItem.Index。Colorを返す。
2044             //-1は全キャッシュ。Colorは返さない(ダミーを戻す)
2045             PostClass _post;
2046             if (_anchorFlag)
2047                 _post = _anchorPost;
2048             else
2049                 _post = _curPost;
2050
2051             PostClass tPost = GetCurTabPost(Index);
2052
2053             if (_post == null) return;
2054
2055             if (Item.Index == -1)
2056                 Item.BackColor = JudgeColor(_post, tPost);
2057             else
2058                 _curList.ChangeItemBackColor(Item.Index, JudgeColor(_post, tPost));
2059         }
2060
2061         private Color JudgeColor(PostClass BasePost, PostClass TargetPost)
2062         {
2063             Color cl;
2064             if (TargetPost.StatusId == BasePost.InReplyToStatusId)
2065                 //@先
2066                 cl = _clAtTo;
2067             else if (TargetPost.IsMe)
2068                 //自分=発言者
2069                 cl = _clSelf;
2070             else if (TargetPost.IsReply)
2071                 //自分宛返信
2072                 cl = _clAtSelf;
2073             else if (BasePost.ReplyToList.Contains(TargetPost.ScreenName.ToLower()))
2074                 //返信先
2075                 cl = _clAtFromTarget;
2076             else if (TargetPost.ReplyToList.Contains(BasePost.ScreenName.ToLower()))
2077                 //その人への返信
2078                 cl = _clAtTarget;
2079             else if (TargetPost.ScreenName.Equals(BasePost.ScreenName, StringComparison.OrdinalIgnoreCase))
2080                 //発言者
2081                 cl = _clTarget;
2082             else
2083                 //その他
2084                 cl = _clListBackcolor;
2085
2086             return cl;
2087         }
2088
2089         private async void PostButton_Click(object sender, EventArgs e)
2090         {
2091             if (StatusText.Text.Trim().Length == 0)
2092             {
2093                 if (!ImageSelector.Enabled)
2094                 {
2095                     DoRefresh();
2096                     return;
2097                 }
2098             }
2099
2100             if (this.ExistCurrentPost && StatusText.Text.Trim() == string.Format("RT @{0}: {1}", _curPost.ScreenName, _curPost.TextFromApi))
2101             {
2102                 DialogResult rtResult = MessageBox.Show(string.Format(Properties.Resources.PostButton_Click1, Environment.NewLine),
2103                                                                "Retweet",
2104                                                                MessageBoxButtons.YesNoCancel,
2105                                                                MessageBoxIcon.Question);
2106                 switch (rtResult)
2107                 {
2108                     case DialogResult.Yes:
2109                         doReTweetOfficial(false);
2110                         StatusText.Text = "";
2111                         return;
2112                     case DialogResult.Cancel:
2113                         return;
2114                 }
2115             }
2116
2117             _history[_history.Count - 1] = new PostingStatus(StatusText.Text, _reply_to_id, _reply_to_name);
2118
2119             if (SettingDialog.Nicoms)
2120             {
2121                 StatusText.SelectionStart = StatusText.Text.Length;
2122                 await UrlConvertAsync(MyCommon.UrlConverter.Nicoms);
2123             }
2124             //if (SettingDialog.UrlConvertAuto)
2125             //{
2126             //    StatusText.SelectionStart = StatusText.Text.Length;
2127             //    UrlConvertAutoToolStripMenuItem_Click(null, null);
2128             //}
2129             //else if (SettingDialog.Nicoms)
2130             //{
2131             //    StatusText.SelectionStart = StatusText.Text.Length;
2132             //    UrlConvert(UrlConverter.Nicoms);
2133             //}
2134             StatusText.SelectionStart = StatusText.Text.Length;
2135             GetWorkerArg args = new GetWorkerArg();
2136             args.page = 0;
2137             args.type = MyCommon.WORKERTYPE.PostMessage;
2138             CheckReplyTo(StatusText.Text);
2139
2140             //整形によって増加する文字数を取得
2141             int adjustCount = 0;
2142             string tmpStatus = StatusText.Text.Trim();
2143             if (ToolStripMenuItemApiCommandEvasion.Checked)
2144             {
2145                 // APIコマンド回避
2146                 if (Regex.IsMatch(tmpStatus,
2147                     @"^[+\-\[\]\s\\.,*/(){}^~|='&%$#""<>?]*(get|g|fav|follow|f|on|off|stop|quit|leave|l|whois|w|nudge|n|stats|invite|track|untrack|tracks|tracking|\*)([+\-\[\]\s\\.,*/(){}^~|='&%$#""<>?]+|$)",
2148                     RegexOptions.IgnoreCase)
2149                    && tmpStatus.EndsWith(" .") == false) adjustCount += 2;
2150             }
2151
2152             if (ToolStripMenuItemUrlMultibyteSplit.Checked)
2153             {
2154                 // URLと全角文字の切り離し
2155                 adjustCount += Regex.Matches(tmpStatus, @"https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#^]+").Count;
2156             }
2157
2158             bool isCutOff = false;
2159             bool isRemoveFooter = MyCommon.IsKeyDown(Keys.Shift);
2160             if (StatusText.Multiline && !this._cfgCommon.PostCtrlEnter)
2161             {
2162                 //複数行でEnter投稿の場合、Ctrlも押されていたらフッタ付加しない
2163                 isRemoveFooter = MyCommon.IsKeyDown(Keys.Control);
2164             }
2165             if (this._cfgCommon.PostShiftEnter)
2166             {
2167                 isRemoveFooter = MyCommon.IsKeyDown(Keys.Control);
2168             }
2169             if (!isRemoveFooter && (StatusText.Text.Contains("RT @") || StatusText.Text.Contains("QT @")))
2170             {
2171                 isRemoveFooter = true;
2172             }
2173             if (GetRestStatusCount(false, !isRemoveFooter) - adjustCount < 0)
2174             {
2175                 if (MessageBox.Show(Properties.Resources.PostLengthOverMessage1, Properties.Resources.PostLengthOverMessage2, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK)
2176                 {
2177                     isCutOff = true;
2178                     //if (!SettingDialog.UrlConvertAuto) UrlConvertAutoToolStripMenuItem_Click(null, null);
2179                     if (GetRestStatusCount(false, !isRemoveFooter) - adjustCount < 0)
2180                     {
2181                         isRemoveFooter = true;
2182                     }
2183                 }
2184                 else
2185                 {
2186                     return;
2187                 }
2188             }
2189
2190             string footer = "";
2191             string header = "";
2192             if (StatusText.Text.StartsWith("D ") || StatusText.Text.StartsWith("d "))
2193             {
2194                 //DM時は何もつけない
2195                 footer = "";
2196             }
2197             else
2198             {
2199                 //ハッシュタグ
2200                 if (HashMgr.IsNotAddToAtReply)
2201                 {
2202                     if (!string.IsNullOrEmpty(HashMgr.UseHash) && _reply_to_id == null && string.IsNullOrEmpty(_reply_to_name))
2203                     {
2204                         if (HashMgr.IsHead)
2205                             header = HashMgr.UseHash + " ";
2206                         else
2207                             footer = " " + HashMgr.UseHash;
2208                     }
2209                 }
2210                 else
2211                 {
2212                     if (!string.IsNullOrEmpty(HashMgr.UseHash))
2213                     {
2214                         if (HashMgr.IsHead)
2215                             header = HashMgr.UseHash + " ";
2216                         else
2217                             footer = " " + HashMgr.UseHash;
2218                     }
2219                 }
2220                 if (!isRemoveFooter)
2221                 {
2222                     if (this._cfgLocal.UseRecommendStatus)
2223                         // 推奨ステータスを使用する
2224                         footer += SettingDialog.RecommendStatusText;
2225                     else
2226                         // テキストボックスに入力されている文字列を使用する
2227                         footer += " " + this._cfgLocal.StatusText.Trim();
2228                 }
2229             }
2230             args.status.status = header + StatusText.Text + footer;
2231
2232             if (ToolStripMenuItemApiCommandEvasion.Checked)
2233             {
2234                 // APIコマンド回避
2235                 if (Regex.IsMatch(args.status.status,
2236                     @"^[+\-\[\]\s\\.,*/(){}^~|='&%$#""<>?]*(get|g|fav|follow|f|on|off|stop|quit|leave|l|whois|w|nudge|n|stats|invite|track|untrack|tracks|tracking|\*)([+\-\[\]\s\\.,*/(){}^~|='&%$#""<>?]+|$)",
2237                     RegexOptions.IgnoreCase)
2238                    && args.status.status.EndsWith(" .") == false) args.status.status += " .";
2239             }
2240
2241             if (ToolStripMenuItemUrlMultibyteSplit.Checked)
2242             {
2243                 // URLと全角文字の切り離し
2244                 Match mc2 = Regex.Match(args.status.status, @"https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#^]+");
2245                 if (mc2.Success) args.status.status = Regex.Replace(args.status.status, @"https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#^]+", "$& ");
2246             }
2247
2248             if (IdeographicSpaceToSpaceToolStripMenuItem.Checked)
2249             {
2250                 // 文中の全角スペースを半角スペース1個にする
2251                 args.status.status = args.status.status.Replace(" ", " ");
2252             }
2253
2254             if (isCutOff && args.status.status.Length > 140)
2255             {
2256                 args.status.status = args.status.status.Substring(0, 140);
2257                 string AtId = @"(@|@)[a-z0-9_/]+$";
2258                 string HashTag = @"(^|[^0-9A-Z&\/\?]+)(#|#)([0-9A-Z_]*[A-Z_]+)$";
2259                 string Url = @"https?:\/\/[a-z0-9!\*'\(\);:&=\+\$\/%#\[\]\-_\.,~?]+$"; //簡易判定
2260                 string pattern = string.Format("({0})|({1})|({2})", AtId, HashTag, Url);
2261                 Match mc = Regex.Match(args.status.status, pattern, RegexOptions.IgnoreCase);
2262                 if (mc.Success)
2263                 {
2264                     //さらに@ID、ハッシュタグ、URLと推測される文字列をカットする
2265                     args.status.status = args.status.status.Substring(0, 140 - mc.Value.Length);
2266                 }
2267                 if (MessageBox.Show(args.status.status, "Post or Cancel?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel) return;
2268             }
2269
2270             args.status.inReplyToId = _reply_to_id;
2271             args.status.inReplyToName = _reply_to_name;
2272             if (ImageSelector.Visible)
2273             {
2274                 //画像投稿
2275                 if (!ImageSelector.TryGetSelectedMedia(out args.status.imageService, out args.status.imagePath))
2276                     return;
2277             }
2278
2279             RunAsync(args);
2280
2281             _reply_to_id = null;
2282             _reply_to_name = null;
2283             StatusText.Text = "";
2284             _history.Add(new PostingStatus());
2285             _hisIdx = _history.Count - 1;
2286             if (!ToolStripFocusLockMenuItem.Checked)
2287                 ((Control)ListTab.SelectedTab.Tag).Focus();
2288             urlUndoBuffer = null;
2289             UrlUndoToolStripMenuItem.Enabled = false;  //Undoをできないように設定
2290
2291             //Google検索(試験実装)
2292             if (StatusText.Text.StartsWith("Google:", StringComparison.OrdinalIgnoreCase) && StatusText.Text.Trim().Length > 7)
2293             {
2294                 string tmp = string.Format(Properties.Resources.SearchItem2Url, Uri.EscapeUriString(StatusText.Text.Substring(7)));
2295                 await this.OpenUriAsync(tmp);
2296             }
2297         }
2298
2299         private void EndToolStripMenuItem_Click(object sender, EventArgs e)
2300         {
2301             MyCommon._endingFlag = true;
2302             this.Close();
2303         }
2304
2305         private void TweenMain_FormClosing(object sender, FormClosingEventArgs e)
2306         {
2307             if (!SettingDialog.CloseToExit && e.CloseReason == CloseReason.UserClosing && MyCommon._endingFlag == false)
2308             {
2309                 //_endingFlag=false:フォームの×ボタン
2310                 e.Cancel = true;
2311                 this.Visible = false;
2312             }
2313             else
2314             {
2315                 _hookGlobalHotkey.UnregisterAllOriginalHotkey();
2316                 _ignoreConfigSave = true;
2317                 MyCommon._endingFlag = true;
2318                 TimerTimeline.Enabled = false;
2319                 TimerRefreshIcon.Enabled = false;
2320             }
2321         }
2322
2323         private void NotifyIcon1_BalloonTipClicked(object sender, EventArgs e)
2324         {
2325             this.Visible = true;
2326             if (this.WindowState == FormWindowState.Minimized)
2327             {
2328                 this.WindowState = FormWindowState.Normal;
2329             }
2330             this.Activate();
2331             this.BringToFront();
2332         }
2333
2334         private static int errorCount = 0;
2335
2336         private static bool CheckAccountValid()
2337         {
2338             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid)
2339             {
2340                 errorCount += 1;
2341                 if (errorCount > 5)
2342                 {
2343                     errorCount = 0;
2344                     Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2345                     return true;
2346                 }
2347                 return false;
2348             }
2349             errorCount = 0;
2350             return true;
2351         }
2352
2353         private void GetTimelineWorker_DoWork(object sender, DoWorkEventArgs e)
2354         {
2355             BackgroundWorker bw = (BackgroundWorker)sender;
2356             if (bw.CancellationPending || MyCommon._endingFlag)
2357             {
2358                 e.Cancel = true;
2359                 return;
2360             }
2361
2362             Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
2363
2364             MyApplication.InitCulture();
2365
2366             string ret = "";
2367             GetWorkerResult rslt = new GetWorkerResult();
2368
2369             bool read = !SettingDialog.UnreadManage;
2370             if (_initial && SettingDialog.UnreadManage) read = this._cfgCommon.Read;
2371
2372             GetWorkerArg args = (GetWorkerArg)e.Argument;
2373
2374             if (!CheckAccountValid())
2375             {
2376                 rslt.retMsg = "Auth error. Check your account";
2377                 rslt.type = MyCommon.WORKERTYPE.ErrorState;  //エラー表示のみ行ない、後処理キャンセル
2378                 rslt.tName = args.tName;
2379                 e.Result = rslt;
2380                 return;
2381             }
2382
2383             bw.ReportProgress(0, ""); //Notifyアイコンアニメーション開始
2384
2385             switch (args.type)
2386             {
2387                 case MyCommon.WORKERTYPE.Timeline:
2388                 case MyCommon.WORKERTYPE.Reply:
2389                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2390                     ret = tw.GetTimelineApi(read, args.type, args.page == -1, _initial);
2391                     //新着時未読クリア
2392                     if (string.IsNullOrEmpty(ret) && args.type == MyCommon.WORKERTYPE.Timeline && SettingDialog.ReadOldPosts)
2393                         _statuses.SetRead();
2394                     //振り分け
2395                     rslt.addCount = _statuses.DistributePosts();
2396                     break;
2397                 case MyCommon.WORKERTYPE.DirectMessegeRcv:    //送信分もまとめて取得
2398                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2399                     ret = tw.GetDirectMessageApi(read, MyCommon.WORKERTYPE.DirectMessegeRcv, args.page == -1);
2400                     if (string.IsNullOrEmpty(ret)) ret = tw.GetDirectMessageApi(read, MyCommon.WORKERTYPE.DirectMessegeSnt, args.page == -1);
2401                     rslt.addCount = _statuses.DistributePosts();
2402                     break;
2403
2404                 case MyCommon.WORKERTYPE.FavAdd:
2405                 {
2406                     //スレッド処理はしない
2407                     TabClass tab;
2408                     if (_statuses.Tabs.TryGetValue(args.tName, out tab))
2409                     {
2410                         for (int i = 0; i <= args.ids.Count - 1; i++)
2411                         {
2412                             var post = tab.Posts[args.ids[i]];
2413
2414                             args.page = i + 1;
2415                             bw.ReportProgress(50, MakeStatusMessage(args, false));
2416                             if (!post.IsFav)
2417                             {
2418                                 if (post.RetweetedId == null)
2419                                     ret = tw.PostFavAdd(post.StatusId);
2420                                 else
2421                                     ret = tw.PostFavAdd(post.RetweetedId.Value);
2422
2423                                 if (ret.Length == 0)
2424                                 {
2425                                     args.sIds.Add(post.StatusId);
2426                                     post.IsFav = true;    //リスト再描画必要
2427                                     _favTimestamps.Add(DateTime.Now);
2428                                     if (string.IsNullOrEmpty(post.RelTabName))
2429                                     {
2430                                         //検索,リストUserTimeline.Relatedタブからのfavは、favタブへ追加せず。それ以外は追加
2431                                         _statuses.GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
2432                                     }
2433                                     else
2434                                     {
2435                                         //検索,リスト,UserTimeline.Relatedタブからのfavで、TLでも取得済みならfav反映
2436                                         if (_statuses.ContainsKey(post.StatusId))
2437                                         {
2438                                             PostClass postTl = _statuses[post.StatusId];
2439                                             postTl.IsFav = true;
2440                                             _statuses.GetTabByType(MyCommon.TabUsageType.Favorites).Add(postTl.StatusId, postTl.IsRead, false);
2441                                         }
2442                                     }
2443                                     //検索,リスト,UserTimeline,Relatedの各タブに反映
2444                                     foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch | MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.UserTimeline | MyCommon.TabUsageType.Related))
2445                                     {
2446                                         if (tb.Contains(post.StatusId)) tb.Posts[post.StatusId].IsFav = true;
2447                                     }
2448                                 }
2449                             }
2450                         }
2451                     }
2452                     rslt.sIds = args.sIds;
2453                     break;
2454                 }
2455
2456                 case MyCommon.WORKERTYPE.FavRemove:
2457                 {
2458                     //スレッド処理はしない
2459                     TabClass tab;
2460                     if (_statuses.Tabs.TryGetValue(args.tName, out tab))
2461                     {
2462                         for (int i = 0; i <= args.ids.Count - 1; i++)
2463                         {
2464                             var post = tab.Posts[args.ids[i]];
2465
2466                             args.page = i + 1;
2467                             bw.ReportProgress(50, MakeStatusMessage(args, false));
2468                             if (post.IsFav)
2469                             {
2470                                 if (post.RetweetedId == null)
2471                                     ret = tw.PostFavRemove(post.StatusId);
2472                                 else
2473                                     ret = tw.PostFavRemove(post.RetweetedId.Value);
2474
2475                                 if (ret.Length == 0)
2476                                 {
2477                                     args.sIds.Add(post.StatusId);
2478                                     post.IsFav = false;    //リスト再描画必要
2479                                     if (_statuses.ContainsKey(post.StatusId)) _statuses[post.StatusId].IsFav = false;
2480                                     //検索,リスト,UserTimeline,Relatedの各タブに反映
2481                                     foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch | MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.UserTimeline | MyCommon.TabUsageType.Related))
2482                                     {
2483                                         if (tb.Contains(post.StatusId)) tb.Posts[post.StatusId].IsFav = false;
2484                                     }
2485                                 }
2486                             }
2487                         }
2488                     }
2489                     rslt.sIds = args.sIds;
2490                     break;
2491                 }
2492
2493                 case MyCommon.WORKERTYPE.PostMessage:
2494                     bw.ReportProgress(200);
2495                     if (args.status.imagePath == null || args.status.imagePath.Length == 0 || string.IsNullOrEmpty(args.status.imagePath[0]))
2496                     {
2497                         ret = tw.PostStatus(args.status.status, args.status.inReplyToId);
2498                     }
2499                     else
2500                     {
2501                         var service = ImageSelector.GetService(args.status.imageService);
2502                         try
2503                         {
2504                             service.PostStatusAsync(args.status.status, args.status.inReplyToId, args.status.imagePath)
2505                                 .Wait();
2506                         }
2507                         catch (AggregateException ex)
2508                         {
2509                             ret = ex.InnerException.Message;
2510                         }
2511                     }
2512                     bw.ReportProgress(300);
2513                     rslt.status = args.status;
2514                     break;
2515                 case MyCommon.WORKERTYPE.Retweet:
2516                     bw.ReportProgress(200);
2517                     for (int i = 0; i <= args.ids.Count - 1; i++)
2518                     {
2519                         ret = tw.PostRetweet(args.ids[i], read);
2520                     }
2521                     bw.ReportProgress(300);
2522                     break;
2523                 case MyCommon.WORKERTYPE.Follower:
2524                     bw.ReportProgress(50, Properties.Resources.UpdateFollowersMenuItem1_ClickText1);
2525                     try
2526                     {
2527                         tw.RefreshFollowerIds();
2528                     }
2529                     catch (WebApiException ex) { ret = ex.Message; }
2530                     break;
2531                 case MyCommon.WORKERTYPE.NoRetweetIds:
2532                     try
2533                     {
2534                         tw.RefreshNoRetweetIds();
2535                     }
2536                     catch (WebApiException ex) { ret = ex.Message; }
2537                     break;
2538                 case MyCommon.WORKERTYPE.Configuration:
2539                     try
2540                     {
2541                         this.SettingDialog.TwitterConfiguration = tw.ConfigurationApi();
2542                     }
2543                     catch (WebApiException ex) { ret = ex.Message; }
2544                     break;
2545                 case MyCommon.WORKERTYPE.Favorites:
2546                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2547                     ret = tw.GetFavoritesApi(read, args.type, args.page == -1);
2548                     rslt.addCount = _statuses.DistributePosts();
2549                     break;
2550                 case MyCommon.WORKERTYPE.PublicSearch:
2551                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2552                     if (string.IsNullOrEmpty(args.tName))
2553                     {
2554                         foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch))
2555                         {
2556                             //if (!string.IsNullOrEmpty(tb.SearchWords)) ret = tw.GetPhoenixSearch(read, tb, false);
2557                             if (!string.IsNullOrEmpty(tb.SearchWords)) ret = tw.GetSearch(read, tb, false);
2558                         }
2559                     }
2560                     else
2561                     {
2562                         TabClass tb = _statuses.GetTabByName(args.tName);
2563                         if (tb != null)
2564                         {
2565                             //ret = tw.GetPhoenixSearch(read, tb, false);
2566                             ret = tw.GetSearch(read, tb, false);
2567                             if (string.IsNullOrEmpty(ret) && args.page == -1)
2568                             {
2569                                 //ret = tw.GetPhoenixSearch(read, tb, true)
2570                                 ret = tw.GetSearch(read, tb, true);
2571                             }
2572                         }
2573                     }
2574                     //振り分け
2575                     rslt.addCount = _statuses.DistributePosts();
2576                     break;
2577                 case MyCommon.WORKERTYPE.UserTimeline:
2578                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2579                     int count = 20;
2580                     if (SettingDialog.UseAdditionalCount) count = SettingDialog.UserTimelineCountApi;
2581                     if (string.IsNullOrEmpty(args.tName))
2582                     {
2583                         foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.UserTimeline))
2584                         {
2585                             if (!string.IsNullOrEmpty(tb.User)) ret = tw.GetUserTimelineApi(read, count, tb.User, tb, false);
2586                         }
2587                     }
2588                     else
2589                     {
2590                         TabClass tb = _statuses.GetTabByName(args.tName);
2591                         if (tb != null)
2592                         {
2593                             ret = tw.GetUserTimelineApi(read, count, tb.User, tb, args.page == -1);
2594                         }
2595                     }
2596                     //振り分け
2597                     rslt.addCount = _statuses.DistributePosts();
2598                     break;
2599                 case MyCommon.WORKERTYPE.List:
2600                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2601                     if (string.IsNullOrEmpty(args.tName))
2602                     {
2603                         //定期更新
2604                         foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.Lists))
2605                         {
2606                             if (tb.ListInfo != null && tb.ListInfo.Id != 0) ret = tw.GetListStatus(read, tb, false, _initial);
2607                         }
2608                     }
2609                     else
2610                     {
2611                         //手動更新(特定タブのみ更新)
2612                         TabClass tb = _statuses.GetTabByName(args.tName);
2613                         if (tb != null)
2614                         {
2615                             ret = tw.GetListStatus(read, tb, args.page == -1, _initial);
2616                         }
2617                     }
2618                     //振り分け
2619                     rslt.addCount = _statuses.DistributePosts();
2620                     break;
2621
2622                 case MyCommon.WORKERTYPE.Related:
2623                 {
2624                     bw.ReportProgress(50, MakeStatusMessage(args, false));
2625                     TabClass tab = _statuses.GetTabByName(args.tName);
2626                     ret = tw.GetRelatedResult(read, tab);
2627                     rslt.addCount = _statuses.DistributePosts();
2628                     break;
2629                 }
2630
2631                 case MyCommon.WORKERTYPE.BlockIds:
2632                     bw.ReportProgress(50, Properties.Resources.UpdateBlockUserText1);
2633                     try
2634                     {
2635                         tw.RefreshBlockIds();
2636                     }
2637                     catch (WebApiException ex) { ret = ex.Message; }
2638                     break;
2639             }
2640             //キャンセル要求
2641             if (bw.CancellationPending)
2642             {
2643                 e.Cancel = true;
2644                 return;
2645             }
2646
2647             //時速表示用
2648             if (args.type == MyCommon.WORKERTYPE.FavAdd)
2649             {
2650                 DateTime oneHour = DateTime.Now.Subtract(new TimeSpan(1, 0, 0));
2651                 for (int i = _favTimestamps.Count - 1; i >= 0; i--)
2652                 {
2653                     if (_favTimestamps[i].CompareTo(oneHour) < 0)
2654                     {
2655                         _favTimestamps.RemoveAt(i);
2656                     }
2657                 }
2658             }
2659             if (args.type == MyCommon.WORKERTYPE.Timeline && !_initial)
2660             {
2661                 lock (_syncObject)
2662                 {
2663                     DateTime tm = DateTime.Now;
2664                     if (_tlTimestamps.ContainsKey(tm))
2665                         _tlTimestamps[tm] += rslt.addCount;
2666                     else
2667                         _tlTimestamps.Add(tm, rslt.addCount);
2668
2669                     DateTime oneHour = DateTime.Now.Subtract(new TimeSpan(1, 0, 0));
2670                     List<DateTime> keys = new List<DateTime>();
2671                     _tlCount = 0;
2672                     foreach (DateTime key in _tlTimestamps.Keys)
2673                     {
2674                         if (key.CompareTo(oneHour) < 0)
2675                         {
2676                             keys.Add(key);
2677                         }
2678                         else
2679                         {
2680                             _tlCount += _tlTimestamps[key];
2681                         }
2682                     }
2683                     foreach (DateTime key in keys)
2684                     {
2685                         _tlTimestamps.Remove(key);
2686                     }
2687                     keys.Clear();
2688                 }
2689             }
2690
2691             //終了ステータス
2692             bw.ReportProgress(100, MakeStatusMessage(args, true)); //ステータス書き換え、Notifyアイコンアニメーション開始
2693
2694             rslt.retMsg = ret;
2695             rslt.type = args.type;
2696             rslt.tName = args.tName;
2697
2698             e.Result = rslt;
2699         }
2700
2701         private string MakeStatusMessage(GetWorkerArg AsyncArg, bool Finish)
2702         {
2703             string smsg = "";
2704             if (!Finish)
2705             {
2706                 //継続中メッセージ
2707                 switch (AsyncArg.type)
2708                 {
2709                     case MyCommon.WORKERTYPE.Timeline:
2710                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText5 + AsyncArg.page.ToString() + Properties.Resources.GetTimelineWorker_RunWorkerCompletedText6;
2711                         break;
2712                     case MyCommon.WORKERTYPE.Reply:
2713                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText4 + AsyncArg.page.ToString() + Properties.Resources.GetTimelineWorker_RunWorkerCompletedText6;
2714                         break;
2715                     case MyCommon.WORKERTYPE.DirectMessegeRcv:
2716                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText8 + AsyncArg.page.ToString() + Properties.Resources.GetTimelineWorker_RunWorkerCompletedText6;
2717                         break;
2718                     case MyCommon.WORKERTYPE.FavAdd:
2719                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText15 + AsyncArg.page.ToString() + "/" + AsyncArg.ids.Count.ToString() +
2720                                             Properties.Resources.GetTimelineWorker_RunWorkerCompletedText16 + (AsyncArg.page - AsyncArg.sIds.Count - 1).ToString();
2721                         break;
2722                     case MyCommon.WORKERTYPE.FavRemove:
2723                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText17 + AsyncArg.page.ToString() + "/" + AsyncArg.ids.Count.ToString() +
2724                                             Properties.Resources.GetTimelineWorker_RunWorkerCompletedText18 + (AsyncArg.page - AsyncArg.sIds.Count - 1).ToString();
2725                         break;
2726                     case MyCommon.WORKERTYPE.Favorites:
2727                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText19;
2728                         break;
2729                     case MyCommon.WORKERTYPE.PublicSearch:
2730                         smsg = "Search refreshing...";
2731                         break;
2732                     case MyCommon.WORKERTYPE.List:
2733                         smsg = "List refreshing...";
2734                         break;
2735                     case MyCommon.WORKERTYPE.Related:
2736                         smsg = "Related refreshing...";
2737                         break;
2738                     case MyCommon.WORKERTYPE.UserTimeline:
2739                         smsg = "UserTimeline refreshing...";
2740                         break;
2741                 }
2742             }
2743             else
2744             {
2745                 //完了メッセージ
2746                 switch (AsyncArg.type)
2747                 {
2748                     case MyCommon.WORKERTYPE.Timeline:
2749                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText1;
2750                         break;
2751                     case MyCommon.WORKERTYPE.Reply:
2752                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText9;
2753                         break;
2754                     case MyCommon.WORKERTYPE.DirectMessegeRcv:
2755                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText11;
2756                         break;
2757                     case MyCommon.WORKERTYPE.DirectMessegeSnt:
2758                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText13;
2759                         break;
2760                     case MyCommon.WORKERTYPE.FavAdd:
2761                         //進捗メッセージ残す
2762                         break;
2763                     case MyCommon.WORKERTYPE.FavRemove:
2764                         //進捗メッセージ残す
2765                         break;
2766                     case MyCommon.WORKERTYPE.Favorites:
2767                         smsg = Properties.Resources.GetTimelineWorker_RunWorkerCompletedText20;
2768                         break;
2769                     case MyCommon.WORKERTYPE.Follower:
2770                         smsg = Properties.Resources.UpdateFollowersMenuItem1_ClickText3;
2771                         break;
2772                     case MyCommon.WORKERTYPE.NoRetweetIds:
2773                         smsg = "NoRetweetIds refreshed";
2774                         break;
2775                     case MyCommon.WORKERTYPE.Configuration:
2776                         //進捗メッセージ残す
2777                         break;
2778                     case MyCommon.WORKERTYPE.PublicSearch:
2779                         smsg = "Search refreshed";
2780                         break;
2781                     case MyCommon.WORKERTYPE.List:
2782                         smsg = "List refreshed";
2783                         break;
2784                     case MyCommon.WORKERTYPE.Related:
2785                         smsg = "Related refreshed";
2786                         break;
2787                     case MyCommon.WORKERTYPE.UserTimeline:
2788                         smsg = "UserTimeline refreshed";
2789                         break;
2790                     case MyCommon.WORKERTYPE.BlockIds:
2791                         smsg = Properties.Resources.UpdateBlockUserText3;
2792                         break;
2793                 }
2794             }
2795             return smsg;
2796         }
2797
2798         private void GetTimelineWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
2799         {
2800             if (MyCommon._endingFlag) return;
2801             if (e.ProgressPercentage > 100)
2802             {
2803                 //発言投稿
2804                 if (e.ProgressPercentage == 200)    //開始
2805                     StatusLabel.Text = "Posting...";
2806                 if (e.ProgressPercentage == 300)  //終了
2807                     StatusLabel.Text = Properties.Resources.PostWorker_RunWorkerCompletedText4;
2808             }
2809             else
2810             {
2811                 string smsg = (string)e.UserState;
2812                 if (smsg.Length > 0) StatusLabel.Text = smsg;
2813             }
2814         }
2815
2816         private void GetTimelineWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
2817         {
2818             if (MyCommon._endingFlag || e.Cancelled) return; //キャンセル
2819
2820             if (e.Error != null)
2821             {
2822                 _myStatusError = true;
2823                 _waitTimeline = false;
2824                 _waitReply = false;
2825                 _waitDm = false;
2826                 _waitFav = false;
2827                 _waitPubSearch = false;
2828                 _waitUserTimeline = false;
2829                 _waitLists = false;
2830                 throw new Exception("BackgroundWorker Exception", e.Error);
2831             }
2832
2833             GetWorkerResult rslt = (GetWorkerResult)e.Result;
2834
2835             //エラー
2836             if (rslt.retMsg.Length > 0)
2837             {
2838                 _myStatusError = true;
2839                 StatusLabel.Text = rslt.retMsg;
2840             }
2841
2842             if (rslt.type == MyCommon.WORKERTYPE.ErrorState) return;
2843
2844             if (rslt.type == MyCommon.WORKERTYPE.FavRemove)
2845             {
2846                 this.RemovePostFromFavTab(rslt.sIds.ToArray());
2847             }
2848
2849             //リストに反映
2850             //bool busy = false;
2851             //foreach (BackgroundWorker bw in _bw)
2852             //{
2853             //    if (bw != null && bw.IsBusy)
2854             //    {
2855             //        busy = true;
2856             //        break;
2857             //    }
2858             //}
2859             //if (!busy) RefreshTimeline(); //background処理なければ、リスト反映
2860             if (rslt.type == MyCommon.WORKERTYPE.Timeline ||
2861                 rslt.type == MyCommon.WORKERTYPE.Reply ||
2862                 rslt.type == MyCommon.WORKERTYPE.List ||
2863                 rslt.type == MyCommon.WORKERTYPE.PublicSearch ||
2864                 rslt.type == MyCommon.WORKERTYPE.DirectMessegeRcv ||
2865                 rslt.type == MyCommon.WORKERTYPE.DirectMessegeSnt ||
2866                 rslt.type == MyCommon.WORKERTYPE.Favorites ||
2867                 rslt.type == MyCommon.WORKERTYPE.Follower ||
2868                 rslt.type == MyCommon.WORKERTYPE.NoRetweetIds ||
2869                 rslt.type == MyCommon.WORKERTYPE.FavAdd ||
2870                 rslt.type == MyCommon.WORKERTYPE.FavRemove ||
2871                 rslt.type == MyCommon.WORKERTYPE.Related ||
2872                 rslt.type == MyCommon.WORKERTYPE.UserTimeline ||
2873                 rslt.type == MyCommon.WORKERTYPE.BlockIds ||
2874                 rslt.type == MyCommon.WORKERTYPE.Configuration)
2875             {
2876                 RefreshTimeline(false); //リスト反映
2877             }
2878
2879             switch (rslt.type)
2880             {
2881                 case MyCommon.WORKERTYPE.Timeline:
2882                     _waitTimeline = false;
2883                     if (!_initial)
2884                     {
2885                         //    //API使用時の取得調整は別途考える(カウント調整?)
2886                     }
2887                     break;
2888                 case MyCommon.WORKERTYPE.Reply:
2889                     _waitReply = false;
2890                     if (rslt.newDM && !_initial)
2891                     {
2892                         GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
2893                     }
2894                     break;
2895                 case MyCommon.WORKERTYPE.Favorites:
2896                     _waitFav = false;
2897                     break;
2898                 case MyCommon.WORKERTYPE.DirectMessegeRcv:
2899                     _waitDm = false;
2900                     break;
2901                 case MyCommon.WORKERTYPE.FavAdd:
2902                 case MyCommon.WORKERTYPE.FavRemove:
2903                     if (_curList != null && _curTab != null)
2904                     {
2905                         using (ControlTransaction.Update(this._curList))
2906                         {
2907                             if (rslt.type == MyCommon.WORKERTYPE.FavRemove && _statuses.Tabs[_curTab.Text].TabType == MyCommon.TabUsageType.Favorites)
2908                             {
2909                                 //色変えは不要
2910                             }
2911                             else
2912                             {
2913                                 for (int i = 0; i <= rslt.sIds.Count - 1; i++)
2914                                 {
2915                                     if (_curTab.Text.Equals(rslt.tName))
2916                                     {
2917                                         int idx = _statuses.Tabs[rslt.tName].IndexOf(rslt.sIds[i]);
2918                                         if (idx > -1)
2919                                         {
2920                                             PostClass post = null;
2921                                             TabClass tb = _statuses.Tabs[rslt.tName];
2922                                             if (tb != null)
2923                                             {
2924                                                 if (tb.TabType == MyCommon.TabUsageType.Lists || tb.TabType == MyCommon.TabUsageType.PublicSearch)
2925                                                 {
2926                                                     post = tb.Posts[rslt.sIds[i]];
2927                                                 }
2928                                                 else
2929                                                 {
2930                                                     post = _statuses[rslt.sIds[i]];
2931                                                 }
2932                                                 ChangeCacheStyleRead(post.IsRead, idx);
2933                                             }
2934                                             if (idx == _curItemIndex) DispSelectedPost(true); //選択アイテム再表示
2935                                         }
2936                                     }
2937                                 }
2938                             }
2939                         }
2940                     }
2941                     break;
2942                 case MyCommon.WORKERTYPE.PostMessage:
2943                     if (string.IsNullOrEmpty(rslt.retMsg) ||
2944                         rslt.retMsg.StartsWith("OK:") ||
2945                         rslt.retMsg == "Warn:Status is a duplicate.")
2946                     {
2947                         _postTimestamps.Add(DateTime.Now);
2948                         DateTime oneHour = DateTime.Now.Subtract(new TimeSpan(1, 0, 0));
2949                         for (int i = _postTimestamps.Count - 1; i >= 0; i--)
2950                         {
2951                             if (_postTimestamps[i].CompareTo(oneHour) < 0)
2952                             {
2953                                 _postTimestamps.RemoveAt(i);
2954                             }
2955                         }
2956
2957                         if (!HashMgr.IsPermanent && !string.IsNullOrEmpty(HashMgr.UseHash))
2958                         {
2959                             HashMgr.ClearHashtag();
2960                             this.HashStripSplitButton.Text = "#[-]";
2961                             this.HashToggleMenuItem.Checked = false;
2962                             this.HashToggleToolStripMenuItem.Checked = false;
2963                         }
2964                         SetMainWindowTitle();
2965                         rslt.retMsg = "";
2966                     }
2967                     else
2968                     {
2969                         DialogResult retry;
2970                         try
2971                         {
2972                             retry = MessageBox.Show(string.Format("{0}   --->   [ " + rslt.retMsg + " ]" + Environment.NewLine + "\"" + rslt.status.status + "\"" + Environment.NewLine + "{1}",
2973                                                                 Properties.Resources.StatusUpdateFailed1,
2974                                                                 Properties.Resources.StatusUpdateFailed2),
2975                                                             "Failed to update status",
2976                                                             MessageBoxButtons.RetryCancel,
2977                                                             MessageBoxIcon.Question);
2978                         }
2979                         catch (Exception)
2980                         {
2981                             retry = DialogResult.Abort;
2982                         }
2983                         if (retry == DialogResult.Retry)
2984                         {
2985                             GetWorkerArg args = new GetWorkerArg();
2986                             args.page = 0;
2987                             args.type = MyCommon.WORKERTYPE.PostMessage;
2988                             args.status = rslt.status;
2989                             RunAsync(args);
2990                         }
2991                         else
2992                         {
2993                             if (ToolStripFocusLockMenuItem.Checked)
2994                             {
2995                                 //連投モードのときだけEnterイベントが起きないので強制的に背景色を戻す
2996                                 StatusText_Enter(StatusText, new EventArgs());
2997                             }
2998                         }
2999                     }
3000                     if (rslt.retMsg.Length == 0 && this._cfgCommon.PostAndGet)
3001                     {
3002                         if (_isActiveUserstream)
3003                         {
3004                             RefreshTimeline(true);
3005                         }
3006                         else
3007                         {
3008                             GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
3009                         }
3010                     }
3011                     break;
3012                 case MyCommon.WORKERTYPE.Retweet:
3013                     if (rslt.retMsg.Length == 0)
3014                     {
3015                         _postTimestamps.Add(DateTime.Now);
3016                         DateTime oneHour = DateTime.Now.Subtract(new TimeSpan(1, 0, 0));
3017                         for (int i = _postTimestamps.Count - 1; i >= 0; i--)
3018                         {
3019                             if (_postTimestamps[i].CompareTo(oneHour) < 0)
3020                             {
3021                                 _postTimestamps.RemoveAt(i);
3022                             }
3023                         }
3024                         if (!_isActiveUserstream && this._cfgCommon.PostAndGet) GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
3025                     }
3026                     break;
3027                 case MyCommon.WORKERTYPE.Follower:
3028                     //_waitFollower = false;
3029                     this.PurgeListViewItemCache();
3030                     if (_curList != null) _curList.Refresh();
3031                     break;
3032                 case MyCommon.WORKERTYPE.NoRetweetIds:
3033                     break;
3034                 case MyCommon.WORKERTYPE.Configuration:
3035                     //_waitFollower = false
3036                     if (SettingDialog.TwitterConfiguration.PhotoSizeLimit != 0)
3037                     {
3038                         foreach (var service in this.ImageSelector.GetServices())
3039                         {
3040                             service.UpdateTwitterConfiguration(this.SettingDialog.TwitterConfiguration);
3041                         }
3042                     }
3043                     this.PurgeListViewItemCache();
3044                     if (_curList != null) _curList.Refresh();
3045                     break;
3046                 case MyCommon.WORKERTYPE.PublicSearch:
3047                     _waitPubSearch = false;
3048                     break;
3049                 case MyCommon.WORKERTYPE.UserTimeline:
3050                     _waitUserTimeline = false;
3051                     break;
3052                 case MyCommon.WORKERTYPE.List:
3053                     _waitLists = false;
3054                     break;
3055                 case MyCommon.WORKERTYPE.Related:
3056                     TabClass tab = _statuses.GetTabByType(MyCommon.TabUsageType.Related);
3057                     if (tab != null && tab.RelationTargetPost != null && tab.Contains(tab.RelationTargetPost.StatusId))
3058                     {
3059                         foreach (TabPage tp in ListTab.TabPages)
3060                         {
3061                             if (tp.Text == tab.TabName)
3062                             {
3063                                 ((DetailsListView)tp.Tag).SelectedIndices.Add(tab.IndexOf(tab.RelationTargetPost.StatusId));
3064                                 ((DetailsListView)tp.Tag).Items[tab.IndexOf(tab.RelationTargetPost.StatusId)].Focused = true;
3065                                 break;
3066                             }
3067                         }
3068                     }
3069                     break;
3070             }
3071         }
3072
3073         private async Task RefreshMuteUserIdsAsync()
3074         {
3075             this.StatusLabel.Text = Properties.Resources.UpdateMuteUserIds_Start;
3076
3077             try
3078             {
3079                 await tw.RefreshMuteUserIdsAsync();
3080             }
3081             catch (WebApiException ex)
3082             {
3083                 this.StatusLabel.Text = string.Format(Properties.Resources.UpdateMuteUserIds_Error, ex.Message);
3084                 return;
3085             }
3086
3087             this.StatusLabel.Text = Properties.Resources.UpdateMuteUserIds_Finish;
3088         }
3089
3090         private void RemovePostFromFavTab(Int64[] ids)
3091         {
3092             string favTabName = _statuses.GetTabByType(MyCommon.TabUsageType.Favorites).TabName;
3093             int fidx = 0;
3094             if (_curTab.Text.Equals(favTabName))
3095             {
3096                 if (_curList.FocusedItem != null)
3097                     fidx = _curList.FocusedItem.Index;
3098                 else if (_curList.TopItem != null)
3099                     fidx = _curList.TopItem.Index;
3100                 else
3101                     fidx = 0;
3102             }
3103
3104             foreach (long i in ids)
3105             {
3106                 try
3107                 {
3108                     _statuses.RemoveFavPost(i);
3109                 }
3110                 catch (Exception)
3111                 {
3112                     continue;
3113                 }
3114             }
3115             if (_curTab != null && _curTab.Text.Equals(favTabName))
3116             {
3117                 this.PurgeListViewItemCache();
3118                 _curPost = null;
3119                 //_curItemIndex = -1;
3120             }
3121             foreach (TabPage tp in ListTab.TabPages)
3122             {
3123                 if (tp.Text == favTabName)
3124                 {
3125                     ((DetailsListView)tp.Tag).VirtualListSize = _statuses.Tabs[favTabName].AllCount;
3126                     break;
3127                 }
3128             }
3129             if (_curTab.Text.Equals(favTabName))
3130             {
3131                 do
3132                 {
3133                     _curList.SelectedIndices.Clear();
3134                 }
3135                 while (_curList.SelectedIndices.Count > 0);
3136
3137                 if (_statuses.Tabs[favTabName].AllCount > 0)
3138                 {
3139                     if (_statuses.Tabs[favTabName].AllCount - 1 > fidx && fidx > -1)
3140                     {
3141                         _curList.SelectedIndices.Add(fidx);
3142                     }
3143                     else
3144                     {
3145                         _curList.SelectedIndices.Add(_statuses.Tabs[favTabName].AllCount - 1);
3146                     }
3147                     if (_curList.SelectedIndices.Count > 0)
3148                     {
3149                         _curList.EnsureVisible(_curList.SelectedIndices[0]);
3150                         _curList.FocusedItem = _curList.Items[_curList.SelectedIndices[0]];
3151                     }
3152                 }
3153             }
3154         }
3155
3156         private static Dictionary<MyCommon.WORKERTYPE, DateTime> lastTime = new Dictionary<MyCommon.WORKERTYPE, DateTime>();
3157
3158         private void GetTimeline(MyCommon.WORKERTYPE WkType, int fromPage, string tabName)
3159         {
3160             if (!this.IsNetworkAvailable()) return;
3161
3162             //非同期実行引数設定
3163             GetWorkerArg args = new GetWorkerArg();
3164             args.page = fromPage;
3165             args.type = WkType;
3166             args.tName = tabName;
3167
3168             if (!lastTime.ContainsKey(WkType)) lastTime.Add(WkType, new DateTime());
3169             double period = DateTime.Now.Subtract(lastTime[WkType]).TotalSeconds;
3170             if (period > 1 || period < -1)
3171             {
3172                 lastTime[WkType] = DateTime.Now;
3173                 RunAsync(args);
3174             }
3175
3176             //Timeline取得モードの場合はReplyも同時に取得
3177             //if (!SettingDialog.UseAPI &&
3178             //   !_initial &&
3179             //   WkType == MyCommon.WORKERTYPE.Timeline &&
3180             //   SettingDialog.CheckReply)
3181             //{
3182             //    //TimerReply.Enabled = false;
3183             //    _mentionCounter = SettingDialog.ReplyPeriodInt;
3184             //    GetWorkerArg _args = new GetWorkerArg();
3185             //    _args.page = fromPage;
3186             //    _args.endPage = toPage;
3187             //    _args.type = MyCommon.WORKERTYPE.Reply;
3188             //    RunAsync(_args);
3189             //}
3190         }
3191
3192         private void NotifyIcon1_MouseClick(object sender, MouseEventArgs e)
3193         {
3194             if (e.Button == MouseButtons.Left)
3195             {
3196                 this.Visible = true;
3197                 if (this.WindowState == FormWindowState.Minimized)
3198                 {
3199                     this.WindowState = _formWindowState;
3200                 }
3201                 this.Activate();
3202                 this.BringToFront();
3203             }
3204         }
3205
3206         private void MyList_MouseDoubleClick(object sender, MouseEventArgs e)
3207         {
3208             switch (SettingDialog.ListDoubleClickAction)
3209             {
3210                 case 0:
3211                     MakeReplyOrDirectStatus();
3212                     break;
3213                 case 1:
3214                     FavoriteChange(true);
3215                     break;
3216                 case 2:
3217                     if (_curPost != null)
3218                         ShowUserStatus(_curPost.ScreenName, false);
3219                     break;
3220                 case 3:
3221                     ShowUserTimeline();
3222                     break;
3223                 case 4:
3224                     ShowRelatedStatusesMenuItem_Click(null, null);
3225                     break;
3226                 case 5:
3227                     MoveToHomeToolStripMenuItem_Click(null, null);
3228                     break;
3229                 case 6:
3230                     StatusOpenMenuItem_Click(null, null);
3231                     break;
3232                 case 7:
3233                     //動作なし
3234                     break;
3235             }
3236         }
3237
3238         private void FavAddToolStripMenuItem_Click(object sender, EventArgs e)
3239         {
3240             FavoriteChange(true);
3241         }
3242
3243         private void FavRemoveToolStripMenuItem_Click(object sender, EventArgs e)
3244         {
3245             FavoriteChange(false);
3246         }
3247
3248
3249         private void FavoriteRetweetMenuItem_Click(object sender, EventArgs e)
3250         {
3251             FavoritesRetweetOriginal();
3252         }
3253
3254         private void FavoriteRetweetUnofficialMenuItem_Click(object sender, EventArgs e)
3255         {
3256             FavoritesRetweetUnofficial();
3257         }
3258
3259         private void FavoriteChange(bool FavAdd , bool multiFavoriteChangeDialogEnable = true)
3260         {
3261             //trueでFavAdd,falseでFavRemove
3262             if (_statuses.Tabs[_curTab.Text].TabType == MyCommon.TabUsageType.DirectMessage || _curList.SelectedIndices.Count == 0
3263                 || !this.ExistCurrentPost) return;
3264
3265             //複数fav確認msg
3266             if (_curList.SelectedIndices.Count > 250 && FavAdd)
3267             {
3268                 MessageBox.Show(Properties.Resources.FavoriteLimitCountText);
3269                 _DoFavRetweetFlags = false;
3270                 return;
3271             }
3272             else if (multiFavoriteChangeDialogEnable && _curList.SelectedIndices.Count > 1)
3273             {
3274                 if (FavAdd)
3275                 {
3276                     string QuestionText = Properties.Resources.FavAddToolStripMenuItem_ClickText1;
3277                     if (_DoFavRetweetFlags) QuestionText = Properties.Resources.FavoriteRetweetQuestionText3;
3278                     if (MessageBox.Show(QuestionText, Properties.Resources.FavAddToolStripMenuItem_ClickText2,
3279                                        MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
3280                     {
3281                         _DoFavRetweetFlags = false;
3282                         return;
3283                     }
3284                 }
3285                 else
3286                 {
3287                     if (MessageBox.Show(Properties.Resources.FavRemoveToolStripMenuItem_ClickText1, Properties.Resources.FavRemoveToolStripMenuItem_ClickText2,
3288                                     MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
3289                     {
3290                         return;
3291                     }
3292                 }
3293             }
3294
3295             GetWorkerArg args = new GetWorkerArg();
3296             args.ids = new List<long>();
3297             args.sIds = new List<long>();
3298             args.tName = _curTab.Text;
3299             if (FavAdd)
3300             {
3301                 args.type = MyCommon.WORKERTYPE.FavAdd;
3302             }
3303             else
3304             {
3305                 args.type = MyCommon.WORKERTYPE.FavRemove;
3306             }
3307             foreach (int idx in _curList.SelectedIndices)
3308             {
3309                 PostClass post = GetCurTabPost(idx);
3310                 if (FavAdd)
3311                 {
3312                     if (!post.IsFav) args.ids.Add(post.StatusId);
3313                 }
3314                 else
3315                 {
3316                     if (post.IsFav) args.ids.Add(post.StatusId);
3317                 }
3318             }
3319             if (args.ids.Count == 0)
3320             {
3321                 if (FavAdd)
3322                     StatusLabel.Text = Properties.Resources.FavAddToolStripMenuItem_ClickText4;
3323                 else
3324                     StatusLabel.Text = Properties.Resources.FavRemoveToolStripMenuItem_ClickText4;
3325
3326                 return;
3327             }
3328
3329             RunAsync(args);
3330         }
3331
3332         private PostClass GetCurTabPost(int Index)
3333         {
3334             this.itemCacheLock.EnterReadLock();
3335             try
3336             {
3337                 if (_postCache != null && Index >= _itemCacheIndex && Index < _itemCacheIndex + _postCache.Length)
3338                     return _postCache[Index - _itemCacheIndex];
3339             }
3340             finally { this.itemCacheLock.ExitReadLock(); }
3341
3342             return _statuses[_curTab.Text, Index];
3343         }
3344
3345
3346         private void MoveToHomeToolStripMenuItem_Click(object sender, EventArgs e)
3347         {
3348             if (_curList.SelectedIndices.Count > 0)
3349                 OpenUriAsync(MyCommon.TwitterUrl + GetCurTabPost(_curList.SelectedIndices[0]).ScreenName);
3350             else if (_curList.SelectedIndices.Count == 0)
3351                 OpenUriAsync(MyCommon.TwitterUrl);
3352         }
3353
3354         private void MoveToFavToolStripMenuItem_Click(object sender, EventArgs e)
3355         {
3356             if (_curList.SelectedIndices.Count > 0)
3357                 OpenUriAsync(MyCommon.TwitterUrl + "#!/" + GetCurTabPost(_curList.SelectedIndices[0]).ScreenName + "/favorites");
3358         }
3359
3360         private void TweenMain_ClientSizeChanged(object sender, EventArgs e)
3361         {
3362             if ((!_initialLayout) && this.Visible)
3363             {
3364                 if (this.WindowState == FormWindowState.Normal)
3365                 {
3366                     _mySize = this.ClientSize;
3367                     _mySpDis = this.SplitContainer1.SplitterDistance;
3368                     _mySpDis3 = this.SplitContainer3.SplitterDistance;
3369                     if (StatusText.Multiline) _mySpDis2 = this.StatusText.Height;
3370                     _modifySettingLocal = true;
3371                 }
3372             }
3373         }
3374
3375         private void MyList_ColumnClick(object sender, ColumnClickEventArgs e)
3376         {
3377             if (this._cfgCommon.SortOrderLock) return;
3378             IdComparerClass.ComparerMode mode = IdComparerClass.ComparerMode.Id;
3379             if (_iconCol)
3380             {
3381                 mode = IdComparerClass.ComparerMode.Id;
3382             }
3383             else
3384             {
3385                 switch (e.Column)
3386                 {
3387                     case 0:
3388                     case 5:
3389                     case 6:    //0:アイコン,5:未読マーク,6:プロテクト・フィルターマーク
3390                         //ソートしない
3391                         return;
3392                     case 1:  //ニックネーム
3393                         mode = IdComparerClass.ComparerMode.Nickname;
3394                         break;
3395                     case 2:  //本文
3396                         mode = IdComparerClass.ComparerMode.Data;
3397                         break;
3398                     case 3:  //時刻=発言Id
3399                         mode = IdComparerClass.ComparerMode.Id;
3400                         break;
3401                     case 4:  //名前
3402                         mode = IdComparerClass.ComparerMode.Name;
3403                         break;
3404                     case 7:  //Source
3405                         mode = IdComparerClass.ComparerMode.Source;
3406                         break;
3407                 }
3408             }
3409             _statuses.ToggleSortOrder(mode);
3410             InitColumnText();
3411
3412             DetailsListView list = (DetailsListView)sender;
3413             if (_iconCol)
3414             {
3415                 list.Columns[0].Text = ColumnOrgText[0];
3416                 list.Columns[1].Text = ColumnText[2];
3417             }
3418             else
3419             {
3420                 for (int i = 0; i <= 7; i++)
3421                 {
3422                     list.Columns[i].Text = ColumnOrgText[i];
3423                 }
3424                 list.Columns[e.Column].Text = ColumnText[e.Column];
3425             }
3426
3427             this.PurgeListViewItemCache();
3428
3429             if (_statuses.Tabs[_curTab.Text].AllCount > 0 && _curPost != null)
3430             {
3431                 int idx = _statuses.Tabs[_curTab.Text].IndexOf(_curPost.StatusId);
3432                 if (idx > -1)
3433                 {
3434                     SelectListItem(_curList, idx);
3435                     _curList.EnsureVisible(idx);
3436                 }
3437             }
3438             _curList.Refresh();
3439             _modifySettingCommon = true;
3440         }
3441
3442         private void TweenMain_LocationChanged(object sender, EventArgs e)
3443         {
3444             if (this.WindowState == FormWindowState.Normal && !_initialLayout)
3445             {
3446                 _myLoc = this.DesktopLocation;
3447                 _modifySettingLocal = true;
3448             }
3449         }
3450
3451         private void ContextMenuOperate_Opening(object sender, CancelEventArgs e)
3452         {
3453             if (ListTab.SelectedTab == null) return;
3454             if (_statuses == null || _statuses.Tabs == null || !_statuses.Tabs.ContainsKey(ListTab.SelectedTab.Text)) return;
3455             if (!this.ExistCurrentPost)
3456             {
3457                 ReplyStripMenuItem.Enabled = false;
3458                 ReplyAllStripMenuItem.Enabled = false;
3459                 DMStripMenuItem.Enabled = false;
3460                 ShowProfileMenuItem.Enabled = false;
3461                 ShowUserTimelineContextMenuItem.Enabled = false;
3462                 ListManageUserContextToolStripMenuItem2.Enabled = false;
3463                 MoveToFavToolStripMenuItem.Enabled = false;
3464                 TabMenuItem.Enabled = false;
3465                 IDRuleMenuItem.Enabled = false;
3466                 ReadedStripMenuItem.Enabled = false;
3467                 UnreadStripMenuItem.Enabled = false;
3468             }
3469             else
3470             {
3471                 ShowProfileMenuItem.Enabled = true;
3472                 ListManageUserContextToolStripMenuItem2.Enabled = true;
3473                 ReplyStripMenuItem.Enabled = true;
3474                 ReplyAllStripMenuItem.Enabled = true;
3475                 DMStripMenuItem.Enabled = true;
3476                 ShowUserTimelineContextMenuItem.Enabled = true;
3477                 MoveToFavToolStripMenuItem.Enabled = true;
3478                 TabMenuItem.Enabled = true;
3479                 IDRuleMenuItem.Enabled = true;
3480                 ReadedStripMenuItem.Enabled = true;
3481                 UnreadStripMenuItem.Enabled = true;
3482             }
3483             DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText1;
3484             if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || _curPost.IsDm)
3485             {
3486                 FavAddToolStripMenuItem.Enabled = false;
3487                 FavRemoveToolStripMenuItem.Enabled = false;
3488                 StatusOpenMenuItem.Enabled = false;
3489                 FavorareMenuItem.Enabled = false;
3490                 ShowRelatedStatusesMenuItem.Enabled = false;
3491
3492                 ReTweetStripMenuItem.Enabled = false;
3493                 ReTweetOriginalStripMenuItem.Enabled = false;
3494                 QuoteStripMenuItem.Enabled = false;
3495                 FavoriteRetweetContextMenu.Enabled = false;
3496                 FavoriteRetweetUnofficialContextMenu.Enabled = false;
3497                 if (this.ExistCurrentPost && _curPost.IsDm)
3498                     DeleteStripMenuItem.Enabled = true;
3499                 else
3500                     DeleteStripMenuItem.Enabled = false;
3501             }
3502             else
3503             {
3504                 FavAddToolStripMenuItem.Enabled = true;
3505                 FavRemoveToolStripMenuItem.Enabled = true;
3506                 StatusOpenMenuItem.Enabled = true;
3507                 FavorareMenuItem.Enabled = true;
3508                 ShowRelatedStatusesMenuItem.Enabled = true;  //PublicSearchの時問題出るかも
3509
3510                 if (_curPost.IsMe)
3511                 {
3512                     ReTweetOriginalStripMenuItem.Enabled = false;
3513                     FavoriteRetweetContextMenu.Enabled = false;
3514                     if (string.IsNullOrEmpty(_curPost.RetweetedBy))
3515                     {
3516                         DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText1;
3517                     }
3518                     else
3519                     {
3520                         DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText2;
3521                     }
3522                     DeleteStripMenuItem.Enabled = true;
3523                 }
3524                 else
3525                 {
3526                     if (string.IsNullOrEmpty(_curPost.RetweetedBy))
3527                     {
3528                         DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText1;
3529                     }
3530                     else
3531                     {
3532                         DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText2;
3533                     }
3534                     DeleteStripMenuItem.Enabled = false;
3535                     if (_curPost.IsProtect)
3536                     {
3537                         ReTweetOriginalStripMenuItem.Enabled = false;
3538                         ReTweetStripMenuItem.Enabled = false;
3539                         QuoteStripMenuItem.Enabled = false;
3540                         FavoriteRetweetContextMenu.Enabled = false;
3541                         FavoriteRetweetUnofficialContextMenu.Enabled = false;
3542                     }
3543                     else
3544                     {
3545                         ReTweetOriginalStripMenuItem.Enabled = true;
3546                         ReTweetStripMenuItem.Enabled = true;
3547                         QuoteStripMenuItem.Enabled = true;
3548                         FavoriteRetweetContextMenu.Enabled = true;
3549                         FavoriteRetweetUnofficialContextMenu.Enabled = true;
3550                     }
3551                 }
3552             }
3553             //if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType != MyCommon.TabUsageType.Favorites)
3554             //{
3555             //    RefreshMoreStripMenuItem.Enabled = true;
3556             //}
3557             //else
3558             //{
3559             //    RefreshMoreStripMenuItem.Enabled = false;
3560             //}
3561             if (!this.ExistCurrentPost
3562                 || _curPost.InReplyToStatusId == null)
3563             {
3564                 RepliedStatusOpenMenuItem.Enabled = false;
3565             }
3566             else
3567             {
3568                 RepliedStatusOpenMenuItem.Enabled = true;
3569             }
3570             if (!this.ExistCurrentPost || string.IsNullOrEmpty(_curPost.RetweetedBy))
3571             {
3572                 MoveToRTHomeMenuItem.Enabled = false;
3573             }
3574             else
3575             {
3576                 MoveToRTHomeMenuItem.Enabled = true;
3577             }
3578         }
3579
3580         private void ReplyStripMenuItem_Click(object sender, EventArgs e)
3581         {
3582             MakeReplyOrDirectStatus(false, true);
3583         }
3584
3585         private void DMStripMenuItem_Click(object sender, EventArgs e)
3586         {
3587             MakeReplyOrDirectStatus(false, false);
3588         }
3589
3590         private void doStatusDelete()
3591         {
3592             if (_curTab == null || _curList == null) return;
3593             if (_statuses.Tabs[_curTab.Text].TabType != MyCommon.TabUsageType.DirectMessage)
3594             {
3595                 bool myPost = false;
3596                 foreach (int idx in _curList.SelectedIndices)
3597                 {
3598                     if (GetCurTabPost(idx).IsMe ||
3599                        GetCurTabPost(idx).RetweetedBy.ToLower() == tw.Username.ToLower())
3600                     {
3601                         myPost = true;
3602                         break;
3603                     }
3604                 }
3605                 if (!myPost) return;
3606             }
3607             else
3608             {
3609                 if (_curList.SelectedIndices.Count == 0)
3610                     return;
3611             }
3612
3613             string tmp = string.Format(Properties.Resources.DeleteStripMenuItem_ClickText1, Environment.NewLine);
3614
3615             if (MessageBox.Show(tmp, Properties.Resources.DeleteStripMenuItem_ClickText2,
3616                   MessageBoxButtons.OKCancel,
3617                   MessageBoxIcon.Question) == DialogResult.Cancel) return;
3618
3619             int fidx;
3620             if (_curList.FocusedItem != null)
3621                 fidx = _curList.FocusedItem.Index;
3622             else if (_curList.TopItem != null)
3623                 fidx = _curList.TopItem.Index;
3624             else
3625                 fidx = 0;
3626
3627             try
3628             {
3629                 this.Cursor = Cursors.WaitCursor;
3630
3631                 bool rslt = true;
3632                 foreach (long Id in _statuses.GetId(_curTab.Text, _curList.SelectedIndices))
3633                 {
3634                     string rtn = "";
3635                     if (_statuses.Tabs[_curTab.Text].TabType == MyCommon.TabUsageType.DirectMessage)
3636                     {
3637                         rtn = tw.RemoveDirectMessage(Id, _statuses[Id]);
3638                     }
3639                     else
3640                     {
3641                         if (_statuses[Id].IsMe || _statuses[Id].RetweetedBy.ToLower() == tw.Username.ToLower())
3642                             rtn = tw.RemoveStatus(Id);
3643                         else
3644                             continue;
3645                     }
3646                     if (rtn.Length > 0)
3647                     {
3648                         //エラー
3649                         rslt = false;
3650                     }
3651                     else
3652                     {
3653                         _statuses.RemovePost(Id);
3654                     }
3655                 }
3656
3657                 if (!rslt)
3658                     StatusLabel.Text = Properties.Resources.DeleteStripMenuItem_ClickText3;  //失敗
3659                 else
3660                     StatusLabel.Text = Properties.Resources.DeleteStripMenuItem_ClickText4;  //成功
3661
3662                 this.PurgeListViewItemCache();
3663                 _curPost = null;
3664                 _curItemIndex = -1;
3665                 foreach (TabPage tb in ListTab.TabPages)
3666                 {
3667                     ((DetailsListView)tb.Tag).VirtualListSize = _statuses.Tabs[tb.Text].AllCount;
3668                     if (_curTab.Equals(tb))
3669                     {
3670                         do
3671                         {
3672                             _curList.SelectedIndices.Clear();
3673                         }
3674                         while (_curList.SelectedIndices.Count > 0);
3675
3676                         if (_statuses.Tabs[tb.Text].AllCount > 0)
3677                         {
3678                             if (_statuses.Tabs[tb.Text].AllCount - 1 > fidx && fidx > -1)
3679                                 _curList.SelectedIndices.Add(fidx);
3680                             else
3681                                 _curList.SelectedIndices.Add(_statuses.Tabs[tb.Text].AllCount - 1);
3682
3683                             if (_curList.SelectedIndices.Count > 0)
3684                             {
3685                                 _curList.EnsureVisible(_curList.SelectedIndices[0]);
3686                                 _curList.FocusedItem = _curList.Items[_curList.SelectedIndices[0]];
3687                             }
3688                         }
3689                     }
3690                     if (_statuses.Tabs[tb.Text].UnreadCount == 0)
3691                     {
3692                         if (SettingDialog.TabIconDisp)
3693                         {
3694                             if (tb.ImageIndex == 0) tb.ImageIndex = -1; //タブアイコン
3695                         }
3696                     }
3697                 }
3698                 if (!SettingDialog.TabIconDisp) ListTab.Refresh();
3699             }
3700             finally
3701             {
3702                 this.Cursor = Cursors.Default;
3703             }
3704         }
3705
3706         private void DeleteStripMenuItem_Click(object sender, EventArgs e)
3707         {
3708             doStatusDelete();
3709         }
3710
3711         private void ReadedStripMenuItem_Click(object sender, EventArgs e)
3712         {
3713             using (ControlTransaction.Update(this._curList))
3714             {
3715                 if (SettingDialog.UnreadManage)
3716                 {
3717                     foreach (int idx in _curList.SelectedIndices)
3718                     {
3719                         _statuses.SetReadAllTab(true, _curTab.Text, idx);
3720                     }
3721                 }
3722                 foreach (int idx in _curList.SelectedIndices)
3723                 {
3724                     ChangeCacheStyleRead(true, idx);
3725                 }
3726                 ColorizeList();
3727             }
3728             foreach (TabPage tb in ListTab.TabPages)
3729             {
3730                 if (_statuses.Tabs[tb.Text].UnreadCount == 0)
3731                 {
3732                     if (SettingDialog.TabIconDisp)
3733                     {
3734                         if (tb.ImageIndex == 0) tb.ImageIndex = -1; //タブアイコン
3735                     }
3736                 }
3737             }
3738             if (!SettingDialog.TabIconDisp) ListTab.Refresh();
3739         }
3740
3741         private void UnreadStripMenuItem_Click(object sender, EventArgs e)
3742         {
3743             using (ControlTransaction.Update(this._curList))
3744             {
3745                 if (SettingDialog.UnreadManage)
3746                 {
3747                     foreach (int idx in _curList.SelectedIndices)
3748                     {
3749                         _statuses.SetReadAllTab(false, _curTab.Text, idx);
3750                     }
3751                 }
3752                 foreach (int idx in _curList.SelectedIndices)
3753                 {
3754                     ChangeCacheStyleRead(false, idx);
3755                 }
3756                 ColorizeList();
3757             }
3758             foreach (TabPage tb in ListTab.TabPages)
3759             {
3760                 if (_statuses.Tabs[tb.Text].UnreadCount > 0)
3761                 {
3762                     if (SettingDialog.TabIconDisp)
3763                     {
3764                         if (tb.ImageIndex == -1) tb.ImageIndex = 0; //タブアイコン
3765                     }
3766                 }
3767             }
3768             if (!SettingDialog.TabIconDisp) ListTab.Refresh();
3769         }
3770
3771         private void RefreshStripMenuItem_Click(object sender, EventArgs e)
3772         {
3773             DoRefresh();
3774         }
3775
3776         private void DoRefresh()
3777         {
3778             if (_curTab != null)
3779             {
3780                 switch (_statuses.Tabs[_curTab.Text].TabType)
3781                 {
3782                     case MyCommon.TabUsageType.Mentions:
3783                         GetTimeline(MyCommon.WORKERTYPE.Reply, 1, "");
3784                         break;
3785                     case MyCommon.TabUsageType.DirectMessage:
3786                         GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
3787                         break;
3788                     case MyCommon.TabUsageType.Favorites:
3789                         GetTimeline(MyCommon.WORKERTYPE.Favorites, 1, "");
3790                         break;
3791                     //case MyCommon.TabUsageType.Profile:
3792                         //// TODO
3793                     case MyCommon.TabUsageType.PublicSearch:
3794                         //// TODO
3795                         TabClass tb = _statuses.Tabs[_curTab.Text];
3796                         if (string.IsNullOrEmpty(tb.SearchWords)) return;
3797                         GetTimeline(MyCommon.WORKERTYPE.PublicSearch, 1, _curTab.Text);
3798                         break;
3799                     case MyCommon.TabUsageType.UserTimeline:
3800                         GetTimeline(MyCommon.WORKERTYPE.UserTimeline, 1, _curTab.Text);
3801                         break;
3802                     case MyCommon.TabUsageType.Lists:
3803                         //// TODO
3804                         TabClass tab = _statuses.Tabs[_curTab.Text];
3805                         if (tab.ListInfo == null || tab.ListInfo.Id == 0) return;
3806                         GetTimeline(MyCommon.WORKERTYPE.List, 1, _curTab.Text);
3807                         break;
3808                     default:
3809                         GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
3810                         break;
3811                 }
3812             }
3813             else
3814             {
3815                 GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
3816             }
3817         }
3818
3819         private void DoRefreshMore()
3820         {
3821             //ページ指定をマイナス1に
3822             if (_curTab != null)
3823             {
3824                 switch (_statuses.Tabs[_curTab.Text].TabType)
3825                 {
3826                     case MyCommon.TabUsageType.Mentions:
3827                         GetTimeline(MyCommon.WORKERTYPE.Reply, -1, "");
3828                         break;
3829                     case MyCommon.TabUsageType.DirectMessage:
3830                         GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, -1, "");
3831                         break;
3832                     case MyCommon.TabUsageType.Favorites:
3833                         GetTimeline(MyCommon.WORKERTYPE.Favorites, -1, "");
3834                         break;
3835                     case MyCommon.TabUsageType.Profile:
3836                         //// TODO
3837                         break;
3838                     case MyCommon.TabUsageType.PublicSearch:
3839                         // TODO
3840                         TabClass tb = _statuses.Tabs[_curTab.Text];
3841                         if (string.IsNullOrEmpty(tb.SearchWords)) return;
3842                         GetTimeline(MyCommon.WORKERTYPE.PublicSearch, -1, _curTab.Text);
3843                         break;
3844                     case MyCommon.TabUsageType.UserTimeline:
3845                         GetTimeline(MyCommon.WORKERTYPE.UserTimeline, -1, _curTab.Text);
3846                         break;
3847                     case MyCommon.TabUsageType.Lists:
3848                         //// TODO
3849                         TabClass tab = _statuses.Tabs[_curTab.Text];
3850                         if (tab.ListInfo == null || tab.ListInfo.Id == 0) return;
3851                         GetTimeline(MyCommon.WORKERTYPE.List, -1, _curTab.Text);
3852                         break;
3853                     default:
3854                         GetTimeline(MyCommon.WORKERTYPE.Timeline, -1, "");
3855                         break;
3856                 }
3857             }
3858             else
3859             {
3860                 GetTimeline(MyCommon.WORKERTYPE.Timeline, -1, "");
3861             }
3862         }
3863
3864         private void SettingStripMenuItem_Click(object sender, EventArgs e)
3865         {
3866             DialogResult result;
3867             string uid = tw.Username.ToLower();
3868
3869             try
3870             {
3871                 result = SettingDialog.ShowDialog(this);
3872             }
3873             catch (Exception)
3874             {
3875                 return;
3876             }
3877
3878             if (result == DialogResult.OK)
3879             {
3880                 lock (_syncObject)
3881                 {
3882                     var oldIconSz = this._cfgCommon.IconSize;
3883
3884                     this.SettingDialog.SaveConfig(this._cfgCommon, this._cfgLocal);
3885
3886                     tw.RestrictFavCheck = SettingDialog.RestrictFavCheck;
3887                     tw.ReadOwnPost = SettingDialog.ReadOwnPost;
3888                     ShortUrl.Instance.DisableExpanding = !SettingDialog.TinyUrlResolve;
3889                     ShortUrl.Instance.BitlyId = SettingDialog.BitlyUser;
3890                     ShortUrl.Instance.BitlyKey = SettingDialog.BitlyPwd;
3891                     HttpTwitter.TwitterUrl = _cfgCommon.TwitterUrl;
3892
3893                     Networking.DefaultTimeout = TimeSpan.FromSeconds(this.SettingDialog.DefaultTimeOut);
3894                     Networking.SetWebProxy(this.SettingDialog.SelectedProxyType,
3895                         this.SettingDialog.ProxyAddress, this.SettingDialog.ProxyPort,
3896                         this.SettingDialog.ProxyUser, this.SettingDialog.ProxyPassword);
3897
3898                     ImageSelector.Reset(tw, SettingDialog.TwitterConfiguration);
3899
3900                     try
3901                     {
3902                         if (SettingDialog.TabIconDisp)
3903                         {
3904                             ListTab.DrawItem -= ListTab_DrawItem;
3905                             ListTab.DrawMode = TabDrawMode.Normal;
3906                             ListTab.ImageList = this.TabImage;
3907                         }
3908                         else
3909                         {
3910                             ListTab.DrawItem -= ListTab_DrawItem;
3911                             ListTab.DrawItem += ListTab_DrawItem;
3912                             ListTab.DrawMode = TabDrawMode.OwnerDrawFixed;
3913                             ListTab.ImageList = null;
3914                         }
3915                     }
3916                     catch (Exception ex)
3917                     {
3918                         ex.Data["Instance"] = "ListTab(TabIconDisp)";
3919                         ex.Data["IsTerminatePermission"] = false;
3920                         throw;
3921                     }
3922
3923                     try
3924                     {
3925                         if (!SettingDialog.UnreadManage)
3926                         {
3927                             ReadedStripMenuItem.Enabled = false;
3928                             UnreadStripMenuItem.Enabled = false;
3929                             if (SettingDialog.TabIconDisp)
3930                             {
3931                                 foreach (TabPage myTab in ListTab.TabPages)
3932                                 {
3933                                     myTab.ImageIndex = -1;
3934                                 }
3935                             }
3936                         }
3937                         else
3938                         {
3939                             ReadedStripMenuItem.Enabled = true;
3940                             UnreadStripMenuItem.Enabled = true;
3941                         }
3942                     }
3943                     catch (Exception ex)
3944                     {
3945                         ex.Data["Instance"] = "ListTab(UnreadManage)";
3946                         ex.Data["IsTerminatePermission"] = false;
3947                         throw;
3948                     }
3949
3950                     // タブの表示位置の決定
3951                     SetTabAlignment();
3952
3953                     SplitContainer1.IsPanelInverted = !SettingDialog.StatusAreaAtBottom;
3954
3955                     var imgazyobizinet = ThumbnailGenerator.ImgAzyobuziNetInstance;
3956                     imgazyobizinet.Enabled = this.SettingDialog.EnableImgAzyobuziNet;
3957                     imgazyobizinet.DisabledInDM = this.SettingDialog.ImgAzyobuziNetDisabledInDM;
3958
3959                     PlaySoundMenuItem.Checked = SettingDialog.PlaySound;
3960                     this.PlaySoundFileMenuItem.Checked = SettingDialog.PlaySound;
3961                     _fntUnread = SettingDialog.FontUnread;
3962                     _clUnread = SettingDialog.ColorUnread;
3963                     _fntReaded = SettingDialog.FontReaded;
3964                     _clReaded = SettingDialog.ColorReaded;
3965                     _clFav = SettingDialog.ColorFav;
3966                     _clOWL = SettingDialog.ColorOWL;
3967                     _clRetweet = SettingDialog.ColorRetweet;
3968                     _fntDetail = SettingDialog.FontDetail;
3969                     _clDetail = SettingDialog.ColorDetail;
3970                     _clDetailLink = SettingDialog.ColorDetailLink;
3971                     _clDetailBackcolor = SettingDialog.ColorDetailBackcolor;
3972                     _clSelf = SettingDialog.ColorSelf;
3973                     _clAtSelf = SettingDialog.ColorAtSelf;
3974                     _clTarget = SettingDialog.ColorTarget;
3975                     _clAtTarget = SettingDialog.ColorAtTarget;
3976                     _clAtFromTarget = SettingDialog.ColorAtFromTarget;
3977                     _clAtTo = SettingDialog.ColorAtTo;
3978                     _clListBackcolor = SettingDialog.ColorListBackcolor;
3979                     _clInputBackcolor = SettingDialog.ColorInputBackcolor;
3980                     _clInputFont = SettingDialog.ColorInputFont;
3981                     _fntInputFont = SettingDialog.FontInputFont;
3982                     _brsBackColorMine.Dispose();
3983                     _brsBackColorAt.Dispose();
3984                     _brsBackColorYou.Dispose();
3985                     _brsBackColorAtYou.Dispose();
3986                     _brsBackColorAtFromTarget.Dispose();
3987                     _brsBackColorAtTo.Dispose();
3988                     _brsBackColorNone.Dispose();
3989                     _brsBackColorMine = new SolidBrush(_clSelf);
3990                     _brsBackColorAt = new SolidBrush(_clAtSelf);
3991                     _brsBackColorYou = new SolidBrush(_clTarget);
3992                     _brsBackColorAtYou = new SolidBrush(_clAtTarget);
3993                     _brsBackColorAtFromTarget = new SolidBrush(_clAtFromTarget);
3994                     _brsBackColorAtTo = new SolidBrush(_clAtTo);
3995                     _brsBackColorNone = new SolidBrush(_clListBackcolor);
3996
3997                     try
3998                     {
3999                         if (StatusText.Focused) StatusText.BackColor = _clInputBackcolor;
4000                         StatusText.Font = _fntInputFont;
4001                         StatusText.ForeColor = _clInputFont;
4002                     }
4003                     catch (Exception ex)
4004                     {
4005                         MessageBox.Show(ex.Message);
4006                     }
4007
4008                     try
4009                     {
4010                         InitDetailHtmlFormat();
4011                     }
4012                     catch (Exception ex)
4013                     {
4014                         ex.Data["Instance"] = "Font";
4015                         ex.Data["IsTerminatePermission"] = false;
4016                         throw;
4017                     }
4018
4019                     try
4020                     {
4021                         _statuses.SetUnreadManage(SettingDialog.UnreadManage);
4022                     }
4023                     catch (Exception ex)
4024                     {
4025                         ex.Data["Instance"] = "_statuses";
4026                         ex.Data["IsTerminatePermission"] = false;
4027                         throw;
4028                     }
4029
4030                     try
4031                     {
4032                         foreach (TabPage tb in ListTab.TabPages)
4033                         {
4034                             if (SettingDialog.TabIconDisp)
4035                             {
4036                                 if (_statuses.Tabs[tb.Text].UnreadCount == 0)
4037                                     tb.ImageIndex = -1;
4038                                 else
4039                                     tb.ImageIndex = 0;
4040                             }
4041                         }
4042                     }
4043                     catch (Exception ex)
4044                     {
4045                         ex.Data["Instance"] = "ListTab(TabIconDisp no2)";
4046                         ex.Data["IsTerminatePermission"] = false;
4047                         throw;
4048                     }
4049
4050                     try
4051                     {
4052                         var oldIconCol = _iconCol;
4053
4054                         if (this._cfgCommon.IconSize != oldIconSz)
4055                             ApplyListViewIconSize(this._cfgCommon.IconSize);
4056
4057                         foreach (TabPage tp in ListTab.TabPages)
4058                         {
4059                             DetailsListView lst = (DetailsListView)tp.Tag;
4060
4061                             using (ControlTransaction.Update(lst))
4062                             {
4063                                 lst.GridLines = this._cfgCommon.ShowGrid;
4064                                 lst.Font = _fntReaded;
4065                                 lst.BackColor = _clListBackcolor;
4066
4067                                 if (_iconCol != oldIconCol)
4068                                     ResetColumns(lst);
4069                             }
4070                         }
4071                     }
4072                     catch (Exception ex)
4073                     {
4074                         ex.Data["Instance"] = "ListView(IconSize)";
4075                         ex.Data["IsTerminatePermission"] = false;
4076                         throw;
4077                     }
4078
4079                     SetMainWindowTitle();
4080                     SetNotifyIconText();
4081
4082                     this.PurgeListViewItemCache();
4083                     if (_curList != null) _curList.Refresh();
4084                     ListTab.Refresh();
4085
4086                     _hookGlobalHotkey.UnregisterAllOriginalHotkey();
4087                     if (SettingDialog.HotkeyEnabled)
4088                     {
4089                         ///グローバルホットキーの登録。設定で変更可能にするかも
4090                         HookGlobalHotkey.ModKeys modKey = HookGlobalHotkey.ModKeys.None;
4091                         if ((SettingDialog.HotkeyMod & Keys.Alt) == Keys.Alt) modKey = modKey | HookGlobalHotkey.ModKeys.Alt;
4092                         if ((SettingDialog.HotkeyMod & Keys.Control) == Keys.Control) modKey = modKey | HookGlobalHotkey.ModKeys.Ctrl;
4093                         if ((SettingDialog.HotkeyMod & Keys.Shift) == Keys.Shift) modKey = modKey | HookGlobalHotkey.ModKeys.Shift;
4094                         if ((SettingDialog.HotkeyMod & Keys.LWin) == Keys.LWin) modKey = modKey | HookGlobalHotkey.ModKeys.Win;
4095
4096                         _hookGlobalHotkey.RegisterOriginalHotkey(SettingDialog.HotkeyKey, SettingDialog.HotkeyValue, modKey);
4097                     }
4098
4099                     if (uid != tw.Username) this.doGetFollowersMenu();
4100
4101                     if (SettingDialog.IsNotifyUseGrowl) gh.RegisterGrowl();
4102                     try
4103                     {
4104                         StatusText_TextChanged(null, null);
4105                     }
4106                     catch (Exception)
4107                     {
4108                     }
4109                 }
4110             }
4111
4112             Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4113
4114             this.TopMost = SettingDialog.AlwaysTop;
4115             SaveConfigsAll(false);
4116         }
4117
4118         /// <summary>
4119         /// タブの表示位置を設定する
4120         /// </summary>
4121         private void SetTabAlignment()
4122         {
4123             var newAlignment = this._cfgCommon.ViewTabBottom ? TabAlignment.Bottom : TabAlignment.Top;
4124             if (ListTab.Alignment == newAlignment) return;
4125
4126             //現在の選択状態を退避
4127             Dictionary<string, long[]> selId = new Dictionary<string, long[]>();
4128             Dictionary<string, long[]> focusedId = new Dictionary<string, long[]>();
4129             SaveSelectedStatus(selId, focusedId);
4130
4131             ListTab.Alignment = newAlignment;
4132
4133             //選択状態を復帰
4134             foreach (TabPage tab in ListTab.TabPages)
4135             {
4136                 DetailsListView lst = (DetailsListView)tab.Tag;
4137                 TabClass tabInfo = _statuses.Tabs[tab.Text];
4138                 using (ControlTransaction.Update(lst))
4139                 {
4140                     this.SelectListItem(lst,
4141                                         tabInfo.IndexOf(selId[tab.Text]),
4142                                         tabInfo.IndexOf(focusedId[tab.Text]));
4143                 }
4144             }
4145         }
4146
4147         private void ApplyListViewIconSize(MyCommon.IconSizes iconSz)
4148         {
4149             // アイコンサイズの再設定
4150             _iconCol = false;
4151             switch (iconSz)
4152             {
4153                 case MyCommon.IconSizes.IconNone:
4154                     _iconSz = 0;
4155                     break;
4156                 case MyCommon.IconSizes.Icon16:
4157                     _iconSz = 16;
4158                     break;
4159                 case MyCommon.IconSizes.Icon24:
4160                     _iconSz = 26;
4161                     break;
4162                 case MyCommon.IconSizes.Icon48:
4163                     _iconSz = 48;
4164                     break;
4165                 case MyCommon.IconSizes.Icon48_2:
4166                     _iconSz = 48;
4167                     _iconCol = true;
4168                     break;
4169             }
4170
4171             if (_iconSz > 0)
4172             {
4173                 // ディスプレイの DPI 設定を考慮したサイズを設定する
4174                 _listViewImageList.ImageSize = new Size(
4175                     1,
4176                     (int)Math.Ceiling(this._iconSz * this.currentScaleFactor.Height));
4177             }
4178             else
4179             {
4180                 _listViewImageList.ImageSize = new Size(1, 1);
4181             }
4182         }
4183
4184         private void ResetColumns(DetailsListView list)
4185         {
4186             using (ControlTransaction.Update(list))
4187             using (ControlTransaction.Layout(list, false))
4188             {
4189                 // カラムヘッダの再設定
4190                 list.ColumnClick -= MyList_ColumnClick;
4191                 list.DrawColumnHeader -= MyList_DrawColumnHeader;
4192                 list.ColumnReordered -= MyList_ColumnReordered;
4193                 list.ColumnWidthChanged -= MyList_ColumnWidthChanged;
4194
4195                 var cols = list.Columns.Cast<ColumnHeader>().ToList();
4196                 list.Columns.Clear();
4197                 cols.ForEach(col => col.Dispose());
4198                 cols.Clear();
4199
4200                 InitColumns(list, true);
4201
4202                 list.ColumnClick += MyList_ColumnClick;
4203                 list.DrawColumnHeader += MyList_DrawColumnHeader;
4204                 list.ColumnReordered += MyList_ColumnReordered;
4205                 list.ColumnWidthChanged += MyList_ColumnWidthChanged;
4206             }
4207         }
4208
4209         private void PostBrowser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
4210         {
4211             if (e.Url.AbsoluteUri != "about:blank")
4212             {
4213                 DispSelectedPost();
4214                 OpenUriAsync(e.Url.OriginalString);
4215             }
4216         }
4217
4218         private void PostBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
4219         {
4220             if (e.Url.Scheme == "data")
4221             {
4222                 StatusLabelUrl.Text = PostBrowser.StatusText.Replace("&", "&&");
4223             }
4224             else if (e.Url.AbsoluteUri != "about:blank")
4225             {
4226                 e.Cancel = true;
4227
4228                 if (e.Url.AbsoluteUri.StartsWith("http://twitter.com/search?q=%23") ||
4229                    e.Url.AbsoluteUri.StartsWith("https://twitter.com/search?q=%23"))
4230                 {
4231                     //ハッシュタグの場合は、タブで開く
4232                     string urlStr = Uri.UnescapeDataString(e.Url.AbsoluteUri);
4233                     int i = urlStr.IndexOf('#');
4234                     if (i == -1) return;
4235
4236                     string hash = urlStr.Substring(i);
4237                     HashSupl.AddItem(hash);
4238                     HashMgr.AddHashToHistory(hash.Trim(), false);
4239                     AddNewTabForSearch(hash);
4240                     return;
4241                 }
4242                 else
4243                 {
4244                     Match m = Regex.Match(e.Url.AbsoluteUri, "^https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)$");
4245                     if (m.Success && IsTwitterId(m.Result("${ScreenName}")))
4246                     {
4247                         // Ctrlを押しながらリンクをクリックした場合は設定と逆の動作をする
4248                         if (SettingDialog.OpenUserTimeline)
4249                         {
4250                             if (MyCommon.IsKeyDown(Keys.Control))
4251                                 OpenUriAsync(e.Url.OriginalString);
4252                             else
4253                                 this.AddNewTabForUserTimeline(m.Result("${ScreenName}"));
4254                         }
4255                         else
4256                         {
4257                             if (MyCommon.IsKeyDown(Keys.Control))
4258                                 this.AddNewTabForUserTimeline(m.Result("${ScreenName}"));
4259                             else
4260                                 OpenUriAsync(e.Url.OriginalString);
4261                         }
4262                     }
4263                     else
4264                     {
4265                         OpenUriAsync(e.Url.OriginalString);
4266                     }
4267                 }
4268             }
4269         }
4270
4271         public void AddNewTabForSearch(string searchWord)
4272         {
4273             //同一検索条件のタブが既に存在すれば、そのタブアクティブにして終了
4274             foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.PublicSearch))
4275             {
4276                 if (tb.SearchWords == searchWord && string.IsNullOrEmpty(tb.SearchLang))
4277                 {
4278                     foreach (TabPage tp in ListTab.TabPages)
4279                     {
4280                         if (tb.TabName == tp.Text)
4281                         {
4282                             ListTab.SelectedTab = tp;
4283                             return;
4284                         }
4285                     }
4286                 }
4287             }
4288             //ユニークなタブ名生成
4289             string tabName = searchWord;
4290             for (int i = 0; i <= 100; i++)
4291             {
4292                 if (_statuses.ContainsTab(tabName))
4293                     tabName += "_";
4294                 else
4295                     break;
4296             }
4297             //タブ追加
4298             _statuses.AddTab(tabName, MyCommon.TabUsageType.PublicSearch, null);
4299             AddNewTab(tabName, false, MyCommon.TabUsageType.PublicSearch);
4300             //追加したタブをアクティブに
4301             ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
4302             //検索条件の設定
4303             ComboBox cmb = (ComboBox)ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"];
4304             cmb.Items.Add(searchWord);
4305             cmb.Text = searchWord;
4306             SaveConfigsTabs();
4307             //検索実行
4308             this.SearchButton_Click(ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"], null);
4309         }
4310
4311         private void ShowUserTimeline()
4312         {
4313             if (!this.ExistCurrentPost) return;
4314             AddNewTabForUserTimeline(_curPost.ScreenName);
4315         }
4316
4317         private void SearchComboBox_KeyDown(object sender, KeyEventArgs e)
4318         {
4319             if (e.KeyCode == Keys.Escape)
4320             {
4321                 TabPage relTp = ListTab.SelectedTab;
4322                 RemoveSpecifiedTab(relTp.Text, false);
4323                 SaveConfigsTabs();
4324                 e.SuppressKeyPress = true;
4325             }
4326         }
4327
4328         public void AddNewTabForUserTimeline(string user)
4329         {
4330             //同一検索条件のタブが既に存在すれば、そのタブアクティブにして終了
4331             foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.UserTimeline))
4332             {
4333                 if (tb.User == user)
4334                 {
4335                     foreach (TabPage tp in ListTab.TabPages)
4336                     {
4337                         if (tb.TabName == tp.Text)
4338                         {
4339                             ListTab.SelectedTab = tp;
4340                             return;
4341                         }
4342                     }
4343                 }
4344             }
4345             //ユニークなタブ名生成
4346             string tabName = "user:" + user;
4347             while (_statuses.ContainsTab(tabName))
4348             {
4349                 tabName += "_";
4350             }
4351             //タブ追加
4352             _statuses.AddTab(tabName, MyCommon.TabUsageType.UserTimeline, null);
4353             _statuses.Tabs[tabName].User = user;
4354             AddNewTab(tabName, false, MyCommon.TabUsageType.UserTimeline);
4355             //追加したタブをアクティブに
4356             ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
4357             SaveConfigsTabs();
4358             //検索実行
4359
4360             GetTimeline(MyCommon.WORKERTYPE.UserTimeline, 1, tabName);
4361         }
4362
4363         public bool AddNewTab(string tabName, bool startup, MyCommon.TabUsageType tabType, ListElement listInfo = null)
4364         {
4365             //重複チェック
4366             foreach (TabPage tb in ListTab.TabPages)
4367             {
4368                 if (tb.Text == tabName) return false;
4369             }
4370
4371             //新規タブ名チェック
4372             if (tabName == Properties.Resources.AddNewTabText1) return false;
4373
4374             //タブタイプ重複チェック
4375             if (!startup)
4376             {
4377                 if (tabType == MyCommon.TabUsageType.DirectMessage ||
4378                    tabType == MyCommon.TabUsageType.Favorites ||
4379                    tabType == MyCommon.TabUsageType.Home ||
4380                    tabType == MyCommon.TabUsageType.Mentions ||
4381                    tabType == MyCommon.TabUsageType.Related)
4382                 {
4383                     if (_statuses.GetTabByType(tabType) != null) return false;
4384                 }
4385             }
4386
4387             TabPage _tabPage = new TabPage();
4388             DetailsListView _listCustom = new DetailsListView();
4389
4390             int cnt = ListTab.TabPages.Count;
4391
4392             ///ToDo:Create and set controls follow tabtypes
4393
4394             using (ControlTransaction.Update(_listCustom))
4395             using (ControlTransaction.Layout(this.SplitContainer1.Panel1, false))
4396             using (ControlTransaction.Layout(this.SplitContainer1.Panel2, false))
4397             using (ControlTransaction.Layout(this.SplitContainer1, false))
4398             using (ControlTransaction.Layout(this.ListTab, false))
4399             using (ControlTransaction.Layout(this))
4400             using (ControlTransaction.Layout(_tabPage, false))
4401             {
4402                 /// UserTimeline関連
4403                 Label label = null;
4404                 if (tabType == MyCommon.TabUsageType.UserTimeline || tabType == MyCommon.TabUsageType.Lists)
4405                 {
4406                     label = new Label();
4407                     label.Dock = DockStyle.Top;
4408                     label.Name = "labelUser";
4409                     if (tabType == MyCommon.TabUsageType.Lists)
4410                     {
4411                         label.Text = listInfo.ToString();
4412                     }
4413                     else
4414                     {
4415                         label.Text = _statuses.Tabs[tabName].User + "'s Timeline";
4416                     }
4417                     label.TextAlign = ContentAlignment.MiddleLeft;
4418                     using (ComboBox tmpComboBox = new ComboBox())
4419                     {
4420                         label.Height = tmpComboBox.Height;
4421                     }
4422                     _tabPage.Controls.Add(label);
4423                 }
4424
4425                 /// 検索関連の準備
4426                 Panel pnl = null;
4427                 if (tabType == MyCommon.TabUsageType.PublicSearch)
4428                 {
4429                     pnl = new Panel();
4430
4431                     Label lbl = new Label();
4432                     ComboBox cmb = new ComboBox();
4433                     Button btn = new Button();
4434                     ComboBox cmbLang = new ComboBox();
4435
4436                     pnl.SuspendLayout();
4437
4438                     pnl.Controls.Add(cmb);
4439                     pnl.Controls.Add(cmbLang);
4440                     pnl.Controls.Add(btn);
4441                     pnl.Controls.Add(lbl);
4442                     pnl.Name = "panelSearch";
4443                     pnl.Dock = DockStyle.Top;
4444                     pnl.Height = cmb.Height;
4445                     pnl.Enter += SearchControls_Enter;
4446                     pnl.Leave += SearchControls_Leave;
4447
4448                     cmb.Text = "";
4449                     cmb.Anchor = AnchorStyles.Left | AnchorStyles.Right;
4450                     cmb.Dock = DockStyle.Fill;
4451                     cmb.Name = "comboSearch";
4452                     cmb.DropDownStyle = ComboBoxStyle.DropDown;
4453                     cmb.ImeMode = ImeMode.NoControl;
4454                     cmb.TabStop = false;
4455                     cmb.AutoCompleteMode = AutoCompleteMode.None;
4456                     cmb.KeyDown += SearchComboBox_KeyDown;
4457
4458                     if (_statuses.ContainsTab(tabName))
4459                     {
4460                         cmb.Items.Add(_statuses.Tabs[tabName].SearchWords);
4461                         cmb.Text = _statuses.Tabs[tabName].SearchWords;
4462                     }
4463
4464                     cmbLang.Text = "";
4465                     cmbLang.Anchor = AnchorStyles.Left | AnchorStyles.Right;
4466                     cmbLang.Dock = DockStyle.Right;
4467                     cmbLang.Width = 50;
4468                     cmbLang.Name = "comboLang";
4469                     cmbLang.DropDownStyle = ComboBoxStyle.DropDownList;
4470                     cmbLang.TabStop = false;
4471                     cmbLang.Items.Add("");
4472                     cmbLang.Items.Add("ja");
4473                     cmbLang.Items.Add("en");
4474                     cmbLang.Items.Add("ar");
4475                     cmbLang.Items.Add("da");
4476                     cmbLang.Items.Add("nl");
4477                     cmbLang.Items.Add("fa");
4478                     cmbLang.Items.Add("fi");
4479                     cmbLang.Items.Add("fr");
4480                     cmbLang.Items.Add("de");
4481                     cmbLang.Items.Add("hu");
4482                     cmbLang.Items.Add("is");
4483                     cmbLang.Items.Add("it");
4484                     cmbLang.Items.Add("no");
4485                     cmbLang.Items.Add("pl");
4486                     cmbLang.Items.Add("pt");
4487                     cmbLang.Items.Add("ru");
4488                     cmbLang.Items.Add("es");
4489                     cmbLang.Items.Add("sv");
4490                     cmbLang.Items.Add("th");
4491                     if (_statuses.ContainsTab(tabName)) cmbLang.Text = _statuses.Tabs[tabName].SearchLang;
4492
4493                     lbl.Text = "Search(C-S-f)";
4494                     lbl.Name = "label1";
4495                     lbl.Dock = DockStyle.Left;
4496                     lbl.Width = 90;
4497                     lbl.Height = cmb.Height;
4498                     lbl.TextAlign = ContentAlignment.MiddleLeft;
4499
4500                     btn.Text = "Search";
4501                     btn.Name = "buttonSearch";
4502                     btn.UseVisualStyleBackColor = true;
4503                     btn.Dock = DockStyle.Right;
4504                     btn.TabStop = false;
4505                     btn.Click += SearchButton_Click;
4506                 }
4507
4508                 this.ListTab.Controls.Add(_tabPage);
4509                 _tabPage.Controls.Add(_listCustom);
4510
4511                 if (tabType == MyCommon.TabUsageType.PublicSearch) _tabPage.Controls.Add(pnl);
4512                 if (tabType == MyCommon.TabUsageType.UserTimeline || tabType == MyCommon.TabUsageType.Lists) _tabPage.Controls.Add(label);
4513
4514                 _tabPage.Location = new Point(4, 4);
4515                 _tabPage.Name = "CTab" + cnt.ToString();
4516                 _tabPage.Size = new Size(380, 260);
4517                 _tabPage.TabIndex = 2 + cnt;
4518                 _tabPage.Text = tabName;
4519                 _tabPage.UseVisualStyleBackColor = true;
4520
4521                 _listCustom.AllowColumnReorder = true;
4522                 _listCustom.ContextMenuStrip = this.ContextMenuOperate;
4523                 _listCustom.ColumnHeaderContextMenuStrip = this.ContextMenuColumnHeader;
4524                 _listCustom.Dock = DockStyle.Fill;
4525                 _listCustom.FullRowSelect = true;
4526                 _listCustom.HideSelection = false;
4527                 _listCustom.Location = new Point(0, 0);
4528                 _listCustom.Margin = new Padding(0);
4529                 _listCustom.Name = "CList" + Environment.TickCount.ToString();
4530                 _listCustom.ShowItemToolTips = true;
4531                 _listCustom.Size = new Size(380, 260);
4532                 _listCustom.UseCompatibleStateImageBehavior = false;
4533                 _listCustom.View = View.Details;
4534                 _listCustom.OwnerDraw = true;
4535                 _listCustom.VirtualMode = true;
4536                 _listCustom.Font = _fntReaded;
4537                 _listCustom.BackColor = _clListBackcolor;
4538
4539                 _listCustom.GridLines = this._cfgCommon.ShowGrid;
4540                 _listCustom.AllowDrop = true;
4541
4542                 _listCustom.SmallImageList = _listViewImageList;
4543
4544                 InitColumns(_listCustom, startup);
4545
4546                 _listCustom.SelectedIndexChanged += MyList_SelectedIndexChanged;
4547                 _listCustom.MouseDoubleClick += MyList_MouseDoubleClick;
4548                 _listCustom.ColumnClick += MyList_ColumnClick;
4549                 _listCustom.DrawColumnHeader += MyList_DrawColumnHeader;
4550                 _listCustom.DragDrop += TweenMain_DragDrop;
4551                 _listCustom.DragEnter += TweenMain_DragEnter;
4552                 _listCustom.DragOver += TweenMain_DragOver;
4553                 _listCustom.DrawItem += MyList_DrawItem;
4554                 _listCustom.MouseClick += MyList_MouseClick;
4555                 _listCustom.ColumnReordered += MyList_ColumnReordered;
4556                 _listCustom.ColumnWidthChanged += MyList_ColumnWidthChanged;
4557                 _listCustom.CacheVirtualItems += MyList_CacheVirtualItems;
4558                 _listCustom.RetrieveVirtualItem += MyList_RetrieveVirtualItem;
4559                 _listCustom.DrawSubItem += MyList_DrawSubItem;
4560                 _listCustom.HScrolled += MyList_HScrolled;
4561
4562                 if (tabType == MyCommon.TabUsageType.PublicSearch) pnl.ResumeLayout(false);
4563             }
4564
4565             _tabPage.Tag = _listCustom;
4566             return true;
4567         }
4568
4569         public bool RemoveSpecifiedTab(string TabName, bool confirm)
4570         {
4571             if (_statuses.IsDefaultTab(TabName) || _statuses.Tabs[TabName].Protected) return false;
4572
4573             if (confirm)
4574             {
4575                 string tmp = string.Format(Properties.Resources.RemoveSpecifiedTabText1, Environment.NewLine);
4576                 if (MessageBox.Show(tmp, TabName + " " + Properties.Resources.RemoveSpecifiedTabText2,
4577                                  MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Cancel)
4578                 {
4579                     return false;
4580                 }
4581             }
4582
4583             var _tabPage = ListTab.TabPages.Cast<TabPage>().FirstOrDefault<TabPage>(tp => tp.Text == TabName);
4584             if (_tabPage == null) return false;
4585
4586             SetListProperty();   //他のタブに列幅等を反映
4587
4588             MyCommon.TabUsageType tabType = _statuses.Tabs[TabName].TabType;
4589
4590             //オブジェクトインスタンスの削除
4591             DetailsListView _listCustom = (DetailsListView)_tabPage.Tag;
4592             _tabPage.Tag = null;
4593
4594             using (ControlTransaction.Layout(this.SplitContainer1.Panel1, false))
4595             using (ControlTransaction.Layout(this.SplitContainer1.Panel2, false))
4596             using (ControlTransaction.Layout(this.SplitContainer1, false))
4597             using (ControlTransaction.Layout(this.ListTab, false))
4598             using (ControlTransaction.Layout(this))
4599             using (ControlTransaction.Layout(_tabPage, false))
4600             {
4601                 if (this.ListTab.SelectedTab == _tabPage)
4602                 {
4603                     this.ListTab.SelectTab((this._beforeSelectedTab != null && this.ListTab.TabPages.Contains(this._beforeSelectedTab)) ? this._beforeSelectedTab : this.ListTab.TabPages[0]);
4604                     this._beforeSelectedTab = null;
4605                 }
4606                 this.ListTab.Controls.Remove(_tabPage);
4607
4608                 // 後付けのコントロールを破棄
4609                 if (tabType == MyCommon.TabUsageType.UserTimeline || tabType == MyCommon.TabUsageType.Lists)
4610                 {
4611                     using (Control label = _tabPage.Controls["labelUser"])
4612                     {
4613                         _tabPage.Controls.Remove(label);
4614                     }
4615                 }
4616                 else if (tabType == MyCommon.TabUsageType.PublicSearch)
4617                 {
4618                     using (Control pnl = _tabPage.Controls["panelSearch"])
4619                     {
4620                         pnl.Enter -= SearchControls_Enter;
4621                         pnl.Leave -= SearchControls_Leave;
4622                         _tabPage.Controls.Remove(pnl);
4623
4624                         foreach (Control ctrl in pnl.Controls)
4625                         {
4626                             if (ctrl.Name == "buttonSearch")
4627                             {
4628                                 ctrl.Click -= SearchButton_Click;
4629                             }
4630                             else if (ctrl.Name == "comboSearch")
4631                             {
4632                                 ctrl.KeyDown -= SearchComboBox_KeyDown;
4633                             }
4634                             pnl.Controls.Remove(ctrl);
4635                             ctrl.Dispose();
4636                         }
4637                     }
4638                 }
4639
4640                 _tabPage.Controls.Remove(_listCustom);
4641
4642                 _listCustom.SelectedIndexChanged -= MyList_SelectedIndexChanged;
4643                 _listCustom.MouseDoubleClick -= MyList_MouseDoubleClick;
4644                 _listCustom.ColumnClick -= MyList_ColumnClick;
4645                 _listCustom.DrawColumnHeader -= MyList_DrawColumnHeader;
4646                 _listCustom.DragDrop -= TweenMain_DragDrop;
4647                 _listCustom.DragEnter -= TweenMain_DragEnter;
4648                 _listCustom.DragOver -= TweenMain_DragOver;
4649                 _listCustom.DrawItem -= MyList_DrawItem;
4650                 _listCustom.MouseClick -= MyList_MouseClick;
4651                 _listCustom.ColumnReordered -= MyList_ColumnReordered;
4652                 _listCustom.ColumnWidthChanged -= MyList_ColumnWidthChanged;
4653                 _listCustom.CacheVirtualItems -= MyList_CacheVirtualItems;
4654                 _listCustom.RetrieveVirtualItem -= MyList_RetrieveVirtualItem;
4655                 _listCustom.DrawSubItem -= MyList_DrawSubItem;
4656                 _listCustom.HScrolled -= MyList_HScrolled;
4657
4658                 var cols = _listCustom.Columns.Cast<ColumnHeader>().ToList<ColumnHeader>();
4659                 _listCustom.Columns.Clear();
4660                 cols.ForEach(col => col.Dispose());
4661                 cols.Clear();
4662
4663                 _listCustom.ContextMenuStrip = null;
4664                 _listCustom.ColumnHeaderContextMenuStrip = null;
4665                 _listCustom.Font = null;
4666
4667                 _listCustom.SmallImageList = null;
4668                 _listCustom.ListViewItemSorter = null;
4669
4670                 //キャッシュのクリア
4671                 if (_curTab.Equals(_tabPage))
4672                 {
4673                     _curTab = null;
4674                     _curItemIndex = -1;
4675                     _curList = null;
4676                     _curPost = null;
4677                 }
4678                 this.PurgeListViewItemCache();
4679             }
4680
4681             _tabPage.Dispose();
4682             _listCustom.Dispose();
4683             _statuses.RemoveTab(TabName);
4684
4685             foreach (TabPage tp in ListTab.TabPages)
4686             {
4687                 DetailsListView lst = (DetailsListView)tp.Tag;
4688                 var count = _statuses.Tabs[tp.Text].AllCount;
4689                 if (lst.VirtualListSize != count)
4690                 {
4691                     lst.VirtualListSize = count;
4692                 }
4693             }
4694
4695             return true;
4696         }
4697
4698         private void ListTab_Deselected(object sender, TabControlEventArgs e)
4699         {
4700             this.PurgeListViewItemCache();
4701             _beforeSelectedTab = e.TabPage;
4702         }
4703
4704         private void ListTab_MouseMove(object sender, MouseEventArgs e)
4705         {
4706             //タブのD&D
4707
4708             if (!SettingDialog.TabMouseLock && e.Button == MouseButtons.Left && _tabDrag)
4709             {
4710                 string tn = "";
4711                 Rectangle dragEnableRectangle = new Rectangle((int)(_tabMouseDownPoint.X - (SystemInformation.DragSize.Width / 2)), (int)(_tabMouseDownPoint.Y - (SystemInformation.DragSize.Height / 2)), SystemInformation.DragSize.Width, SystemInformation.DragSize.Height);
4712                 if (!dragEnableRectangle.Contains(e.Location))
4713                 {
4714                     //タブが多段の場合にはMouseDownの前の段階で選択されたタブの段が変わっているので、このタイミングでカーソルの位置からタブを判定出来ない。
4715                     tn = ListTab.SelectedTab.Text;
4716                 }
4717
4718                 if (string.IsNullOrEmpty(tn)) return;
4719
4720                 foreach (TabPage tb in ListTab.TabPages)
4721                 {
4722                     if (tb.Text == tn)
4723                     {
4724                         ListTab.DoDragDrop(tb, DragDropEffects.All);
4725                         break;
4726                     }
4727                 }
4728             }
4729             else
4730             {
4731                 _tabDrag = false;
4732             }
4733
4734             Point cpos = new Point(e.X, e.Y);
4735             for (int i = 0; i < ListTab.TabPages.Count; i++)
4736             {
4737                 Rectangle rect = ListTab.GetTabRect(i);
4738                 if (rect.Left <= cpos.X & cpos.X <= rect.Right &
4739                    rect.Top <= cpos.Y & cpos.Y <= rect.Bottom)
4740                 {
4741                     _rclickTabName = ListTab.TabPages[i].Text;
4742                     break;
4743                 }
4744             }
4745         }
4746
4747         private void ListTab_SelectedIndexChanged(object sender, EventArgs e)
4748         {
4749             //_curList.Refresh();
4750             DispSelectedPost();
4751             SetMainWindowTitle();
4752             SetStatusLabelUrl();
4753             if (ListTab.Focused || ((Control)ListTab.SelectedTab.Tag).Focused) this.Tag = ListTab.Tag;
4754             TabMenuControl(ListTab.SelectedTab.Text);
4755             this.PushSelectPostChain();
4756         }
4757
4758         private void SetListProperty()
4759         {
4760             //削除などで見つからない場合は処理せず
4761             if (_curList == null) return;
4762             if (!_isColumnChanged) return;
4763
4764             int[] dispOrder = new int[_curList.Columns.Count];
4765             for (int i = 0; i < _curList.Columns.Count; i++)
4766             {
4767                 for (int j = 0; j < _curList.Columns.Count; j++)
4768                 {
4769                     if (_curList.Columns[j].DisplayIndex == i)
4770                     {
4771                         dispOrder[i] = j;
4772                         break;
4773                     }
4774                 }
4775             }
4776
4777             //列幅、列並びを他のタブに設定
4778             foreach (TabPage tb in ListTab.TabPages)
4779             {
4780                 if (!tb.Equals(_curTab))
4781                 {
4782                     if (tb.Tag != null && tb.Controls.Count > 0)
4783                     {
4784                         DetailsListView lst = (DetailsListView)tb.Tag;
4785                         for (int i = 0; i < lst.Columns.Count; i++)
4786                         {
4787                             lst.Columns[dispOrder[i]].DisplayIndex = i;
4788                             lst.Columns[i].Width = _curList.Columns[i].Width;
4789                         }
4790                     }
4791                 }
4792             }
4793
4794             _isColumnChanged = false;
4795         }
4796
4797         private void PostBrowser_StatusTextChanged(object sender, EventArgs e)
4798         {
4799             try
4800             {
4801                 if (PostBrowser.StatusText.StartsWith("http") || PostBrowser.StatusText.StartsWith("ftp")
4802                         || PostBrowser.StatusText.StartsWith("data"))
4803                 {
4804                     StatusLabelUrl.Text = PostBrowser.StatusText.Replace("&", "&&");
4805                 }
4806                 if (string.IsNullOrEmpty(PostBrowser.StatusText))
4807                 {
4808                     SetStatusLabelUrl();
4809                 }
4810             }
4811             catch (Exception)
4812             {
4813             }
4814         }
4815
4816         private void StatusText_KeyPress(object sender, KeyPressEventArgs e)
4817         {
4818             if (e.KeyChar == '@')
4819             {
4820                 if (!this._cfgCommon.UseAtIdSupplement) return;
4821                 //@マーク
4822                 int cnt = AtIdSupl.ItemCount;
4823                 ShowSuplDialog(StatusText, AtIdSupl);
4824                 if (cnt != AtIdSupl.ItemCount) _modifySettingAtId = true;
4825                 e.Handled = true;
4826             }
4827             else if (e.KeyChar == '#')
4828             {
4829                 if (!this._cfgCommon.UseHashSupplement) return;
4830                 ShowSuplDialog(StatusText, HashSupl);
4831                 e.Handled = true;
4832             }
4833         }
4834
4835         public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog)
4836         {
4837             ShowSuplDialog(owner, dialog, 0, "");
4838         }
4839
4840         public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog, int offset)
4841         {
4842             ShowSuplDialog(owner, dialog, offset, "");
4843         }
4844
4845         public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog, int offset, string startswith)
4846         {
4847             dialog.StartsWith = startswith;
4848             if (dialog.Visible)
4849             {
4850                 dialog.Focus();
4851             }
4852             else
4853             {
4854                 dialog.ShowDialog();
4855             }
4856             this.TopMost = SettingDialog.AlwaysTop;
4857             int selStart = owner.SelectionStart;
4858             string fHalf = "";
4859             string eHalf = "";
4860             if (dialog.DialogResult == DialogResult.OK)
4861             {
4862                 if (!string.IsNullOrEmpty(dialog.inputText))
4863                 {
4864                     if (selStart > 0)
4865                     {
4866                         fHalf = owner.Text.Substring(0, selStart - offset);
4867                     }
4868                     if (selStart < owner.Text.Length)
4869                     {
4870                         eHalf = owner.Text.Substring(selStart);
4871                     }
4872                     owner.Text = fHalf + dialog.inputText + eHalf;
4873                     owner.SelectionStart = selStart + dialog.inputText.Length;
4874                 }
4875             }
4876             else
4877             {
4878                 if (selStart > 0)
4879                 {
4880                     fHalf = owner.Text.Substring(0, selStart);
4881                 }
4882                 if (selStart < owner.Text.Length)
4883                 {
4884                     eHalf = owner.Text.Substring(selStart);
4885                 }
4886                 owner.Text = fHalf + eHalf;
4887                 if (selStart > 0)
4888                 {
4889                     owner.SelectionStart = selStart;
4890                 }
4891             }
4892             owner.Focus();
4893         }
4894
4895         private void StatusText_KeyUp(object sender, KeyEventArgs e)
4896         {
4897             //スペースキーで未読ジャンプ
4898             if (!e.Alt && !e.Control && !e.Shift)
4899             {
4900                 if (e.KeyCode == Keys.Space || e.KeyCode == Keys.ProcessKey)
4901                 {
4902                     bool isSpace = false;
4903                     foreach (char c in StatusText.Text.ToCharArray())
4904                     {
4905                         if (c == ' ' || c == ' ')
4906                         {
4907                             isSpace = true;
4908                         }
4909                         else
4910                         {
4911                             isSpace = false;
4912                             break;
4913                         }
4914                     }
4915                     if (isSpace)
4916                     {
4917                         e.Handled = true;
4918                         StatusText.Text = "";
4919                         JumpUnreadMenuItem_Click(null, null);
4920                     }
4921                 }
4922             }
4923             this.StatusText_TextChanged(null, null);
4924         }
4925
4926         private void StatusText_TextChanged(object sender, EventArgs e)
4927         {
4928             //文字数カウント
4929             int pLen = GetRestStatusCount(true, false);
4930             lblLen.Text = pLen.ToString();
4931             if (pLen < 0)
4932             {
4933                 StatusText.ForeColor = Color.Red;
4934             }
4935             else
4936             {
4937                 StatusText.ForeColor = _clInputFont;
4938             }
4939             if (string.IsNullOrEmpty(StatusText.Text))
4940             {
4941                 _reply_to_id = null;
4942                 _reply_to_name = null;
4943             }
4944         }
4945
4946         private int GetRestStatusCount(bool isAuto, bool isAddFooter)
4947         {
4948             //文字数カウント
4949             var statusText = this.StatusText.Text;
4950             statusText = statusText.Replace("\r\n", "\n");
4951
4952             int pLen = 140 - statusText.Length;
4953             if (this.NotifyIcon1 == null || !this.NotifyIcon1.Visible) return pLen;
4954             if ((isAuto && !MyCommon.IsKeyDown(Keys.Control) && this._cfgCommon.PostShiftEnter) ||
4955                 (isAuto && !MyCommon.IsKeyDown(Keys.Shift) && !this._cfgCommon.PostShiftEnter) ||
4956                 (!isAuto && isAddFooter))
4957             {
4958                 if (this._cfgLocal.UseRecommendStatus)
4959                     pLen -= SettingDialog.RecommendStatusText.Length;
4960                 else if (this._cfgLocal.StatusText.Length > 0)
4961                     pLen -= this._cfgLocal.StatusText.Length + 1;
4962             }
4963             if (!string.IsNullOrEmpty(HashMgr.UseHash))
4964             {
4965                 pLen -= HashMgr.UseHash.Length + 1;
4966             }
4967             //foreach (Match m in Regex.Matches(statusText, "https?:\/\/[-_.!~*//()a-zA-Z0-9;\/?:\@&=+\$,%#^]+"))
4968             //{
4969             //    pLen += m.Length - SettingDialog.TwitterConfiguration.ShortUrlLength;
4970             //}
4971             foreach (Match m in Regex.Matches(statusText, Twitter.rgUrl, RegexOptions.IgnoreCase))
4972             {
4973                 string before = m.Result("${before}");
4974                 string url = m.Result("${url}");
4975                 string protocol = m.Result("${protocol}");
4976                 string domain = m.Result("${domain}");
4977                 string path = m.Result("${path}");
4978                 if (protocol.Length == 0)
4979                 {
4980                     if (Regex.IsMatch(before, Twitter.url_invalid_without_protocol_preceding_chars))
4981                     {
4982                         continue;
4983                     }
4984
4985                     bool last_url_invalid_match = false;
4986                     string lasturl = null;
4987                     foreach (Match mm in Regex.Matches(domain, Twitter.url_valid_ascii_domain, RegexOptions.IgnoreCase))
4988                     {
4989                         lasturl = mm.ToString();
4990                         last_url_invalid_match = Regex.IsMatch(lasturl, Twitter.url_invalid_short_domain, RegexOptions.IgnoreCase);
4991                         if (!last_url_invalid_match)
4992                         {
4993                             pLen += lasturl.Length - SettingDialog.TwitterConfiguration.ShortUrlLength;
4994                         }
4995                     }
4996
4997                     if (path.Length != 0)
4998                     {
4999                         if (last_url_invalid_match)
5000                         {
5001                             pLen += lasturl.Length - SettingDialog.TwitterConfiguration.ShortUrlLength;
5002                         }
5003                         pLen += path.Length;
5004                     }
5005                 }
5006                 else
5007                 {
5008                     int shortUrlLength = protocol == "https://"
5009                         ? SettingDialog.TwitterConfiguration.ShortUrlLengthHttps
5010                         : SettingDialog.TwitterConfiguration.ShortUrlLength;
5011
5012                     pLen += url.Length - shortUrlLength;
5013                 }
5014                 
5015                 //if (m.Result("${url}").Length > SettingDialog.TwitterConfiguration.ShortUrlLength)
5016                 //{
5017                 //    pLen += m.Result("${url}").Length - SettingDialog.TwitterConfiguration.ShortUrlLength;
5018                 //}
5019             }
5020             if (ImageSelector.Visible && !string.IsNullOrEmpty(ImageSelector.ServiceName))
5021             {
5022                 pLen -= SettingDialog.TwitterConfiguration.CharactersReservedPerMedia;
5023             }
5024             return pLen;
5025         }
5026
5027         private void MyList_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e)
5028         {
5029             this.itemCacheLock.EnterUpgradeableReadLock();
5030             try
5031             {
5032                 if (_curList.Equals(sender))
5033                 {
5034                     if (_itemCache != null &&
5035                        e.StartIndex >= _itemCacheIndex &&
5036                        e.EndIndex < _itemCacheIndex + _itemCache.Length)
5037                     {
5038                         //If the newly requested cache is a subset of the old cache, 
5039                         //no need to rebuild everything, so do nothing.
5040                         return;
5041                     }
5042
5043                     //Now we need to rebuild the cache.
5044                     CreateCache(e.StartIndex, e.EndIndex);
5045                 }
5046             }
5047             finally { this.itemCacheLock.ExitUpgradeableReadLock(); }
5048         }
5049
5050         private async void MyList_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
5051         {
5052             ListViewItem item = null;
5053             PostClass cacheItemPost = null;
5054
5055             if (_curList.Equals(sender))
5056                 this.TryGetListViewItemCache(e.ItemIndex, out item, out cacheItemPost);
5057
5058             if (item == null)
5059             {
5060                 //A cache miss, so create a new ListViewItem and pass it back.
5061                 TabPage tb = (TabPage)((DetailsListView)sender).Parent;
5062                 try
5063                 {
5064                     item = this.CreateItem(tb, _statuses[tb.Text, e.ItemIndex], e.ItemIndex);
5065                 }
5066                 catch (Exception)
5067                 {
5068                     //不正な要求に対する間に合わせの応答
5069                     string[] sitem = {"", "", "", "", "", "", "", ""};
5070                     item = new ImageListViewItem(sitem);
5071                 }
5072             }
5073
5074             // e.Item に値をセットする前に await しないこと
5075             e.Item = item;
5076
5077             await ((ImageListViewItem)item).GetImageAsync();
5078         }
5079
5080         private void CreateCache(int StartIndex, int EndIndex)
5081         {
5082             this.itemCacheLock.EnterWriteLock();
5083             try
5084             {
5085                 var tabInfo = _statuses.Tabs[_curTab.Text];
5086
5087                 //キャッシュ要求(要求範囲±30を作成)
5088                 StartIndex -= 30;
5089                 if (StartIndex < 0) StartIndex = 0;
5090                 EndIndex += 30;
5091                 if (EndIndex >= tabInfo.AllCount) EndIndex = tabInfo.AllCount - 1;
5092                 _postCache = tabInfo[StartIndex, EndIndex]; //配列で取得
5093                 _itemCacheIndex = StartIndex;
5094
5095                 _itemCache = new ListViewItem[0] {};
5096                 Array.Resize(ref _itemCache, _postCache.Length);
5097
5098                 for (int i = 0; i < _postCache.Length; i++)
5099                 {
5100                     _itemCache[i] = CreateItem(_curTab, _postCache[i], StartIndex + i);
5101                 }
5102             }
5103             catch (Exception)
5104             {
5105                 //キャッシュ要求が実データとずれるため(イベントの遅延?)
5106                 _postCache = null;
5107                 _itemCacheIndex = -1;
5108                 _itemCache = null;
5109             }
5110             finally { this.itemCacheLock.ExitWriteLock(); }
5111         }
5112
5113         /// <summary>
5114         /// DetailsListView のための ListViewItem のキャッシュを消去する
5115         /// </summary>
5116         private void PurgeListViewItemCache()
5117         {
5118             this.itemCacheLock.EnterWriteLock();
5119             try
5120             {
5121                 this._itemCache = null;
5122                 this._itemCacheIndex = -1;
5123                 this._postCache = null;
5124             }
5125             finally { this.itemCacheLock.ExitWriteLock(); }
5126         }
5127
5128         private bool TryGetListViewItemCache(int index, out ListViewItem item, out PostClass post)
5129         {
5130             this.itemCacheLock.EnterReadLock();
5131             try
5132             {
5133                 if (this._itemCache != null && index >= this._itemCacheIndex && index < this._itemCacheIndex + this._itemCache.Length)
5134                 {
5135                     item = this._itemCache[index - _itemCacheIndex];
5136                     post = this._postCache[index - _itemCacheIndex];
5137                     return true;
5138                 }
5139             }
5140             finally { this.itemCacheLock.ExitReadLock(); }
5141
5142             item = null;
5143             post = null;
5144             return false;
5145         }
5146
5147         private ListViewItem CreateItem(TabPage Tab, PostClass Post, int Index)
5148         {
5149             StringBuilder mk = new StringBuilder();
5150             //if (Post.IsDeleted) mk.Append("×");
5151             //if (Post.IsMark) mk.Append("♪");
5152             //if (Post.IsProtect) mk.Append("Ю");
5153             //if (Post.InReplyToStatusId != null) mk.Append("⇒");
5154             if (Post.FavoritedCount > 0) mk.Append("+" + Post.FavoritedCount.ToString());
5155             ImageListViewItem itm;
5156             if (Post.RetweetedId == null)
5157             {
5158                 string[] sitem= {"",
5159                                  Post.Nickname,
5160                                  Post.IsDeleted ? "(DELETED)" : Post.TextSingleLine,
5161                                  Post.CreatedAt.ToString(this._cfgCommon.DateTimeFormat),
5162                                  Post.ScreenName,
5163                                  "",
5164                                  mk.ToString(),
5165                                  Post.Source};
5166                 itm = new ImageListViewItem(sitem, this.IconCache, Post.ImageUrl);
5167             }
5168             else
5169             {
5170                 string[] sitem = {"",
5171                                   Post.Nickname,
5172                                   Post.IsDeleted ? "(DELETED)" : Post.TextSingleLine,
5173                                   Post.CreatedAt.ToString(this._cfgCommon.DateTimeFormat),
5174                                   Post.ScreenName + Environment.NewLine + "(RT:" + Post.RetweetedBy + ")",
5175                                   "",
5176                                   mk.ToString(),
5177                                   Post.Source};
5178                 itm = new ImageListViewItem(sitem, this.IconCache, Post.ImageUrl);
5179             }
5180             itm.StateIndex = Post.StateIndex;
5181
5182             bool read = Post.IsRead;
5183             //未読管理していなかったら既読として扱う
5184             if (!_statuses.Tabs[Tab.Text].UnreadManage || !SettingDialog.UnreadManage) read = true;
5185             ChangeItemStyleRead(read, itm, Post, null);
5186             if (Tab.Equals(_curTab)) ColorizeList(itm, Index);
5187             return itm;
5188         }
5189
5190         /// <summary>
5191         /// 全てのタブの振り分けルールを反映し直します
5192         /// </summary>
5193         private void ApplyPostFilters()
5194         {
5195             try
5196             {
5197                 this.Cursor = Cursors.WaitCursor;
5198
5199                 this.PurgeListViewItemCache();
5200                 this._curPost = null;
5201                 this._curItemIndex = -1;
5202                 this._statuses.FilterAll();
5203
5204                 foreach (TabPage tabPage in this.ListTab.TabPages)
5205                 {
5206                     var tab = this._statuses.Tabs[tabPage.Text];
5207
5208                     var listview = (DetailsListView)tabPage.Tag;
5209                     listview.VirtualListSize = tab.AllCount;
5210
5211                     if (this.SettingDialog.TabIconDisp)
5212                     {
5213                         if (tab.UnreadCount > 0)
5214                             tabPage.ImageIndex = 0;
5215                         else
5216                             tabPage.ImageIndex = -1;
5217                     }
5218                 }
5219
5220                 if (!this.SettingDialog.TabIconDisp)
5221                     this.ListTab.Refresh();
5222             }
5223             finally
5224             {
5225                 this.Cursor = Cursors.Default;
5226             }
5227         }
5228
5229         private void MyList_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
5230         {
5231             e.DrawDefault = true;
5232         }
5233
5234         private void MyList_HScrolled(object sender, EventArgs e)
5235         {
5236             DetailsListView listView = (DetailsListView)sender;
5237             listView.Refresh();
5238         }
5239
5240         private void MyList_DrawItem(object sender, DrawListViewItemEventArgs e)
5241         {
5242             if (e.State == 0) return;
5243             e.DrawDefault = false;
5244
5245             SolidBrush brs2 = null;
5246             if (!e.Item.Selected)     //e.ItemStateでうまく判定できない???
5247             {
5248                 if (e.Item.BackColor == _clSelf)
5249                     brs2 = _brsBackColorMine;
5250                 else if (e.Item.BackColor == _clAtSelf)
5251                     brs2 = _brsBackColorAt;
5252                 else if (e.Item.BackColor == _clTarget)
5253                     brs2 = _brsBackColorYou;
5254                 else if (e.Item.BackColor == _clAtTarget)
5255                     brs2 = _brsBackColorAtYou;
5256                 else if (e.Item.BackColor == _clAtFromTarget)
5257                     brs2 = _brsBackColorAtFromTarget;
5258                 else if (e.Item.BackColor == _clAtTo)
5259                     brs2 = _brsBackColorAtTo;
5260                 else
5261                     brs2 = _brsBackColorNone;
5262             }
5263             else
5264             {
5265                 //選択中の行
5266                 if (((Control)sender).Focused)
5267                     brs2 = _brsHighLight;
5268                 else
5269                     brs2 = _brsDeactiveSelection;
5270             }
5271             e.Graphics.FillRectangle(brs2, e.Bounds);
5272             e.DrawFocusRectangle();
5273             this.DrawListViewItemIcon(e);
5274         }
5275
5276         private void MyList_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
5277         {
5278             if (e.ItemState == 0) return;
5279
5280             if (e.ColumnIndex > 0)
5281             {
5282                 //アイコン以外の列
5283                 RectangleF rct = e.Bounds;
5284                 rct.Width = e.Header.Width;
5285                 int fontHeight = e.Item.Font.Height;
5286                 if (_iconCol)
5287                 {
5288                     rct.Y += fontHeight;
5289                     rct.Height -= fontHeight;
5290                 }
5291
5292                 int heightDiff;
5293                 int drawLineCount = Math.Max(1, Math.DivRem((int)rct.Height, fontHeight, out heightDiff));
5294
5295                 //if (heightDiff > fontHeight * 0.7)
5296                 //{
5297                 //    rct.Height += fontHeight;
5298                 //    drawLineCount += 1;
5299                 //}
5300
5301                 //フォントの高さの半分を足してるのは保険。無くてもいいかも。
5302                 if (!_iconCol && drawLineCount <= 1)
5303                 {
5304                     //rct.Inflate(0, heightDiff / -2);
5305                     //rct.Height += fontHeight / 2;
5306                 }
5307                 else if (heightDiff < fontHeight * 0.7)
5308                 {
5309                     //最終行が70%以上欠けていたら、最終行は表示しない
5310                     //rct.Height = (float)((fontHeight * drawLineCount) + (fontHeight / 2));
5311                     rct.Height = (fontHeight * drawLineCount) - 1;
5312                 }
5313                 else
5314                 {
5315                     drawLineCount += 1;
5316                 }
5317
5318                 //if (!_iconCol && drawLineCount > 1)
5319                 //{
5320                 //    rct.Y += fontHeight * 0.2;
5321                 //    if (heightDiff >= fontHeight * 0.8) rct.Height -= fontHeight * 0.2;
5322                 //}
5323
5324                 if (rct.Width > 0)
5325                 {
5326                     Color color = (!e.Item.Selected) ? e.Item.ForeColor :   //選択されていない行
5327                         (((Control)sender).Focused) ? _clHighLight :        //選択中の行
5328                         _clUnread;
5329
5330                     if (_iconCol)
5331                     {
5332                         Rectangle rctB = e.Bounds;
5333                         rctB.Width = e.Header.Width;
5334                         rctB.Height = fontHeight;
5335
5336                         using (Font fnt = new Font(e.Item.Font, FontStyle.Bold))
5337                         {
5338                             TextRenderer.DrawText(e.Graphics,
5339                                                     e.Item.SubItems[2].Text,
5340                                                     e.Item.Font,
5341                                                     Rectangle.Round(rct),
5342                                                     color,
5343                                                     TextFormatFlags.WordBreak |
5344                                                     TextFormatFlags.EndEllipsis |
5345                                                     TextFormatFlags.GlyphOverhangPadding |
5346                                                     TextFormatFlags.NoPrefix);
5347                             TextRenderer.DrawText(e.Graphics,
5348                                                     e.Item.SubItems[4].Text + " / " + e.Item.SubItems[1].Text + " (" + e.Item.SubItems[3].Text + ") " + e.Item.SubItems[5].Text + e.Item.SubItems[6].Text + " [" + e.Item.SubItems[7].Text + "]",
5349                                                     fnt,
5350                                                     rctB,
5351                                                     color,
5352                                                     TextFormatFlags.SingleLine |
5353                                                     TextFormatFlags.EndEllipsis |
5354                                                     TextFormatFlags.GlyphOverhangPadding |
5355                                                     TextFormatFlags.NoPrefix);
5356                         }
5357                     }
5358                     else if (drawLineCount == 1)
5359                     {
5360                         TextRenderer.DrawText(e.Graphics,
5361                                                 e.SubItem.Text,
5362                                                 e.Item.Font,
5363                                                 Rectangle.Round(rct),
5364                                                 color,
5365                                                 TextFormatFlags.SingleLine |
5366                                                 TextFormatFlags.EndEllipsis |
5367                                                 TextFormatFlags.GlyphOverhangPadding |
5368                                                 TextFormatFlags.NoPrefix |
5369                                                 TextFormatFlags.VerticalCenter);
5370                     }
5371                     else
5372                     {
5373                         TextRenderer.DrawText(e.Graphics,
5374                                                 e.SubItem.Text,
5375                                                 e.Item.Font,
5376                                                 Rectangle.Round(rct),
5377                                                 color,
5378                                                 TextFormatFlags.WordBreak |
5379                                                 TextFormatFlags.EndEllipsis |
5380                                                 TextFormatFlags.GlyphOverhangPadding |
5381                                                 TextFormatFlags.NoPrefix);
5382                     }
5383                     //if (e.ColumnIndex == 6) this.DrawListViewItemStateIcon(e, rct);
5384                 }
5385             }
5386         }
5387
5388         private void DrawListViewItemIcon(DrawListViewItemEventArgs e)
5389         {
5390             if (_iconSz == 0) return;
5391
5392             ImageListViewItem item = (ImageListViewItem)e.Item;
5393
5394             //e.Bounds.Leftが常に0を指すから自前で計算
5395             Rectangle itemRect = item.Bounds;
5396             var col0 = e.Item.ListView.Columns[0];
5397             itemRect.Width = col0.Width;
5398
5399             if (col0.DisplayIndex > 0)
5400             {
5401                 foreach (ColumnHeader clm in e.Item.ListView.Columns)
5402                 {
5403                     if (clm.DisplayIndex < col0.DisplayIndex)
5404                         itemRect.X += clm.Width;
5405                 }
5406             }
5407
5408             // ディスプレイの DPI 設定を考慮したアイコンサイズ
5409             var realIconSize = new SizeF(this._iconSz * this.currentScaleFactor.Width, this._iconSz * this.currentScaleFactor.Height).ToSize();
5410             var realStateSize = new SizeF(16 * this.currentScaleFactor.Width, 16 * this.currentScaleFactor.Height).ToSize();
5411
5412             Rectangle iconRect;
5413             var img = item.Image;
5414             if (img != null)
5415             {
5416                 iconRect = Rectangle.Intersect(new Rectangle(e.Item.GetBounds(ItemBoundsPortion.Icon).Location, realIconSize), itemRect);
5417                 iconRect.Offset(0, Math.Max(0, (itemRect.Height - realIconSize.Height) / 2));
5418
5419                 if (iconRect.Width > 0)
5420                 {
5421                     e.Graphics.FillRectangle(Brushes.White, iconRect);
5422                     e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
5423                     try
5424                     {
5425                         e.Graphics.DrawImage(img.Image, iconRect);
5426                     }
5427                     catch (ArgumentException)
5428                     {
5429                         item.RefreshImageAsync();
5430                     }
5431                 }
5432             }
5433             else
5434             {
5435                 iconRect = Rectangle.Intersect(new Rectangle(e.Item.GetBounds(ItemBoundsPortion.Icon).Location, new Size(1, 1)), itemRect);
5436                 //iconRect.Offset(0, Math.Max(0, (itemRect.Height - realIconSize.Height) / 2));
5437             }
5438
5439             if (item.StateIndex > -1)
5440             {
5441                 Rectangle stateRect = Rectangle.Intersect(new Rectangle(new Point(iconRect.X + realIconSize.Width + 2, iconRect.Y), realStateSize), itemRect);
5442                 if (stateRect.Width > 0)
5443                 {
5444                     //e.Graphics.FillRectangle(Brushes.White, stateRect);
5445                     //e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.High;
5446                     e.Graphics.DrawImage(this.PostStateImageList.Images[item.StateIndex], stateRect);
5447                 }
5448             }
5449         }
5450
5451         protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
5452         {
5453             base.ScaleControl(factor, specified);
5454
5455             const int baseDpi = 96;
5456
5457             // デザイン時の DPI (96dpi) との比を更新する
5458             this.currentScaleFactor = new SizeF(
5459                 this.CurrentAutoScaleDimensions.Width / baseDpi,
5460                 this.CurrentAutoScaleDimensions.Height / baseDpi);
5461         }
5462
5463         //private void DrawListViewItemStateIcon(DrawListViewSubItemEventArgs e, RectangleF rct)
5464         //{
5465         //    ImageListViewItem item = (ImageListViewItem)e.Item;
5466         //    if (item.StateImageIndex > -1)
5467         //    {
5468         //        ////e.Bounds.Leftが常に0を指すから自前で計算
5469         //        //Rectangle itemRect = item.Bounds;
5470         //        //itemRect.Width = e.Item.ListView.Columns[4].Width;
5471
5472         //        //foreach (ColumnHeader clm in e.Item.ListView.Columns)
5473         //        //{
5474         //        //    if (clm.DisplayIndex < e.Item.ListView.Columns[4].DisplayIndex)
5475         //        //    {
5476         //        //        itemRect.X += clm.Width;
5477         //        //    }
5478         //        //}
5479
5480         //        //Rectangle iconRect = Rectangle.Intersect(new Rectangle(e.Item.GetBounds(ItemBoundsPortion.Icon).Location, new Size(_iconSz, _iconSz)), itemRect);
5481         //        //iconRect.Offset(0, Math.Max(0, (itemRect.Height - _iconSz) / 2));
5482
5483         //        if (rct.Width > 0)
5484         //        {
5485         //            RectangleF stateRect = RectangleF.Intersect(rct, new RectangleF(rct.Location, new Size(18, 16)));
5486         //            //e.Graphics.FillRectangle(Brushes.White, rct);
5487         //            //e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.High;
5488         //            e.Graphics.DrawImage(this.PostStateImageList.Images(item.StateImageIndex), stateRect);
5489         //        }
5490         //    }
5491         //}
5492
5493         private void DoTabSearch(string _word,
5494                                  bool CaseSensitive,
5495                                  bool UseRegex,
5496                                  SEARCHTYPE SType)
5497         {
5498             int cidx = 0;
5499             bool fnd = false;
5500             int toIdx;
5501             int stp = 1;
5502
5503             if (_curList.VirtualListSize == 0)
5504             {
5505                 MessageBox.Show(Properties.Resources.DoTabSearchText2, Properties.Resources.DoTabSearchText3, MessageBoxButtons.OK, MessageBoxIcon.Information);
5506             }
5507
5508             if (_curList.SelectedIndices.Count > 0)
5509             {
5510                 cidx = _curList.SelectedIndices[0];
5511             }
5512             toIdx = _curList.VirtualListSize;
5513
5514             switch (SType)
5515             {
5516                 case SEARCHTYPE.DialogSearch:    //ダイアログからの検索
5517                     if (_curList.SelectedIndices.Count > 0)
5518                         cidx = _curList.SelectedIndices[0];
5519                     else
5520                         cidx = 0;
5521                     break;
5522                 case SEARCHTYPE.NextSearch:      //次を検索
5523                     if (_curList.SelectedIndices.Count > 0)
5524                     {
5525                         cidx = _curList.SelectedIndices[0] + 1;
5526                         if (cidx > toIdx) cidx = toIdx;
5527                     }
5528                     else
5529                     {
5530                         cidx = 0;
5531                     }
5532                     break;
5533                 case SEARCHTYPE.PrevSearch:      //前を検索
5534                     if (_curList.SelectedIndices.Count > 0)
5535                     {
5536                         cidx = _curList.SelectedIndices[0] - 1;
5537                         if (cidx < 0) cidx = 0;
5538                     }
5539                     else
5540                     {
5541                         cidx = toIdx;
5542                     }
5543                     toIdx = -1;
5544                     stp = -1;
5545                     break;
5546             }
5547
5548             RegexOptions regOpt = RegexOptions.None;
5549             StringComparison fndOpt = StringComparison.Ordinal;
5550             if (!CaseSensitive)
5551             {
5552                 regOpt = RegexOptions.IgnoreCase;
5553                 fndOpt = StringComparison.OrdinalIgnoreCase;
5554             }
5555             try
5556             {
5557     RETRY:
5558                 if (UseRegex)
5559                 {
5560                     // 正規表現検索
5561                     Regex _search;
5562                     try
5563                     {
5564                         _search = new Regex(_word, regOpt);
5565                         for (int idx = cidx; idx != toIdx; idx += stp)
5566                         {
5567                             PostClass post;
5568                             try
5569                             {
5570                                 post = _statuses[_curTab.Text, idx];
5571                             }
5572                             catch (Exception)
5573                             {
5574                                 continue;
5575                             }
5576                             if (_search.IsMatch(post.Nickname)
5577                                 || _search.IsMatch(post.TextFromApi)
5578                                 || _search.IsMatch(post.ScreenName))
5579                             {
5580                                 SelectListItem(_curList, idx);
5581                                 _curList.EnsureVisible(idx);
5582                                 return;
5583                             }
5584                         }
5585                     }
5586                     catch (ArgumentException)
5587                     {
5588                         MessageBox.Show(Properties.Resources.DoTabSearchText1, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
5589                         return;
5590                     }
5591                 }
5592                 else
5593                 {
5594                     // 通常検索
5595                     for (int idx = cidx; idx != toIdx; idx += stp)
5596                     {
5597                         PostClass post;
5598                         try
5599                         {
5600                             post = _statuses[_curTab.Text, idx];
5601                         }
5602                         catch (Exception)
5603                         {
5604                             continue;
5605                         }
5606                         if (post.Nickname.IndexOf(_word, fndOpt) > -1
5607                             || post.TextFromApi.IndexOf(_word, fndOpt) > -1
5608                             || post.ScreenName.IndexOf(_word, fndOpt) > -1)
5609                         {
5610                             SelectListItem(_curList, idx);
5611                             _curList.EnsureVisible(idx);
5612                             return;
5613                         }
5614                     }
5615                 }
5616
5617                 if (!fnd)
5618                 {
5619                     switch (SType)
5620                     {
5621                         case SEARCHTYPE.DialogSearch:
5622                         case SEARCHTYPE.NextSearch:
5623                             toIdx = cidx;
5624                             cidx = 0;
5625                             break;
5626                         case SEARCHTYPE.PrevSearch:
5627                             toIdx = cidx;
5628                             cidx = _curList.VirtualListSize - 1;
5629                             break;
5630                     }
5631                     fnd = true;
5632                     goto RETRY;
5633                 }
5634             }
5635             catch (ArgumentOutOfRangeException)
5636             {
5637             }
5638             MessageBox.Show(Properties.Resources.DoTabSearchText2, Properties.Resources.DoTabSearchText3, MessageBoxButtons.OK, MessageBoxIcon.Information);
5639         }
5640
5641         private void MenuItemSubSearch_Click(object sender, EventArgs e)
5642         {
5643             // 検索メニュー
5644             this.ShowSearchDialog();
5645         }
5646
5647         private void MenuItemSearchNext_Click(object sender, EventArgs e)
5648         {
5649             var previousSearch = this.SearchDialog.ResultOptions;
5650             if (previousSearch == null || previousSearch.Type != SearchWordDialog.SearchType.Timeline)
5651             {
5652                 this.SearchDialog.Reset();
5653                 this.ShowSearchDialog();
5654                 return;
5655             }
5656
5657             // 次を検索
5658             this.DoTabSearch(
5659                 previousSearch.Query,
5660                 previousSearch.CaseSensitive,
5661                 previousSearch.UseRegex,
5662                 SEARCHTYPE.NextSearch);
5663         }
5664
5665         private void MenuItemSearchPrev_Click(object sender, EventArgs e)
5666         {
5667             var previousSearch = this.SearchDialog.ResultOptions;
5668             if (previousSearch == null || previousSearch.Type != SearchWordDialog.SearchType.Timeline)
5669             {
5670                 this.SearchDialog.Reset();
5671                 this.ShowSearchDialog();
5672                 return;
5673             }
5674
5675             // 前を検索
5676             this.DoTabSearch(
5677                 previousSearch.Query,
5678                 previousSearch.CaseSensitive,
5679                 previousSearch.UseRegex,
5680                 SEARCHTYPE.PrevSearch);
5681         }
5682
5683         /// <summary>
5684         /// 検索ダイアログを表示し、検索を実行します
5685         /// </summary>
5686         private void ShowSearchDialog()
5687         {
5688             // Recentタブの検索時以外では「新規タブに表示」ボタンを無効化する
5689             if (this._statuses.Tabs[this._curTab.Text].TabType == MyCommon.TabUsageType.Home)
5690                 this.SearchDialog.DisableNewTabButton = false;
5691             else
5692                 this.SearchDialog.DisableNewTabButton = true;
5693
5694             if (this.SearchDialog.ShowDialog(this) != DialogResult.OK)
5695             {
5696                 this.TopMost = this.SettingDialog.AlwaysTop;
5697                 return;
5698             }
5699             this.TopMost = this.SettingDialog.AlwaysTop;
5700
5701             var searchOptions = this.SearchDialog.ResultOptions;
5702             if (searchOptions.Type == SearchWordDialog.SearchType.Timeline)
5703             {
5704                 if (searchOptions.NewTab)
5705                 {
5706                     var tabName = searchOptions.Query;
5707
5708                     try
5709                     {
5710                         tabName = this._statuses.MakeTabName(tabName);
5711                     }
5712                     catch (TabException ex)
5713                     {
5714                         MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
5715                     }
5716
5717                     this.AddNewTab(tabName, false, MyCommon.TabUsageType.UserDefined);
5718                     this._statuses.AddTab(tabName, MyCommon.TabUsageType.UserDefined, null);
5719
5720                     var filter = new PostFilterRule
5721                     {
5722                         FilterBody = new[] { searchOptions.Query },
5723                         UseRegex = searchOptions.UseRegex,
5724                         CaseSensitive = searchOptions.CaseSensitive,
5725                     };
5726                     this._statuses.Tabs[tabName].AddFilter(filter);
5727
5728                     var tabPage = this.ListTab.TabPages.Cast<TabPage>()
5729                         .First(x => x.Text == tabName);
5730
5731                     this.ListTab.SelectedTab = tabPage;
5732                     this.ListTabSelect(tabPage);
5733
5734                     this.ApplyPostFilters();
5735                     this.SaveConfigsTabs();
5736                 }
5737                 else
5738                 {
5739                     this.DoTabSearch(
5740                         searchOptions.Query,
5741                         searchOptions.CaseSensitive,
5742                         searchOptions.UseRegex,
5743                         SEARCHTYPE.DialogSearch);
5744                 }
5745             }
5746             else if (searchOptions.Type == SearchWordDialog.SearchType.Public)
5747             {
5748                 this.AddNewTabForSearch(searchOptions.Query);
5749             }
5750         }
5751
5752         private void AboutMenuItem_Click(object sender, EventArgs e)
5753         {
5754             using (TweenAboutBox about = new TweenAboutBox())
5755             {
5756                 about.ShowDialog(this);
5757             }
5758             this.TopMost = SettingDialog.AlwaysTop;
5759         }
5760
5761         private void JumpUnreadMenuItem_Click(object sender, EventArgs e)
5762         {
5763             int bgnIdx = ListTab.TabPages.IndexOf(_curTab);
5764             int idx = -1;
5765             DetailsListView lst = null;
5766
5767             if (ImageSelector.Enabled)
5768                 return;
5769
5770             //現在タブから最終タブまで探索
5771             for (int i = bgnIdx; i < ListTab.TabPages.Count; i++)
5772             {
5773                 //未読Index取得
5774                 idx = _statuses.GetOldestUnreadIndex(ListTab.TabPages[i].Text);
5775                 if (idx > -1)
5776                 {
5777                     ListTab.SelectedIndex = i;
5778                     lst = (DetailsListView)ListTab.TabPages[i].Tag;
5779                     //_curTab = ListTab.TabPages[i];
5780                     break;
5781                 }
5782             }
5783
5784             //未読みつからず&現在タブが先頭ではなかったら、先頭タブから現在タブの手前まで探索
5785             if (idx == -1 && bgnIdx > 0)
5786             {
5787                 for (int i = 0; i < bgnIdx; i++)
5788                 {
5789                     idx = _statuses.GetOldestUnreadIndex(ListTab.TabPages[i].Text);
5790                     if (idx > -1)
5791                     {
5792                         ListTab.SelectedIndex = i;
5793                         lst = (DetailsListView)ListTab.TabPages[i].Tag;
5794                         //_curTab = ListTab.TabPages[i];
5795                         break;
5796                     }
5797                 }
5798             }
5799
5800             //全部調べたが未読見つからず→先頭タブの最新発言へ
5801             if (idx == -1)
5802             {
5803                 ListTab.SelectedIndex = 0;
5804                 lst = (DetailsListView)ListTab.TabPages[0].Tag;
5805                 //_curTab = ListTab.TabPages[0];
5806                 if (_statuses.SortOrder == SortOrder.Ascending)
5807                     idx = lst.VirtualListSize - 1;
5808                 else
5809                     idx = 0;
5810             }
5811
5812             if (lst.VirtualListSize > 0 && idx > -1 && lst.VirtualListSize > idx)
5813             {
5814                 SelectListItem(lst, idx);
5815                 if (_statuses.SortMode == IdComparerClass.ComparerMode.Id)
5816                 {
5817                     if (_statuses.SortOrder == SortOrder.Ascending && lst.Items[idx].Position.Y > lst.ClientSize.Height - _iconSz - 10 ||
5818                        _statuses.SortOrder == SortOrder.Descending && lst.Items[idx].Position.Y < _iconSz + 10)
5819                     {
5820                         MoveTop();
5821                     }
5822                     else
5823                     {
5824                         lst.EnsureVisible(idx);
5825                     }
5826                 }
5827                 else
5828                 {
5829                     lst.EnsureVisible(idx);
5830                 }
5831             }
5832             lst.Focus();
5833         }
5834
5835         private void StatusOpenMenuItem_Click(object sender, EventArgs e)
5836         {
5837             if (_curList.SelectedIndices.Count > 0 && _statuses.Tabs[_curTab.Text].TabType != MyCommon.TabUsageType.DirectMessage)
5838             {
5839                 var post = _statuses[_curTab.Text, _curList.SelectedIndices[0]];
5840                 OpenUriAsync(MyCommon.GetStatusUrl(post));
5841             }
5842         }
5843
5844         private void FavorareMenuItem_Click(object sender, EventArgs e)
5845         {
5846             if (_curList.SelectedIndices.Count > 0)
5847             {
5848                 PostClass post = _statuses[_curTab.Text, _curList.SelectedIndices[0]];
5849                 OpenUriAsync(Properties.Resources.FavstarUrl + "users/" + post.ScreenName + "/recent");
5850             }
5851         }
5852
5853         private async void VerUpMenuItem_Click(object sender, EventArgs e)
5854         {
5855             await this.CheckNewVersion(false);
5856         }
5857
5858         private void RunTweenUp()
5859         {
5860             ProcessStartInfo pinfo = new ProcessStartInfo();
5861             pinfo.UseShellExecute = true;
5862             pinfo.WorkingDirectory = MyCommon.settingPath;
5863             pinfo.FileName = Path.Combine(MyCommon.settingPath, "TweenUp3.exe");
5864             pinfo.Arguments = "\"" + Application.StartupPath + "\"";
5865             try
5866             {
5867                 Process.Start(pinfo);
5868             }
5869             catch (Exception)
5870             {
5871                 MessageBox.Show("Failed to execute TweenUp3.exe.");
5872             }
5873         }
5874
5875         public class VersionInfo
5876         {
5877             public Version Version { get; set; }
5878             public Uri DownloadUri { get; set; }
5879             public string ReleaseNote { get; set; }
5880         }
5881
5882         /// <summary>
5883         /// OpenTween の最新バージョンの情報を取得します
5884         /// </summary>
5885         public async Task<VersionInfo> GetVersionInfoAsync()
5886         {
5887             var versionInfoUrl = new Uri(ApplicationSettings.VersionInfoUrl + "?" +
5888                 DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount);
5889
5890             var responseText = await Networking.Http.GetStringAsync(versionInfoUrl)
5891                 .ConfigureAwait(false);
5892
5893             // 改行2つで前後パートを分割(前半がバージョン番号など、後半が詳細テキスト)
5894             var msgPart = responseText.Split(new[] { "\n\n", "\r\n\r\n" }, 2, StringSplitOptions.None);
5895
5896             var msgHeader = msgPart[0].Split(new[] { "\n", "\r\n" }, StringSplitOptions.None);
5897             var msgBody = msgPart.Length == 2 ? msgPart[1] : "";
5898
5899             msgBody = Regex.Replace(msgBody, "(?<!\r)\n", "\r\n"); // LF -> CRLF
5900
5901             return new VersionInfo
5902             {
5903                 Version = Version.Parse(msgHeader[0]),
5904                 DownloadUri = new Uri(msgHeader[1]),
5905                 ReleaseNote = msgBody,
5906             };
5907         }
5908
5909         private async Task CheckNewVersion(bool startup = false)
5910         {
5911             if (ApplicationSettings.VersionInfoUrl == null)
5912                 return; // 更新チェック無効化
5913
5914             try
5915             {
5916                 var versionInfo = await this.GetVersionInfoAsync();
5917
5918                 if (versionInfo.Version <= Version.Parse(MyCommon.FileVersion))
5919                 {
5920                     // 更新不要
5921                     if (!startup)
5922                     {
5923                         var msgtext = string.Format(Properties.Resources.CheckNewVersionText7,
5924                             MyCommon.GetReadableVersion(), MyCommon.GetReadableVersion(versionInfo.Version));
5925                         msgtext = MyCommon.ReplaceAppName(msgtext);
5926
5927                         MessageBox.Show(msgtext,
5928                             MyCommon.ReplaceAppName(Properties.Resources.CheckNewVersionText2),
5929                             MessageBoxButtons.OK, MessageBoxIcon.Information);
5930                     }
5931                     return;
5932                 }
5933
5934                 using (var dialog = new UpdateDialog())
5935                 {
5936                     dialog.SummaryText = string.Format(Properties.Resources.CheckNewVersionText3,
5937                         MyCommon.GetReadableVersion(versionInfo.Version));
5938                     dialog.DetailsText = versionInfo.ReleaseNote;
5939
5940                     if (dialog.ShowDialog(this) == DialogResult.Yes)
5941                     {
5942                         await this.OpenUriAsync(versionInfo.DownloadUri.OriginalString);
5943                     }
5944                 }
5945             }
5946             catch (Exception)
5947             {
5948                 this.StatusLabel.Text = Properties.Resources.CheckNewVersionText9;
5949                 if (!startup)
5950                 {
5951                     MessageBox.Show(Properties.Resources.CheckNewVersionText10,
5952                         MyCommon.ReplaceAppName(Properties.Resources.CheckNewVersionText2),
5953                         MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2);
5954                 }
5955             }
5956         }
5957
5958         private void Colorize()
5959         {
5960             _colorize = false;
5961             DispSelectedPost();
5962             //件数関連の場合、タイトル即時書き換え
5963             if (SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.None &&
5964                SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.Post &&
5965                SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.Ver &&
5966                SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.OwnStatus)
5967             {
5968                 SetMainWindowTitle();
5969             }
5970             if (!StatusLabelUrl.Text.StartsWith("http")) SetStatusLabelUrl();
5971             foreach (TabPage tb in ListTab.TabPages)
5972             {
5973                 if (_statuses.Tabs[tb.Text].UnreadCount == 0)
5974                 {
5975                     if (SettingDialog.TabIconDisp)
5976                     {
5977                         if (tb.ImageIndex == 0) tb.ImageIndex = -1;
5978                     }
5979                 }
5980             }
5981             if (!SettingDialog.TabIconDisp) ListTab.Refresh();
5982         }
5983
5984         public string createDetailHtml(string orgdata)
5985         {
5986             return detailHtmlFormatHeader + orgdata + detailHtmlFormatFooter;
5987         }
5988
5989         private async void DisplayItemImage_Downloaded(object sender, EventArgs e)
5990         {
5991             if (sender.Equals(displayItem))
5992             {
5993                 this.ClearUserPicture();
5994
5995                 var img = displayItem.Image;
5996                 try
5997                 {
5998                     if (img != null)
5999                         img = await img.CloneAsync();
6000
6001                     UserPicture.Image = img;
6002                 }
6003                 catch (Exception)
6004                 {
6005                     UserPicture.ShowErrorImage();
6006                 }
6007             }
6008         }
6009
6010         private void DispSelectedPost()
6011         {
6012             DispSelectedPost(false);
6013         }
6014
6015         private PostClass displayPost = new PostClass();
6016
6017         /// <summary>
6018         /// サムネイルの表示処理を表すタスク
6019         /// </summary>
6020         private Task thumbnailTask = null;
6021
6022         /// <summary>
6023         /// サムネイル表示に使用する CancellationToken の生成元
6024         /// </summary>
6025         private CancellationTokenSource thumbnailTokenSource = null;
6026
6027         private void DispSelectedPost(bool forceupdate)
6028         {
6029             if (_curList.SelectedIndices.Count == 0 || _curPost == null)
6030                 return;
6031
6032             var oldDisplayPost = this.displayPost;
6033             this.displayPost = this._curPost;
6034
6035             if (!forceupdate && this._curPost.Equals(oldDisplayPost))
6036                 return;
6037
6038             if (displayItem != null)
6039             {
6040                 displayItem.ImageDownloaded -= this.DisplayItemImage_Downloaded;
6041                 displayItem = null;
6042             }
6043             displayItem = (ImageListViewItem)_curList.Items[_curList.SelectedIndices[0]];
6044             displayItem.ImageDownloaded += this.DisplayItemImage_Downloaded;
6045
6046             using (ControlTransaction.Update(this.TableLayoutPanel1))
6047             {
6048                 var sourceText = "";
6049                 string sourceUrl = null;
6050                 if (!_curPost.IsDm)
6051                 {
6052                     var mc = Regex.Match(_curPost.SourceHtml, "<a href=\"(?<sourceurl>.+?)\"");
6053                     if (mc.Success)
6054                     {
6055                         var src = mc.Groups["sourceurl"].Value;
6056                         if (Regex.IsMatch(src, "^https?://"))
6057                             sourceUrl = src;
6058                         else
6059                             sourceUrl = "https://twitter.com/" + src;
6060                     }
6061
6062                     if (_curPost.Source != null)
6063                         sourceText = _curPost.Source;
6064                 }
6065                 SourceLinkLabel.Text = sourceText;
6066                 SourceLinkLabel.Tag = sourceUrl;
6067                 SourceLinkLabel.TabStop = false; // Text を更新すると勝手に true にされる
6068
6069                 string nameText;
6070                 if (_curPost.IsDm)
6071                 {
6072                     if (_curPost.IsOwl)
6073                         nameText = "DM FROM <- ";
6074                     else
6075                         nameText = "DM TO -> ";
6076                 }
6077                 else
6078                 {
6079                     nameText = "";
6080                 }
6081                 nameText += _curPost.ScreenName + "/" + _curPost.Nickname;
6082                 if (_curPost.RetweetedId != null)
6083                     nameText += " (RT:" + _curPost.RetweetedBy + ")";
6084
6085                 NameLabel.Text = nameText;
6086                 NameLabel.Tag = _curPost.ScreenName;
6087
6088                 var nameForeColor = SystemColors.ControlText;
6089                 if (_curPost.IsOwl && (this._cfgCommon.OneWayLove || _curPost.IsDm))
6090                     nameForeColor = this._clOWL;
6091                 if (_curPost.RetweetedId != null)
6092                     nameForeColor = this._clRetweet;
6093                 if (_curPost.IsFav)
6094                     nameForeColor = this._clFav;
6095                 NameLabel.ForeColor = nameForeColor;
6096
6097                 this.ClearUserPicture();
6098
6099                 if (!string.IsNullOrEmpty(_curPost.ImageUrl))
6100                 {
6101                     var image = IconCache.TryGetFromCache(_curPost.ImageUrl);
6102                     try
6103                     {
6104                         UserPicture.Image = image != null ? image.Clone() : null;
6105                     }
6106                     catch (Exception)
6107                     {
6108                         UserPicture.ShowErrorImage();
6109                     }
6110                 }
6111
6112                 DateTimeLabel.Text = _curPost.CreatedAt.ToString();
6113
6114                 if (DumpPostClassToolStripMenuItem.Checked)
6115                 {
6116                     StringBuilder sb = new StringBuilder(512);
6117
6118                     sb.Append("-----Start PostClass Dump<br>");
6119                     sb.AppendFormat("TextFromApi           : {0}<br>", _curPost.TextFromApi);
6120                     sb.AppendFormat("(PlainText)    : <xmp>{0}</xmp><br>", _curPost.TextFromApi);
6121                     sb.AppendFormat("StatusId             : {0}<br>", _curPost.StatusId.ToString());
6122                     //sb.AppendFormat("ImageIndex     : {0}<br>", _curPost.ImageIndex.ToString());
6123                     sb.AppendFormat("ImageUrl       : {0}<br>", _curPost.ImageUrl);
6124                     sb.AppendFormat("InReplyToStatusId    : {0}<br>", _curPost.InReplyToStatusId.ToString());
6125                     sb.AppendFormat("InReplyToUser  : {0}<br>", _curPost.InReplyToUser);
6126                     sb.AppendFormat("IsDM           : {0}<br>", _curPost.IsDm.ToString());
6127                     sb.AppendFormat("IsFav          : {0}<br>", _curPost.IsFav.ToString());
6128                     sb.AppendFormat("IsMark         : {0}<br>", _curPost.IsMark.ToString());
6129                     sb.AppendFormat("IsMe           : {0}<br>", _curPost.IsMe.ToString());
6130                     sb.AppendFormat("IsOwl          : {0}<br>", _curPost.IsOwl.ToString());
6131                     sb.AppendFormat("IsProtect      : {0}<br>", _curPost.IsProtect.ToString());
6132                     sb.AppendFormat("IsRead         : {0}<br>", _curPost.IsRead.ToString());
6133                     sb.AppendFormat("IsReply        : {0}<br>", _curPost.IsReply.ToString());
6134
6135                     foreach (string nm in _curPost.ReplyToList)
6136                     {
6137                         sb.AppendFormat("ReplyToList    : {0}<br>", nm);
6138                     }
6139
6140                     sb.AppendFormat("ScreenName           : {0}<br>", _curPost.ScreenName);
6141                     sb.AppendFormat("NickName       : {0}<br>", _curPost.Nickname);
6142                     sb.AppendFormat("Text   : {0}<br>", _curPost.Text);
6143                     sb.AppendFormat("(PlainText)    : <xmp>{0}</xmp><br>", _curPost.Text);
6144                     sb.AppendFormat("CreatedAt          : {0}<br>", _curPost.CreatedAt.ToString());
6145                     sb.AppendFormat("Source         : {0}<br>", _curPost.Source);
6146                     sb.AppendFormat("UserId            : {0}<br>", _curPost.UserId);
6147                     sb.AppendFormat("FilterHit      : {0}<br>", _curPost.FilterHit);
6148                     sb.AppendFormat("RetweetedBy    : {0}<br>", _curPost.RetweetedBy);
6149                     sb.AppendFormat("RetweetedId    : {0}<br>", _curPost.RetweetedId);
6150                     sb.AppendFormat("SearchTabName  : {0}<br>", _curPost.RelTabName);
6151                     sb.Append("-----End PostClass Dump<br>");
6152
6153                     PostBrowser.DocumentText = detailHtmlFormatHeader + sb.ToString() + detailHtmlFormatFooter;
6154                 }
6155                 else
6156                 {
6157                     // 同じIDのツイートであれば WebBrowser とサムネイルの更新を行わない
6158                     // (同一ツイートの RT は文面が同じであるため同様に更新しない)
6159                     if (_curPost.StatusId != oldDisplayPost.StatusId)
6160                     {
6161                         this.PostBrowser.DocumentText =
6162                             this.createDetailHtml(_curPost.IsDeleted ? "(DELETED)" : _curPost.Text);
6163
6164                         this.PostBrowser.Document.Window.ScrollTo(0, 0);
6165
6166                         this.SplitContainer3.Panel2Collapsed = true;
6167
6168                         if (this.IsPreviewEnable)
6169                         {
6170                             if (this.thumbnailTokenSource != null)
6171                             {
6172                                 var oldTokenSource = this.thumbnailTokenSource;
6173
6174                                 var cancelTask = Task.Run(() => oldTokenSource.Cancel());
6175
6176                                 Task.WhenAll(this.thumbnailTask, cancelTask)
6177                                     .ContinueWith(_ => oldTokenSource.Dispose(), TaskScheduler.Default);
6178                             }
6179
6180                             this.thumbnailTokenSource = new CancellationTokenSource();
6181
6182                             var token = this.thumbnailTokenSource.Token;
6183                             this.thumbnailTask = this.tweetThumbnail1.ShowThumbnailAsync(_curPost, token);
6184                         }
6185                     }
6186                 }
6187             }
6188         }
6189
6190         private void MatomeMenuItem_Click(object sender, EventArgs e)
6191         {
6192             OpenApplicationWebsite();
6193         }
6194
6195         private void OpenApplicationWebsite()
6196         {
6197             OpenUriAsync(ApplicationSettings.WebsiteUrl);
6198         }
6199
6200         private void ShortcutKeyListMenuItem_Click(object sender, EventArgs e)
6201         {
6202             OpenUriAsync(ApplicationSettings.ShortcutKeyUrl);
6203         }
6204
6205         private void ListTab_KeyDown(object sender, KeyEventArgs e)
6206         {
6207             if (ListTab.SelectedTab != null)
6208             {
6209                 if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch)
6210                 {
6211                     Control pnl = ListTab.SelectedTab.Controls["panelSearch"];
6212                     if (pnl.Controls["comboSearch"].Focused ||
6213                         pnl.Controls["comboLang"].Focused ||
6214                         pnl.Controls["buttonSearch"].Focused) return;
6215                 }
6216                 ModifierState State = GetModifierState(e.Control, e.Shift, e.Alt);
6217                 if (State == ModifierState.NotFlags) return;
6218                 if (State != ModifierState.None) _anchorFlag = false;
6219                 if (CommonKeyDown(e.KeyCode, FocusedControl.ListTab, State))
6220                 {
6221                     e.Handled = true;
6222                     e.SuppressKeyPress = true;
6223                 }
6224             }
6225         }
6226
6227         private ModifierState GetModifierState(bool sControl, bool sShift, bool sAlt)
6228         {
6229             ModifierState state = ModifierState.None;
6230             if (sControl) state = state | ModifierState.Ctrl;
6231             if (sShift) state = state | ModifierState.Shift;
6232             if (sAlt) state = state | ModifierState.Alt;
6233             return state;
6234         }
6235
6236         [FlagsAttribute]
6237         private enum ModifierState
6238         {
6239             None = 0,
6240             Alt = 1,
6241             Shift = 2,
6242             Ctrl = 4,
6243             //CShift = 11,
6244             //CAlt = 12,
6245             //AShift = 13,
6246             NotFlags = 8,
6247
6248             //ListTab = 101,
6249             //PostBrowser = 102,
6250             //StatusText = 103,
6251         }
6252
6253         private enum FocusedControl : int
6254         {
6255             None,
6256             ListTab,
6257             StatusText,
6258             PostBrowser,
6259         }
6260
6261         private bool CommonKeyDown(Keys KeyCode, FocusedControl Focused, ModifierState Modifier)
6262         {
6263             //リストのカーソル移動関係(上下キー、PageUp/Downに該当)
6264             if (Focused == FocusedControl.ListTab)
6265             {
6266                 if (Modifier == (ModifierState.Ctrl | ModifierState.Shift) ||
6267                     Modifier == ModifierState.Ctrl ||
6268                     Modifier == ModifierState.None ||
6269                     Modifier == ModifierState.Shift)
6270                 {
6271                     if (KeyCode == Keys.J)
6272                     {
6273                         SendKeys.Send("{DOWN}");
6274                         return true;
6275                     }
6276                     else if (KeyCode == Keys.K)
6277                     {
6278                         SendKeys.Send("{UP}");
6279                         return true;
6280                     }
6281                 }
6282                 if (Modifier == ModifierState.Shift ||
6283                     Modifier == ModifierState.None)
6284                 {
6285                     if (KeyCode == Keys.F)
6286                     {
6287                         SendKeys.Send("{PGDN}");
6288                         return true;
6289                     }
6290                     else if (KeyCode == Keys.B)
6291                     {
6292                         SendKeys.Send("{PGUP}");
6293                         return true;
6294                     }
6295                 }
6296             }
6297
6298             //修飾キーなし
6299             switch (Modifier)
6300             {
6301                 case ModifierState.None:
6302                     //フォーカス関係なし
6303                     switch (KeyCode)
6304                     {
6305                         case Keys.F1:
6306                             OpenApplicationWebsite();
6307                             return true;
6308                         case Keys.F3:
6309                             MenuItemSearchNext_Click(null, null);
6310                             return true;
6311                         case Keys.F5:
6312                             DoRefresh();
6313                             return true;
6314                         case Keys.F6:
6315                             GetTimeline(MyCommon.WORKERTYPE.Reply, 1, "");
6316                             return true;
6317                         case Keys.F7:
6318                             GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
6319                             return true;
6320                     }
6321                     if (Focused != FocusedControl.StatusText)
6322                     {
6323                         //フォーカスStatusText以外
6324                         switch (KeyCode)
6325                         {
6326                             case Keys.Space:
6327                             case Keys.ProcessKey:
6328                                 if (Focused == FocusedControl.ListTab) _anchorFlag = false;
6329                                 JumpUnreadMenuItem_Click(null, null);
6330                                 return true;
6331                             case Keys.G:
6332                                 if (Focused == FocusedControl.ListTab) _anchorFlag = false;
6333                                 ShowRelatedStatusesMenuItem_Click(null, null);
6334                                 return true;
6335                         }
6336                     }
6337                     if (Focused == FocusedControl.ListTab)
6338                     {
6339                         //フォーカスList
6340                         switch (KeyCode)
6341                         {
6342                             case Keys.N:
6343                             case Keys.Right:
6344                                 GoRelPost(true);
6345                                 return true;
6346                             case Keys.P:
6347                             case Keys.Left:
6348                                 GoRelPost(false);
6349                                 return true;
6350                             case Keys.OemPeriod:
6351                                 GoAnchor();
6352                                 return true;
6353                             case Keys.I:
6354                                 if (this.StatusText.Enabled) this.StatusText.Focus();
6355                                 return true;
6356                             case Keys.Enter:
6357                                 MakeReplyOrDirectStatus();
6358                                 return true;
6359                             case Keys.R:
6360                                 DoRefresh();
6361                                 return true;
6362                         }
6363                         //以下、アンカー初期化
6364                         _anchorFlag = false;
6365                         switch (KeyCode)
6366                         {
6367                             case Keys.L:
6368                                 GoPost(true);
6369                                 return true;
6370                             case Keys.H:
6371                                 GoPost(false);
6372                                 return true;
6373                             case Keys.Z:
6374                             case Keys.Oemcomma:
6375                                 MoveTop();
6376                                 return true;
6377                             case Keys.S:
6378                                 GoNextTab(true);
6379                                 return true;
6380                             case Keys.A:
6381                                 GoNextTab(false);
6382                                 return true;
6383                             case Keys.Oem4:
6384                                 // ] in_reply_to参照元へ戻る
6385                                 GoInReplyToPostTree();
6386                                 return true;
6387                             case Keys.Oem6:
6388                                 // [ in_reply_toへジャンプ
6389                                 GoBackInReplyToPostTree();
6390                                 return true;
6391                             case Keys.Escape:
6392                                 if (ListTab.SelectedTab != null)
6393                                 {
6394                                     MyCommon.TabUsageType tabtype = _statuses.Tabs[ListTab.SelectedTab.Text].TabType;
6395                                     if (tabtype == MyCommon.TabUsageType.Related || tabtype == MyCommon.TabUsageType.UserTimeline || tabtype == MyCommon.TabUsageType.PublicSearch)
6396                                     {
6397                                         TabPage relTp = ListTab.SelectedTab;
6398                                         RemoveSpecifiedTab(relTp.Text, false);
6399                                         SaveConfigsTabs();
6400                                         return true;
6401                                     }
6402                                 }
6403                                 break;
6404                         }
6405                     }
6406                     else if (Focused == FocusedControl.PostBrowser)
6407                     {
6408                         //フォーカスPostBrowser
6409                         switch (KeyCode)
6410                         {
6411                             case Keys.Up:
6412                             case Keys.Down:
6413                                 //スクロールを発生させるため、true を返す
6414                                 return true;
6415                         }
6416                     }
6417                     break;
6418                 case ModifierState.Ctrl:
6419                     //フォーカス関係なし
6420                     switch (KeyCode)
6421                     {
6422                         case Keys.R:
6423                             MakeReplyOrDirectStatus(false, true);
6424                             return true;
6425                         case Keys.D:
6426                             doStatusDelete();
6427                             return true;
6428                         case Keys.M:
6429                             MakeReplyOrDirectStatus(false, false);
6430                             return true;
6431                         case Keys.S:
6432                             FavoriteChange(true);
6433                             return true;
6434                         case Keys.I:
6435                             doRepliedStatusOpen();
6436                             return true;
6437                         case Keys.Q:
6438                             doQuote();
6439                             return true;
6440                         case Keys.B:
6441                             ReadedStripMenuItem_Click(null, null);
6442                             return true;
6443                         case Keys.T:
6444                             HashManageMenuItem_Click(null, null);
6445                             return true;
6446                         case Keys.L:
6447                             UrlConvertAutoToolStripMenuItem_Click(null, null);
6448                             return true;
6449                         case Keys.Y:
6450                             if (Focused != FocusedControl.PostBrowser)
6451                             {
6452                                 MultiLineMenuItem_Click(null, null);
6453                                 return true;
6454                             }
6455                             break;
6456                         case Keys.F:
6457                             MenuItemSubSearch_Click(null, null);
6458                             return true;
6459                         case Keys.U:
6460                             ShowUserTimeline();
6461                             return true;
6462                         case Keys.H:
6463                             // Webページを開く動作
6464                             MoveToHomeToolStripMenuItem_Click(null, null);
6465                             return true;
6466                         case Keys.G:
6467                             // Webページを開く動作
6468                             MoveToFavToolStripMenuItem_Click(null, null);
6469                             return true;
6470                         case Keys.O:
6471                             // Webページを開く動作
6472                             StatusOpenMenuItem_Click(null, null);
6473                             return true;
6474                         case Keys.E:
6475                             // Webページを開く動作
6476                             OpenURLMenuItem_Click(null, null);
6477                             return true;
6478                     }
6479                     //フォーカスList
6480                     if (Focused == FocusedControl.ListTab)
6481                     {
6482                         switch (KeyCode)
6483                         {
6484                             case Keys.Home:
6485                             case Keys.End:
6486                                 _colorize = true;
6487                                 return false;            //スルーする
6488                             case Keys.N:
6489                                 GoNextTab(true);
6490                                 return true;
6491                             case Keys.P:
6492                                 GoNextTab(false);
6493                                 return true;
6494                             case Keys.C:
6495                                 CopyStot();
6496                                 return true;
6497                             case Keys.D1:
6498                             case Keys.D2:
6499                             case Keys.D3:
6500                             case Keys.D4:
6501                             case Keys.D5:
6502                             case Keys.D6:
6503                             case Keys.D7:
6504                             case Keys.D8:
6505                                 // タブダイレクト選択(Ctrl+1~8,Ctrl+9)
6506                                 int tabNo = KeyCode - Keys.D1;
6507                                 if (ListTab.TabPages.Count < tabNo)
6508                                     return false;
6509                                 ListTab.SelectedIndex = tabNo;
6510                                 ListTabSelect(ListTab.TabPages[tabNo]);
6511                                 return true;
6512                             case Keys.D9:
6513                                 ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
6514                                 ListTabSelect(ListTab.TabPages[ListTab.TabPages.Count - 1]);
6515                                 return true;
6516                         }
6517                     }
6518                     else if (Focused == FocusedControl.StatusText)
6519                     {
6520                         //フォーカスStatusText
6521                         switch (KeyCode)
6522                         {
6523                             case Keys.A:
6524                                 StatusText.SelectAll();
6525                                 return true;
6526                             case Keys.Up:
6527                             case Keys.Down:
6528                                 if (!string.IsNullOrWhiteSpace(StatusText.Text))
6529                                 {
6530                                     _history[_hisIdx] = new PostingStatus(StatusText.Text, _reply_to_id, _reply_to_name);
6531                                 }
6532                                 if (KeyCode == Keys.Up)
6533                                 {
6534                                     _hisIdx -= 1;
6535                                     if (_hisIdx < 0) _hisIdx = 0;
6536                                 }
6537                                 else
6538                                 {
6539                                     _hisIdx += 1;
6540                                     if (_hisIdx > _history.Count - 1) _hisIdx = _history.Count - 1;
6541                                 }
6542                                 StatusText.Text = _history[_hisIdx].status;
6543                                 _reply_to_id = _history[_hisIdx].inReplyToId;
6544                                 _reply_to_name = _history[_hisIdx].inReplyToName;
6545                                 StatusText.SelectionStart = StatusText.Text.Length;
6546                                 return true;
6547                             case Keys.PageUp:
6548                             case Keys.P:
6549                                 if (ListTab.SelectedIndex == 0)
6550                                 {
6551                                     ListTab.SelectedIndex = ListTab.TabCount - 1;
6552                                 }
6553                                 else
6554                                 {
6555                                     ListTab.SelectedIndex -= 1;
6556                                 }
6557                                 StatusText.Focus();
6558                                 return true;
6559                             case Keys.PageDown:
6560                             case Keys.N:
6561                                 if (ListTab.SelectedIndex == ListTab.TabCount - 1)
6562                                 {
6563                                     ListTab.SelectedIndex = 0;
6564                                 }
6565                                 else
6566                                 {
6567                                     ListTab.SelectedIndex += 1;
6568                                 }
6569                                 StatusText.Focus();
6570                                 return true;
6571                         }
6572                     }
6573                     else
6574                     {
6575                         //フォーカスPostBrowserもしくは関係なし
6576                         switch (KeyCode)
6577                         {
6578                             case Keys.A:
6579                                 PostBrowser.Document.ExecCommand("SelectAll", false, null);
6580                                 return true;
6581                             case Keys.C:
6582                             case Keys.Insert:
6583                                 string _selText = WebBrowser_GetSelectionText(ref PostBrowser);
6584                                 if (!string.IsNullOrEmpty(_selText))
6585                                 {
6586                                     try
6587                                     {
6588                                         Clipboard.SetDataObject(_selText, false, 5, 100);
6589                                     }
6590                                     catch (Exception ex)
6591                                     {
6592                                         MessageBox.Show(ex.Message);
6593                                     }
6594                                 }
6595                                 return true;
6596                             case Keys.Y:
6597                                 MultiLineMenuItem.Checked = !MultiLineMenuItem.Checked;
6598                                 MultiLineMenuItem_Click(null, null);
6599                                 return true;
6600                         }
6601                     }
6602                     break;
6603                 case ModifierState.Shift:
6604                     //フォーカス関係なし
6605                     switch (KeyCode)
6606                     {
6607                         case Keys.F3:
6608                             MenuItemSearchPrev_Click(null, null);
6609                             return true;
6610                         case Keys.F5:
6611                             DoRefreshMore();
6612                             return true;
6613                         case Keys.F6:
6614                             GetTimeline(MyCommon.WORKERTYPE.Reply, -1, "");
6615                             return true;
6616                         case Keys.F7:
6617                             GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, -1, "");
6618                             return true;
6619                     }
6620                     //フォーカスStatusText以外
6621                     if (Focused != FocusedControl.StatusText)
6622                     {
6623                         if (KeyCode == Keys.R)
6624                         {
6625                             DoRefreshMore();
6626                             return true;
6627                         }
6628                     }
6629                     //フォーカスリスト
6630                     if (Focused == FocusedControl.ListTab)
6631                     {
6632                         switch (KeyCode)
6633                         {
6634                             case Keys.H:
6635                                 GoTopEnd(true);
6636                                 return true;
6637                             case Keys.L:
6638                                 GoTopEnd(false);
6639                                 return true;
6640                             case Keys.M:
6641                                 GoMiddle();
6642                                 return true;
6643                             case Keys.G:
6644                                 GoLast();
6645                                 return true;
6646                             case Keys.Z:
6647                                 MoveMiddle();
6648                                 return true;
6649                             case Keys.Oem4:
6650                                 GoBackInReplyToPostTree(true, false);
6651                                 return true;
6652                             case Keys.Oem6:
6653                                 GoBackInReplyToPostTree(true, true);
6654                                 return true;
6655                             case Keys.N:
6656                             case Keys.Right:
6657                                 // お気に入り前後ジャンプ(SHIFT+N←/P→)
6658                                 GoFav(true);
6659                                 return true;
6660                             case Keys.P:
6661                             case Keys.Left:
6662                                 // お気に入り前後ジャンプ(SHIFT+N←/P→)
6663                                 GoFav(false);
6664                                 return true;
6665                             case Keys.Space:
6666                                 this.GoBackSelectPostChain();
6667                                 return true;
6668                         }
6669                     }
6670                     break;
6671                 case ModifierState.Alt:
6672                     switch (KeyCode)
6673                     {
6674                         case Keys.R:
6675                             doReTweetOfficial(true);
6676                             return true;
6677                         case Keys.P:
6678                             if (_curPost != null)
6679                             {
6680                                 doShowUserStatus(_curPost.ScreenName, false);
6681                                 return true;
6682                             }
6683                             break;
6684                         case Keys.Up:
6685                             ScrollDownPostBrowser(false);
6686                             return true;
6687                         case Keys.Down:
6688                             ScrollDownPostBrowser(true);
6689                             return true;
6690                         case Keys.PageUp:
6691                             PageDownPostBrowser(false);
6692                             return true;
6693                         case Keys.PageDown:
6694                             PageDownPostBrowser(true);
6695                             return true;
6696                     }
6697                     if (Focused == FocusedControl.ListTab)
6698                     {
6699                         // 別タブの同じ書き込みへ(ALT+←/→)
6700                         if (KeyCode == Keys.Right)
6701                         {
6702                             GoSamePostToAnotherTab(false);
6703                             return true;
6704                         }
6705                         else if (KeyCode == Keys.Left)
6706                         {
6707                             GoSamePostToAnotherTab(true);
6708                             return true;
6709                         }
6710                     }
6711                     break;
6712                 case ModifierState.Ctrl | ModifierState.Shift:
6713                     switch (KeyCode)
6714                     {
6715                         case Keys.R:
6716                             MakeReplyOrDirectStatus(false, true, true);
6717                             return true;
6718                         case Keys.C:
6719                             CopyIdUri();
6720                             return true;
6721                         case Keys.F:
6722                             if (ListTab.SelectedTab != null)
6723                             {
6724                                 if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch)
6725                                 {
6726                                     ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus();
6727                                     return true;
6728                                 }
6729                             }
6730                             break;
6731                         case Keys.S:
6732                             FavoriteChange(false);
6733                             return true;
6734                         case Keys.B:
6735                             UnreadStripMenuItem_Click(null, null);
6736                             return true;
6737                         case Keys.T:
6738                             HashToggleMenuItem_Click(null, null);
6739                             return true;
6740                         case Keys.P:
6741                             ImageSelectMenuItem_Click(null, null);
6742                             return true;
6743                         case Keys.H:
6744                             doMoveToRTHome();
6745                             return true;
6746                         case Keys.O:
6747                             FavorareMenuItem_Click(null, null);
6748                             return true;
6749                     }
6750                     if (Focused == FocusedControl.StatusText)
6751                     {
6752                         int idx = 0;
6753                         switch (KeyCode)
6754                         {
6755                             case Keys.Up:
6756                                 if (_curList != null && _curList.VirtualListSize != 0 &&
6757                                             _curList.SelectedIndices.Count > 0 && _curList.SelectedIndices[0] > 0)
6758                                 {
6759                                     idx = _curList.SelectedIndices[0] - 1;
6760                                     SelectListItem(_curList, idx);
6761                                     _curList.EnsureVisible(idx);
6762                                     return true;
6763                                 }
6764                                 break;
6765                             case Keys.Down:
6766                                 if (_curList != null && _curList.VirtualListSize != 0 && _curList.SelectedIndices.Count > 0
6767                                             && _curList.SelectedIndices[0] < _curList.VirtualListSize - 1)
6768                                 {
6769                                     idx = _curList.SelectedIndices[0] + 1;
6770                                     SelectListItem(_curList, idx);
6771                                     _curList.EnsureVisible(idx);
6772                                     return true;
6773                                 }
6774                                 break;
6775                             case Keys.Space:
6776                                 if (StatusText.SelectionStart > 0)
6777                                 {
6778                                     int endidx = StatusText.SelectionStart - 1;
6779                                     string startstr = "";
6780                                     bool pressed = false;
6781                                     for (int i = StatusText.SelectionStart - 1; i >= 0; i--)
6782                                     {
6783                                         char c = StatusText.Text[i];
6784                                         if (Char.IsLetterOrDigit(c) || c == '_')
6785                                         {
6786                                             continue;
6787                                         }
6788                                         if (c == '@')
6789                                         {
6790                                             pressed = true;
6791                                             startstr = StatusText.Text.Substring(i + 1, endidx - i);
6792                                             int cnt = AtIdSupl.ItemCount;
6793                                             ShowSuplDialog(StatusText, AtIdSupl, startstr.Length + 1, startstr);
6794                                             if (AtIdSupl.ItemCount != cnt) _modifySettingAtId = true;
6795                                         }
6796                                         else if (c == '#')
6797                                         {
6798                                             pressed = true;
6799                                             startstr = StatusText.Text.Substring(i + 1, endidx - i);
6800                                             ShowSuplDialog(StatusText, HashSupl, startstr.Length + 1, startstr);
6801                                         }
6802                                         else
6803                                         {
6804                                             break;
6805                                         }
6806                                     }
6807                                     return pressed;
6808                                 }
6809                                 break;
6810                         }
6811                     }
6812                     else if (Focused == FocusedControl.ListTab)
6813                     {
6814                         DetailsListView lst = (DetailsListView)ListTab.SelectedTab.Tag;
6815                         ColumnHeader col;
6816                         switch (KeyCode)
6817                         {
6818                             case Keys.D1:
6819                             case Keys.D2:
6820                             case Keys.D3:
6821                             case Keys.D4:
6822                             case Keys.D5:
6823                             case Keys.D6:
6824                             case Keys.D7:
6825                             case Keys.D8:
6826                                 // ソートダイレクト選択(Ctrl+Shift+1~8,Ctrl+Shift+9)
6827                                 int colNo = KeyCode - Keys.D1;
6828                                 if (lst.Columns.Count < colNo) return false;
6829                                 col = lst.Columns.Cast<ColumnHeader>().Where((x) => { return x.DisplayIndex == colNo; }).FirstOrDefault();
6830                                 if (col == null) return false;
6831                                 MyList_ColumnClick(lst, new ColumnClickEventArgs(col.Index));
6832                                 return true;
6833                             case Keys.D9:
6834                                 col = lst.Columns.Cast<ColumnHeader>().OrderByDescending((x) => { return x.DisplayIndex; }).First();
6835                                 MyList_ColumnClick(lst, new ColumnClickEventArgs(col.Index));
6836                                 return true;
6837                         }
6838                     }
6839                     break;
6840                 case ModifierState.Ctrl | ModifierState.Alt:
6841                     if (KeyCode == Keys.S)
6842                     {
6843                         FavoritesRetweetOriginal();
6844                         return true;
6845                     }
6846                     else if (KeyCode == Keys.R)
6847                     {
6848                         FavoritesRetweetUnofficial();
6849                         return true;
6850                     }
6851                     else if (KeyCode == Keys.H)
6852                     {
6853                         OpenUserAppointUrl();
6854                         return true;
6855                     }
6856                     break;
6857                 case ModifierState.Alt | ModifierState.Shift:
6858                     if (Focused == FocusedControl.PostBrowser)
6859                     {
6860                         if (KeyCode == Keys.R)
6861                             doReTweetUnofficial();
6862                         else if (KeyCode == Keys.C)
6863                             CopyUserId();
6864                         return true;
6865                     }
6866                     switch (KeyCode)
6867                     {
6868                         case Keys.T:
6869                             if (!this.ExistCurrentPost) return false;
6870                             doTranslation(_curPost.TextFromApi);
6871                             return true;
6872                         case Keys.R:
6873                             doReTweetUnofficial();
6874                             return true;
6875                         case Keys.C:
6876                             CopyUserId();
6877                             return true;
6878                         case Keys.Up:
6879                             this.tweetThumbnail1.ScrollUp();
6880                             return true;
6881                         case Keys.Down:
6882                             this.tweetThumbnail1.ScrollDown();
6883                             return true;
6884                     }
6885                     if (Focused == FocusedControl.ListTab && KeyCode == Keys.Enter)
6886                     {
6887                         if (!this.SplitContainer3.Panel2Collapsed)
6888                         {
6889                             OpenThumbnailPicture(this.tweetThumbnail1.Thumbnail);
6890                         }
6891                         return true;
6892                     }
6893                     break;
6894             }
6895
6896             return false;
6897         }
6898
6899         private void ScrollDownPostBrowser(bool forward)
6900         {
6901             var doc = PostBrowser.Document;
6902             if (doc == null) return;
6903
6904             var tags = doc.GetElementsByTagName("html");
6905             if (tags.Count > 0)
6906             {
6907                 if (forward)
6908                     tags[0].ScrollTop += SettingDialog.FontDetail.Height;
6909                 else
6910                     tags[0].ScrollTop -= SettingDialog.FontDetail.Height;
6911             }
6912         }
6913
6914         private void PageDownPostBrowser(bool forward)
6915         {
6916             var doc = PostBrowser.Document;
6917             if (doc == null) return;
6918
6919             var tags = doc.GetElementsByTagName("html");
6920             if (tags.Count > 0)
6921             {
6922                 if (forward)
6923                     tags[0].ScrollTop += PostBrowser.ClientRectangle.Height - SettingDialog.FontDetail.Height;
6924                 else
6925                     tags[0].ScrollTop -= PostBrowser.ClientRectangle.Height - SettingDialog.FontDetail.Height;
6926             }
6927         }
6928
6929         private void GoNextTab(bool forward)
6930         {
6931             int idx = ListTab.SelectedIndex;
6932             if (forward)
6933             {
6934                 idx += 1;
6935                 if (idx > ListTab.TabPages.Count - 1) idx = 0;
6936             }
6937             else
6938             {
6939                 idx -= 1;
6940                 if (idx < 0) idx = ListTab.TabPages.Count - 1;
6941             }
6942             ListTab.SelectedIndex = idx;
6943             ListTabSelect(ListTab.TabPages[idx]);
6944         }
6945
6946         private void CopyStot()
6947         {
6948             string clstr = "";
6949             StringBuilder sb = new StringBuilder();
6950             bool IsProtected = false;
6951             bool isDm = false;
6952             if (this._curTab != null && this._statuses.GetTabByName(this._curTab.Text) != null) isDm = this._statuses.GetTabByName(this._curTab.Text).TabType == MyCommon.TabUsageType.DirectMessage;
6953             foreach (int idx in _curList.SelectedIndices)
6954             {
6955                 PostClass post = _statuses[_curTab.Text, idx];
6956                 if (post.IsProtect)
6957                 {
6958                     IsProtected = true;
6959                     continue;
6960                 }
6961                 if (post.IsDeleted) continue;
6962                 if (!isDm)
6963                 {
6964                     if (post.RetweetedId != null)
6965                         sb.AppendFormat("{0}:{1} [https://twitter.com/{0}/status/{2}]{3}", post.ScreenName, post.TextSingleLine, post.RetweetedId, Environment.NewLine);
6966                     else
6967                         sb.AppendFormat("{0}:{1} [https://twitter.com/{0}/status/{2}]{3}", post.ScreenName, post.TextSingleLine, post.StatusId, Environment.NewLine);
6968                 }
6969                 else
6970                 {
6971                     sb.AppendFormat("{0}:{1} [{2}]{3}", post.ScreenName, post.TextSingleLine, post.StatusId, Environment.NewLine);
6972                 }
6973             }
6974             if (IsProtected)
6975             {
6976                 MessageBox.Show(Properties.Resources.CopyStotText1);
6977             }
6978             if (sb.Length > 0)
6979             {
6980                 clstr = sb.ToString();
6981                 try
6982                 {
6983                     Clipboard.SetDataObject(clstr, false, 5, 100);
6984                 }
6985                 catch (Exception ex)
6986                 {
6987                     MessageBox.Show(ex.Message);
6988                 }
6989             }
6990         }
6991
6992         private void CopyIdUri()
6993         {
6994             string clstr = "";
6995             StringBuilder sb = new StringBuilder();
6996             if (this._curTab == null) return;
6997             if (this._statuses.GetTabByName(this._curTab.Text) == null) return;
6998             if (this._statuses.GetTabByName(this._curTab.Text).TabType == MyCommon.TabUsageType.DirectMessage) return;
6999             foreach (int idx in _curList.SelectedIndices)
7000             {
7001                 var post = _statuses[_curTab.Text, idx];
7002                 sb.Append(MyCommon.GetStatusUrl(post));
7003                 sb.Append(Environment.NewLine);
7004             }
7005             if (sb.Length > 0)
7006             {
7007                 clstr = sb.ToString();
7008                 try
7009                 {
7010                     Clipboard.SetDataObject(clstr, false, 5, 100);
7011                 }
7012                 catch (Exception ex)
7013                 {
7014                     MessageBox.Show(ex.Message);
7015                 }
7016             }
7017         }
7018
7019         private void GoFav(bool forward)
7020         {
7021             if (_curList.VirtualListSize == 0) return;
7022             int fIdx = 0;
7023             int toIdx = 0;
7024             int stp = 1;
7025
7026             if (forward)
7027             {
7028                 if (_curList.SelectedIndices.Count == 0)
7029                 {
7030                     fIdx = 0;
7031                 }
7032                 else
7033                 {
7034                     fIdx = _curList.SelectedIndices[0] + 1;
7035                     if (fIdx > _curList.VirtualListSize - 1) return;
7036                 }
7037                 toIdx = _curList.VirtualListSize;
7038                 stp = 1;
7039             }
7040             else
7041             {
7042                 if (_curList.SelectedIndices.Count == 0)
7043                 {
7044                     fIdx = _curList.VirtualListSize - 1;
7045                 }
7046                 else
7047                 {
7048                     fIdx = _curList.SelectedIndices[0] - 1;
7049                     if (fIdx < 0) return;
7050                 }
7051                 toIdx = -1;
7052                 stp = -1;
7053             }
7054
7055             for (int idx = fIdx; idx != toIdx; idx += stp)
7056             {
7057                 if (_statuses[_curTab.Text, idx].IsFav)
7058                 {
7059                     SelectListItem(_curList, idx);
7060                     _curList.EnsureVisible(idx);
7061                     break;
7062                 }
7063             }
7064         }
7065
7066         private void GoSamePostToAnotherTab(bool left)
7067         {
7068             if (_curList.VirtualListSize == 0) return;
7069             int fIdx = 0;
7070             int toIdx = 0;
7071             int stp = 1;
7072             long targetId = 0;
7073
7074             if (_statuses.Tabs[_curTab.Text].TabType == MyCommon.TabUsageType.DirectMessage) return; // Directタブは対象外(見つかるはずがない)
7075             if (_curList.SelectedIndices.Count == 0) return; //未選択も処理しない
7076
7077             targetId = GetCurTabPost(_curList.SelectedIndices[0]).StatusId;
7078
7079             if (left)
7080             {
7081                 // 左のタブへ
7082                 if (ListTab.SelectedIndex == 0)
7083                 {
7084                     return;
7085                 }
7086                 else
7087                 {
7088                     fIdx = ListTab.SelectedIndex - 1;
7089                 }
7090                 toIdx = -1;
7091                 stp = -1;
7092             }
7093             else
7094             {
7095                 // 右のタブへ
7096                 if (ListTab.SelectedIndex == ListTab.TabCount - 1)
7097                 {
7098                     return;
7099                 }
7100                 else
7101                 {
7102                     fIdx = ListTab.SelectedIndex + 1;
7103                 }
7104                 toIdx = ListTab.TabCount;
7105                 stp = 1;
7106             }
7107
7108             bool found = false;
7109             for (int tabidx = fIdx; tabidx != toIdx; tabidx += stp)
7110             {
7111                 if (_statuses.Tabs[ListTab.TabPages[tabidx].Text].TabType == MyCommon.TabUsageType.DirectMessage) continue; // Directタブは対象外
7112                 for (int idx = 0; idx < ((DetailsListView)ListTab.TabPages[tabidx].Tag).VirtualListSize; idx++)
7113                 {
7114                     if (_statuses[ListTab.TabPages[tabidx].Text, idx].StatusId == targetId)
7115                     {
7116                         ListTab.SelectedIndex = tabidx;
7117                         ListTabSelect(ListTab.TabPages[tabidx]);
7118                         SelectListItem(_curList, idx);
7119                         _curList.EnsureVisible(idx);
7120                         found = true;
7121                         break;
7122                     }
7123                 }
7124                 if (found) break;
7125             }
7126         }
7127
7128         private void GoPost(bool forward)
7129         {
7130             if (_curList.SelectedIndices.Count == 0 || _curPost == null) return;
7131             int fIdx = 0;
7132             int toIdx = 0;
7133             int stp = 1;
7134
7135             if (forward)
7136             {
7137                 fIdx = _curList.SelectedIndices[0] + 1;
7138                 if (fIdx > _curList.VirtualListSize - 1) return;
7139                 toIdx = _curList.VirtualListSize;
7140                 stp = 1;
7141             }
7142             else
7143             {
7144                 fIdx = _curList.SelectedIndices[0] - 1;
7145                 if (fIdx < 0) return;
7146                 toIdx = -1;
7147                 stp = -1;
7148             }
7149
7150             string name = "";
7151             if (_curPost.RetweetedId == null)
7152             {
7153                 name = _curPost.ScreenName;
7154             }
7155             else
7156             {
7157                 name = _curPost.RetweetedBy;
7158             }
7159             for (int idx = fIdx; idx != toIdx; idx += stp)
7160             {
7161                 if (_statuses[_curTab.Text, idx].RetweetedId == null)
7162                 {
7163                     if (_statuses[_curTab.Text, idx].ScreenName == name)
7164                     {
7165                         SelectListItem(_curList, idx);
7166                         _curList.EnsureVisible(idx);
7167                         break;
7168                     }
7169                 }
7170                 else
7171                 {
7172                     if (_statuses[_curTab.Text, idx].RetweetedBy == name)
7173                     {
7174                         SelectListItem(_curList, idx);
7175                         _curList.EnsureVisible(idx);
7176                         break;
7177                     }
7178                 }
7179             }
7180         }
7181
7182         private void GoRelPost(bool forward)
7183         {
7184             if (_curList.SelectedIndices.Count == 0) return;
7185
7186             int fIdx = 0;
7187             int toIdx = 0;
7188             int stp = 1;
7189             if (forward)
7190             {
7191                 fIdx = _curList.SelectedIndices[0] + 1;
7192                 if (fIdx > _curList.VirtualListSize - 1) return;
7193                 toIdx = _curList.VirtualListSize;
7194                 stp = 1;
7195             }
7196             else
7197             {
7198                 fIdx = _curList.SelectedIndices[0] - 1;
7199                 if (fIdx < 0) return;
7200                 toIdx = -1;
7201                 stp = -1;
7202             }
7203
7204             if (!_anchorFlag)
7205             {
7206                 if (_curPost == null) return;
7207                 _anchorPost = _curPost;
7208                 _anchorFlag = true;
7209             }
7210             else
7211             {
7212                 if (_anchorPost == null) return;
7213             }
7214
7215             for (int idx = fIdx; idx != toIdx; idx += stp)
7216             {
7217                 PostClass post = _statuses[_curTab.Text, idx];
7218                 if (post.ScreenName == _anchorPost.ScreenName ||
7219                     post.RetweetedBy == _anchorPost.ScreenName ||
7220                     post.ScreenName == _anchorPost.RetweetedBy ||
7221                     (!string.IsNullOrEmpty(post.RetweetedBy) && post.RetweetedBy == _anchorPost.RetweetedBy) ||
7222                     _anchorPost.ReplyToList.Contains(post.ScreenName.ToLower()) ||
7223                     _anchorPost.ReplyToList.Contains(post.RetweetedBy.ToLower()) ||
7224                     post.ReplyToList.Contains(_anchorPost.ScreenName.ToLower()) ||
7225                     post.ReplyToList.Contains(_anchorPost.RetweetedBy.ToLower()))
7226                 {
7227                     SelectListItem(_curList, idx);
7228                     _curList.EnsureVisible(idx);
7229                     break;
7230                 }
7231             }
7232         }
7233
7234         private void GoAnchor()
7235         {
7236             if (_anchorPost == null) return;
7237             int idx = _statuses.Tabs[_curTab.Text].IndexOf(_anchorPost.StatusId);
7238             if (idx == -1) return;
7239
7240             SelectListItem(_curList, idx);
7241             _curList.EnsureVisible(idx);
7242         }
7243
7244         private void GoTopEnd(bool GoTop)
7245         {
7246             ListViewItem _item;
7247             int idx;
7248
7249             if (GoTop)
7250             {
7251                 _item = _curList.GetItemAt(0, 25);
7252                 if (_item == null)
7253                     idx = 0;
7254                 else
7255                     idx = _item.Index;
7256             }
7257             else
7258             {
7259                 _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1);
7260                 if (_item == null)
7261                     idx = _curList.VirtualListSize - 1;
7262                 else
7263                     idx = _item.Index;
7264             }
7265             SelectListItem(_curList, idx);
7266         }
7267
7268         private void GoMiddle()
7269         {
7270             ListViewItem _item;
7271             int idx1;
7272             int idx2;
7273             int idx3;
7274
7275             _item = _curList.GetItemAt(0, 0);
7276             if (_item == null)
7277             {
7278                 idx1 = 0;
7279             }
7280             else
7281             {
7282                 idx1 = _item.Index;
7283             }
7284
7285             _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1);
7286             if (_item == null)
7287             {
7288                 idx2 = _curList.VirtualListSize - 1;
7289             }
7290             else
7291             {
7292                 idx2 = _item.Index;
7293             }
7294             idx3 = (idx1 + idx2) / 2;
7295
7296             SelectListItem(_curList, idx3);
7297         }
7298
7299         private void GoLast()
7300         {
7301             if (_curList.VirtualListSize == 0) return;
7302
7303             if (_statuses.SortOrder == SortOrder.Ascending)
7304             {
7305                 SelectListItem(_curList, _curList.VirtualListSize - 1);
7306                 _curList.EnsureVisible(_curList.VirtualListSize - 1);
7307             }
7308             else
7309             {
7310                 SelectListItem(_curList, 0);
7311                 _curList.EnsureVisible(0);
7312             }
7313         }
7314
7315         private void MoveTop()
7316         {
7317             if (_curList.SelectedIndices.Count == 0) return;
7318             int idx = _curList.SelectedIndices[0];
7319             if (_statuses.SortOrder == SortOrder.Ascending)
7320             {
7321                 _curList.EnsureVisible(_curList.VirtualListSize - 1);
7322             }
7323             else
7324             {
7325                 _curList.EnsureVisible(0);
7326             }
7327             _curList.EnsureVisible(idx);
7328         }
7329
7330         private void GoInReplyToPostTree()
7331         {
7332             if (_curPost == null) return;
7333
7334             TabClass curTabClass = _statuses.Tabs[_curTab.Text];
7335
7336             if (curTabClass.TabType == MyCommon.TabUsageType.PublicSearch && _curPost.InReplyToStatusId == null && _curPost.TextFromApi.Contains("@"))
7337             {
7338                 PostClass post = null;
7339                 string r = tw.GetStatusApi(false, _curPost.StatusId, ref post);
7340                 if (string.IsNullOrEmpty(r) && post != null)
7341                 {
7342                     _curPost.InReplyToStatusId = post.InReplyToStatusId;
7343                     _curPost.InReplyToUser = post.InReplyToUser;
7344                     _curPost.IsReply = post.IsReply;
7345                     this.PurgeListViewItemCache();
7346                     _curList.RedrawItems(_curItemIndex, _curItemIndex, false);
7347                 }
7348                 else
7349                 {
7350                     this.StatusLabel.Text = r;
7351                 }
7352             }
7353
7354             if (!(this.ExistCurrentPost && _curPost.InReplyToUser != null && _curPost.InReplyToStatusId != null)) return;
7355
7356             if (replyChains == null || (replyChains.Count > 0 && replyChains.Peek().InReplyToId != _curPost.StatusId))
7357             {
7358                 replyChains = new Stack<ReplyChain>();
7359             }
7360             replyChains.Push(new ReplyChain(_curPost.StatusId, _curPost.InReplyToStatusId.Value, _curTab));
7361
7362             int inReplyToIndex;
7363             string inReplyToTabName;
7364             long inReplyToId = _curPost.InReplyToStatusId.Value;
7365             string inReplyToUser = _curPost.InReplyToUser;
7366             //Dictionary<long, PostClass> curTabPosts = curTabClass.Posts;
7367
7368             var inReplyToPosts = from tab in _statuses.Tabs.Values
7369                                  orderby tab != curTabClass
7370                                  from post in tab.Posts.Values
7371                                  where post.StatusId == inReplyToId
7372                                  let index = tab.IndexOf(post.StatusId)
7373                                  where index != -1
7374                                  select new {Tab = tab, Index = index};
7375
7376             try
7377             {
7378                 var inReplyPost = inReplyToPosts.First();
7379                 inReplyToTabName = inReplyPost.Tab.TabName;
7380                 inReplyToIndex = inReplyPost.Index;
7381             }
7382             catch (InvalidOperationException)
7383             {
7384                 PostClass post = null;
7385                 string r = tw.GetStatusApi(false, _curPost.InReplyToStatusId.Value, ref post);
7386                 if (string.IsNullOrEmpty(r) && post != null)
7387                 {
7388                     post.IsRead = true;
7389                     _statuses.AddPost(post);
7390                     _statuses.DistributePosts();
7391                     //_statuses.SubmitUpdate(null, null, null, false);
7392                     this.RefreshTimeline(false);
7393                     try
7394                     {
7395                         var inReplyPost = inReplyToPosts.First();
7396                         inReplyToTabName = inReplyPost.Tab.TabName;
7397                         inReplyToIndex = inReplyPost.Index;
7398                     }
7399                     catch (InvalidOperationException)
7400                     {
7401                         OpenUriAsync("https://twitter.com/" + inReplyToUser + "/statuses/" + inReplyToId.ToString());
7402                         return;
7403                     }
7404                 }
7405                 else
7406                 {
7407                     this.StatusLabel.Text = r;
7408                     OpenUriAsync("https://twitter.com/" + inReplyToUser + "/statuses/" + inReplyToId.ToString());
7409                     return;
7410                 }
7411             }
7412
7413             TabPage tabPage = this.ListTab.TabPages.Cast<TabPage>().First((tp) => { return tp.Text == inReplyToTabName; });
7414             DetailsListView listView = (DetailsListView)tabPage.Tag;
7415
7416             if (_curTab != tabPage)
7417             {
7418                 this.ListTab.SelectTab(tabPage);
7419             }
7420
7421             this.SelectListItem(listView, inReplyToIndex);
7422             listView.EnsureVisible(inReplyToIndex);
7423         }
7424
7425         private void GoBackInReplyToPostTree(bool parallel = false, bool isForward = true)
7426         {
7427             if (_curPost == null) return;
7428
7429             TabClass curTabClass = _statuses.Tabs[_curTab.Text];
7430             //Dictionary<long, PostClass> curTabPosts = curTabClass.Posts;
7431
7432             if (parallel)
7433             {
7434                 if (_curPost.InReplyToStatusId != null)
7435                 {
7436                     var posts = from t in _statuses.Tabs
7437                                 from p in t.Value.Posts
7438                                 where p.Value.StatusId != _curPost.StatusId && p.Value.InReplyToStatusId == _curPost.InReplyToStatusId
7439                                 let indexOf = t.Value.IndexOf(p.Value.StatusId)
7440                                 where indexOf > -1
7441                                 orderby isForward ? indexOf : indexOf * -1
7442                                 orderby t.Value != curTabClass
7443                                 select new {Tab = t.Value, Post = p.Value, Index = indexOf};
7444                     try
7445                     {
7446                         var postList = posts.ToList();
7447                         for (int i = postList.Count - 1; i >= 0; i--)
7448                         {
7449                             int index = i;
7450                             if (postList.FindIndex((pst) => { return pst.Post.StatusId == postList[index].Post.StatusId; }) != index)
7451                             {
7452                                 postList.RemoveAt(index);
7453                             }
7454                         }
7455                         var post = postList.FirstOrDefault((pst) => { return pst.Tab == curTabClass && isForward ? pst.Index > _curItemIndex : pst.Index < _curItemIndex; });
7456                         if (post == null) post = postList.FirstOrDefault((pst) => { return pst.Tab != curTabClass; });
7457                         if (post == null) post = postList.First();
7458                         this.ListTab.SelectTab(this.ListTab.TabPages.Cast<TabPage>().First((tp) => { return tp.Text == post.Tab.TabName; }));
7459                         DetailsListView listView = (DetailsListView)this.ListTab.SelectedTab.Tag;
7460                         SelectListItem(listView, post.Index);
7461                         listView.EnsureVisible(post.Index);
7462                     }
7463                     catch (InvalidOperationException)
7464                     {
7465                         return;
7466                     }
7467                 }
7468             }
7469             else
7470             {
7471                 if (replyChains == null || replyChains.Count < 1)
7472                 {
7473                     var posts = from t in _statuses.Tabs
7474                                 from p in t.Value.Posts
7475                                 where p.Value.InReplyToStatusId == _curPost.StatusId
7476                                 let indexOf = t.Value.IndexOf(p.Value.StatusId)
7477                                 where indexOf > -1
7478                                 orderby indexOf
7479                                 orderby t.Value != curTabClass
7480                                 select new {Tab = t.Value, Index = indexOf};
7481                     try
7482                     {
7483                         var post = posts.First();
7484                         this.ListTab.SelectTab(this.ListTab.TabPages.Cast<TabPage>().First((tp) => { return tp.Text == post.Tab.TabName; }));
7485                         DetailsListView listView = (DetailsListView)this.ListTab.SelectedTab.Tag;
7486                         SelectListItem(listView, post.Index);
7487                         listView.EnsureVisible(post.Index);
7488                     }
7489                     catch (InvalidOperationException)
7490                     {
7491                         return;
7492                     }
7493                 }
7494                 else
7495                 {
7496                     ReplyChain chainHead = replyChains.Pop();
7497                     if (chainHead.InReplyToId == _curPost.StatusId)
7498                     {
7499                         int idx = _statuses.Tabs[chainHead.OriginalTab.Text].IndexOf(chainHead.OriginalId);
7500                         if (idx == -1)
7501                         {
7502                             replyChains = null;
7503                         }
7504                         else
7505                         {
7506                             try
7507                             {
7508                                 ListTab.SelectTab(chainHead.OriginalTab);
7509                             }
7510                             catch (Exception)
7511                             {
7512                                 replyChains = null;
7513                             }
7514                             SelectListItem(_curList, idx);
7515                             _curList.EnsureVisible(idx);
7516                         }
7517                     }
7518                     else
7519                     {
7520                         replyChains = null;
7521                         this.GoBackInReplyToPostTree(parallel);
7522                     }
7523                 }
7524             }
7525         }
7526
7527         private void GoBackSelectPostChain()
7528         {
7529             if (this.selectPostChains.Count > 1)
7530             {
7531                 var idx = -1;
7532                 TabPage tp = null;
7533
7534                 do
7535                 {
7536                     try
7537                     {
7538                         this.selectPostChains.Pop();
7539                         var tabPostPair = this.selectPostChains.Peek();
7540
7541                         if (!this.ListTab.TabPages.Contains(tabPostPair.Item1)) continue;  //該当タブが存在しないので無視
7542
7543                         if (tabPostPair.Item2 != null)
7544                         {
7545                             idx = this._statuses.Tabs[tabPostPair.Item1.Text].IndexOf(tabPostPair.Item2.StatusId);
7546                             if (idx == -1) continue;  //該当ポストが存在しないので無視
7547                         }
7548
7549                         tp = tabPostPair.Item1;
7550
7551                         this.selectPostChains.Pop();
7552                     }
7553                     catch (InvalidOperationException)
7554                     {
7555                     }
7556
7557                     break;
7558                 }
7559                 while (this.selectPostChains.Count > 1);
7560
7561                 if (tp == null)
7562                 {
7563                     //状態がおかしいので処理を中断
7564                     //履歴が残り1つであればクリアしておく
7565                     if (this.selectPostChains.Count == 1)
7566                         this.selectPostChains.Clear();
7567                     return;
7568                 }
7569
7570                 DetailsListView lst = (DetailsListView)tp.Tag;
7571                 this.ListTab.SelectedTab = tp;
7572                 if (idx > -1)
7573                 {
7574                     SelectListItem(lst, idx);
7575                     lst.EnsureVisible(idx);
7576                 }
7577                 lst.Focus();
7578             }
7579         }
7580
7581         private void PushSelectPostChain()
7582         {
7583             int count = this.selectPostChains.Count;
7584             if (count > 0)
7585             {
7586                 var p = this.selectPostChains.Peek();
7587                 if (p.Item1 == this._curTab)
7588                 {
7589                     if (p.Item2 == this._curPost) return;  //最新の履歴と同一
7590                     if (p.Item2 == null) this.selectPostChains.Pop();  //置き換えるため削除
7591                 }
7592             }
7593             if (count >= 2500) TrimPostChain();
7594             this.selectPostChains.Push(Tuple.Create(this._curTab, this._curPost));
7595         }
7596
7597         private void TrimPostChain()
7598         {
7599             if (this.selectPostChains.Count <= 2000) return;
7600             var p = new Stack<Tuple<TabPage, PostClass>>(2000);
7601             for (int i = 0; i < 2000; i++)
7602             {
7603                 p.Push(this.selectPostChains.Pop());
7604             }
7605             this.selectPostChains.Clear();
7606             for (int i = 0; i < 2000; i++)
7607             {
7608                 this.selectPostChains.Push(p.Pop());
7609             }
7610         }
7611
7612         private bool GoStatus(long statusId)
7613         {
7614             if (statusId == 0) return false;
7615             for (int tabidx = 0; tabidx < ListTab.TabCount; tabidx++)
7616             {
7617                 if (_statuses.Tabs[ListTab.TabPages[tabidx].Text].TabType != MyCommon.TabUsageType.DirectMessage && _statuses.Tabs[ListTab.TabPages[tabidx].Text].Contains(statusId))
7618                 {
7619                     int idx = _statuses.Tabs[ListTab.TabPages[tabidx].Text].IndexOf(statusId);
7620                     ListTab.SelectedIndex = tabidx;
7621                     ListTabSelect(ListTab.TabPages[tabidx]);
7622                     SelectListItem(_curList, idx);
7623                     _curList.EnsureVisible(idx);
7624                     return true;
7625                 }
7626             }
7627             return false;
7628         }
7629
7630         private bool GoDirectMessage(long statusId)
7631         {
7632             if (statusId == 0) return false;
7633             for (int tabidx = 0; tabidx < ListTab.TabCount; tabidx++)
7634             {
7635                 if (_statuses.Tabs[ListTab.TabPages[tabidx].Text].TabType == MyCommon.TabUsageType.DirectMessage && _statuses.Tabs[ListTab.TabPages[tabidx].Text].Contains(statusId))
7636                 {
7637                     int idx = _statuses.Tabs[ListTab.TabPages[tabidx].Text].IndexOf(statusId);
7638                     ListTab.SelectedIndex = tabidx;
7639                     ListTabSelect(ListTab.TabPages[tabidx]);
7640                     SelectListItem(_curList, idx);
7641                     _curList.EnsureVisible(idx);
7642                     return true;
7643                 }
7644             }
7645             return false;
7646         }
7647
7648         private void MyList_MouseClick(object sender, MouseEventArgs e)
7649         {
7650             _anchorFlag = false;
7651         }
7652
7653         private void StatusText_Enter(object sender, EventArgs e)
7654         {
7655             // フォーカスの戻り先を StatusText に設定
7656             this.Tag = StatusText;
7657             StatusText.BackColor = _clInputBackcolor;
7658         }
7659
7660         public Color InputBackColor
7661         {
7662             get { return _clInputBackcolor; }
7663             set { _clInputBackcolor = value; }
7664         }
7665
7666         private void StatusText_Leave(object sender, EventArgs e)
7667         {
7668             // フォーカスがメニューに遷移しないならばフォーカスはタブに移ることを期待
7669             if (ListTab.SelectedTab != null && MenuStrip1.Tag == null) this.Tag = ListTab.SelectedTab.Tag;
7670             StatusText.BackColor = Color.FromKnownColor(KnownColor.Window);
7671         }
7672
7673         private void StatusText_KeyDown(object sender, KeyEventArgs e)
7674         {
7675             ModifierState State = GetModifierState(e.Control, e.Shift, e.Alt);
7676             if (State == ModifierState.NotFlags) return;
7677             if (CommonKeyDown(e.KeyCode, FocusedControl.StatusText, State))
7678             {
7679                 e.Handled = true;
7680                 e.SuppressKeyPress = true;
7681             }
7682
7683             this.StatusText_TextChanged(null, null);
7684         }
7685
7686         private void SaveConfigsAll(bool ifModified)
7687         {
7688             if (!ifModified)
7689             {
7690                 SaveConfigsCommon();
7691                 SaveConfigsLocal();
7692                 SaveConfigsTabs();
7693                 SaveConfigsAtId();
7694             }
7695             else
7696             {
7697                 if (_modifySettingCommon) SaveConfigsCommon();
7698                 if (_modifySettingLocal) SaveConfigsLocal();
7699                 if (_modifySettingAtId) SaveConfigsAtId();
7700             }
7701         }
7702
7703         private void SaveConfigsAtId()
7704         {
7705             if (_ignoreConfigSave || !this._cfgCommon.UseAtIdSupplement && AtIdSupl == null) return;
7706
7707             _modifySettingAtId = false;
7708             SettingAtIdList cfgAtId = new SettingAtIdList(AtIdSupl.GetItemList());
7709             cfgAtId.Save();
7710         }
7711
7712         private void SaveConfigsCommon()
7713         {
7714             if (_ignoreConfigSave) return;
7715
7716             _modifySettingCommon = false;
7717             lock (_syncObject)
7718             {
7719                 _cfgCommon.UserName = tw.Username;
7720                 _cfgCommon.UserId = tw.UserId;
7721                 _cfgCommon.Password = tw.Password;
7722                 _cfgCommon.Token = tw.AccessToken;
7723                 _cfgCommon.TokenSecret = tw.AccessTokenSecret;
7724                 _cfgCommon.UserAccounts = SettingDialog.UserAccounts;
7725                 _cfgCommon.UnreadManage = SettingDialog.UnreadManage;
7726                 _cfgCommon.PlaySound = SettingDialog.PlaySound;
7727
7728                 _cfgCommon.NameBalloon = SettingDialog.NameBalloon;
7729                 _cfgCommon.CountApi = SettingDialog.CountApi;
7730                 _cfgCommon.CountApiReply = SettingDialog.CountApiReply;
7731                 _cfgCommon.DispUsername = SettingDialog.DispUsername;
7732                 _cfgCommon.MinimizeToTray = SettingDialog.MinimizeToTray;
7733                 _cfgCommon.CloseToExit = SettingDialog.CloseToExit;
7734                 _cfgCommon.DispLatestPost = SettingDialog.DispLatestPost;
7735                 _cfgCommon.TinyUrlResolve = SettingDialog.TinyUrlResolve;
7736                 _cfgCommon.RestrictFavCheck = SettingDialog.RestrictFavCheck;
7737                 _cfgCommon.AlwaysTop = SettingDialog.AlwaysTop;
7738                 _cfgCommon.UrlConvertAuto = SettingDialog.UrlConvertAuto;
7739                 _cfgCommon.DefaultTimeOut = SettingDialog.DefaultTimeOut;
7740                 _cfgCommon.LimitBalloon = SettingDialog.LimitBalloon;
7741                 _cfgCommon.EventNotifyEnabled = SettingDialog.EventNotifyEnabled;
7742                 _cfgCommon.EventNotifyFlag = SettingDialog.EventNotifyFlag;
7743                 _cfgCommon.IsMyEventNotifyFlag = SettingDialog.IsMyEventNotifyFlag;
7744                 _cfgCommon.ForceEventNotify = SettingDialog.ForceEventNotify;
7745                 _cfgCommon.FavEventUnread = SettingDialog.FavEventUnread;
7746                 _cfgCommon.TranslateLanguage = SettingDialog.TranslateLanguage;
7747                 _cfgCommon.EventSoundFile = SettingDialog.EventSoundFile;
7748                 _cfgCommon.AutoShortUrlFirst = SettingDialog.AutoShortUrlFirst;
7749                 _cfgCommon.TabIconDisp = SettingDialog.TabIconDisp;
7750                 _cfgCommon.ReplyIconState = SettingDialog.ReplyIconState;
7751                 _cfgCommon.ReadOwnPost = SettingDialog.ReadOwnPost;
7752                 _cfgCommon.IsMonospace = SettingDialog.IsMonospace;
7753                 if (IdeographicSpaceToSpaceToolStripMenuItem != null &&
7754                    IdeographicSpaceToSpaceToolStripMenuItem.IsDisposed == false)
7755                 {
7756                     _cfgCommon.WideSpaceConvert = this.IdeographicSpaceToSpaceToolStripMenuItem.Checked;
7757                 }
7758                 _cfgCommon.ReadOldPosts = SettingDialog.ReadOldPosts;
7759                 _cfgCommon.BilyUser = SettingDialog.BitlyUser;
7760                 _cfgCommon.BitlyPwd = SettingDialog.BitlyPwd;
7761                 _cfgCommon.PreviewEnable = SettingDialog.PreviewEnable;
7762                 _cfgCommon.StatusAreaAtBottom = SettingDialog.StatusAreaAtBottom;
7763                 _cfgCommon.Language = SettingDialog.Language;
7764
7765                 _cfgCommon.SortOrder = (int)_statuses.SortOrder;
7766                 switch (_statuses.SortMode)
7767                 {
7768                     case IdComparerClass.ComparerMode.Nickname:  //ニックネーム
7769                         _cfgCommon.SortColumn = 1;
7770                         break;
7771                     case IdComparerClass.ComparerMode.Data:  //本文
7772                         _cfgCommon.SortColumn = 2;
7773                         break;
7774                     case IdComparerClass.ComparerMode.Id:  //時刻=発言Id
7775                         _cfgCommon.SortColumn = 3;
7776                         break;
7777                     case IdComparerClass.ComparerMode.Name:  //名前
7778                         _cfgCommon.SortColumn = 4;
7779                         break;
7780                     case IdComparerClass.ComparerMode.Source:  //Source
7781                         _cfgCommon.SortColumn = 7;
7782                         break;
7783                 }
7784
7785                 _cfgCommon.Nicoms = SettingDialog.Nicoms;
7786                 _cfgCommon.HashTags = HashMgr.HashHistories;
7787                 if (HashMgr.IsPermanent)
7788                 {
7789                     _cfgCommon.HashSelected = HashMgr.UseHash;
7790                 }
7791                 else
7792                 {
7793                     _cfgCommon.HashSelected = "";
7794                 }
7795                 _cfgCommon.HashIsHead = HashMgr.IsHead;
7796                 _cfgCommon.HashIsPermanent = HashMgr.IsPermanent;
7797                 _cfgCommon.HashIsNotAddToAtReply = HashMgr.IsNotAddToAtReply;
7798                 _cfgCommon.TwitterUrl = SettingDialog.TwitterApiUrl;
7799                 _cfgCommon.HotkeyEnabled = SettingDialog.HotkeyEnabled;
7800                 _cfgCommon.HotkeyModifier = SettingDialog.HotkeyMod;
7801                 _cfgCommon.HotkeyKey = SettingDialog.HotkeyKey;
7802                 _cfgCommon.HotkeyValue = SettingDialog.HotkeyValue;
7803                 _cfgCommon.BlinkNewMentions = SettingDialog.BlinkNewMentions;
7804                 if (ToolStripFocusLockMenuItem != null &&
7805                         ToolStripFocusLockMenuItem.IsDisposed == false)
7806                 {
7807                     _cfgCommon.FocusLockToStatusText = this.ToolStripFocusLockMenuItem.Checked;
7808                 }
7809                 _cfgCommon.UseAdditionalCount = SettingDialog.UseAdditionalCount;
7810                 _cfgCommon.MoreCountApi = SettingDialog.MoreCountApi;
7811                 _cfgCommon.FirstCountApi = SettingDialog.FirstCountApi;
7812                 _cfgCommon.SearchCountApi = SettingDialog.SearchCountApi;
7813                 _cfgCommon.FavoritesCountApi = SettingDialog.FavoritesCountApi;
7814                 _cfgCommon.UserTimelineCountApi = SettingDialog.UserTimelineCountApi;
7815                 _cfgCommon.TrackWord = tw.TrackWord;
7816                 _cfgCommon.AllAtReply = tw.AllAtReply;
7817                 _cfgCommon.OpenUserTimeline = SettingDialog.OpenUserTimeline;
7818                 _cfgCommon.ListCountApi = SettingDialog.ListCountApi;
7819                 _cfgCommon.UseImageService = ImageSelector.ServiceIndex;
7820                 _cfgCommon.UseImageServiceName = ImageSelector.ServiceName;
7821                 _cfgCommon.ListDoubleClickAction = SettingDialog.ListDoubleClickAction;
7822                 _cfgCommon.UserAppointUrl = SettingDialog.UserAppointUrl;
7823                 _cfgCommon.EnableImgAzyobuziNet = SettingDialog.EnableImgAzyobuziNet;
7824                 _cfgCommon.ImgAzyobuziNetDisabledInDM = SettingDialog.ImgAzyobuziNetDisabledInDM;
7825                 _cfgCommon.MapThumbnailProvider = SettingDialog.MapThumbnailProvider;
7826                 _cfgCommon.MapThumbnailHeight = SettingDialog.MapThumbnailHeight;
7827                 _cfgCommon.MapThumbnailWidth = SettingDialog.MapThumbnailWidth;
7828                 _cfgCommon.MapThumbnailZoom = SettingDialog.MapThumbnailZoom;
7829                 _cfgCommon.TabMouseLock = SettingDialog.TabMouseLock;
7830                 _cfgCommon.IsRemoveSameEvent = SettingDialog.IsRemoveSameEvent;
7831                 _cfgCommon.IsUseNotifyGrowl = SettingDialog.IsNotifyUseGrowl;
7832
7833                 _cfgCommon.Save();
7834             }
7835         }
7836
7837         private void SaveConfigsLocal()
7838         {
7839             if (_ignoreConfigSave) return;
7840             lock (_syncObject)
7841             {
7842                 _modifySettingLocal = false;
7843                 _cfgLocal.FormSize = _mySize;
7844                 _cfgLocal.FormLocation = _myLoc;
7845                 _cfgLocal.SplitterDistance = _mySpDis;
7846                 _cfgLocal.PreviewDistance = _mySpDis3;
7847                 _cfgLocal.StatusMultiline = StatusText.Multiline;
7848                 _cfgLocal.StatusTextHeight = _mySpDis2;
7849
7850                 _cfgLocal.FontUnread = _fntUnread;
7851                 _cfgLocal.ColorUnread = _clUnread;
7852                 _cfgLocal.FontRead = _fntReaded;
7853                 _cfgLocal.ColorRead = _clReaded;
7854                 _cfgLocal.FontDetail = _fntDetail;
7855                 _cfgLocal.ColorDetail = _clDetail;
7856                 _cfgLocal.ColorDetailBackcolor = _clDetailBackcolor;
7857                 _cfgLocal.ColorDetailLink = _clDetailLink;
7858                 _cfgLocal.ColorFav = _clFav;
7859                 _cfgLocal.ColorOWL = _clOWL;
7860                 _cfgLocal.ColorRetweet = _clRetweet;
7861                 _cfgLocal.ColorSelf = _clSelf;
7862                 _cfgLocal.ColorAtSelf = _clAtSelf;
7863                 _cfgLocal.ColorTarget = _clTarget;
7864                 _cfgLocal.ColorAtTarget = _clAtTarget;
7865                 _cfgLocal.ColorAtFromTarget = _clAtFromTarget;
7866                 _cfgLocal.ColorAtTo = _clAtTo;
7867                 _cfgLocal.ColorListBackcolor = _clListBackcolor;
7868                 _cfgLocal.ColorInputBackcolor = _clInputBackcolor;
7869                 _cfgLocal.ColorInputFont = _clInputFont;
7870                 _cfgLocal.FontInputFont = _fntInputFont;
7871
7872                 _cfgLocal.BrowserPath = SettingDialog.BrowserPath;
7873                 _cfgLocal.ProxyType = SettingDialog.SelectedProxyType;
7874                 _cfgLocal.ProxyAddress = SettingDialog.ProxyAddress;
7875                 _cfgLocal.ProxyPort = SettingDialog.ProxyPort;
7876                 _cfgLocal.ProxyUser = SettingDialog.ProxyUser;
7877                 _cfgLocal.ProxyPassword = SettingDialog.ProxyPassword;
7878                 if (_ignoreConfigSave) return;
7879                 _cfgLocal.Save();
7880             }
7881         }
7882
7883         private void SaveConfigsTabs()
7884         {
7885             SettingTabs tabSetting = new SettingTabs();
7886             for (int i = 0; i < ListTab.TabPages.Count; i++)
7887             {
7888                 if (_statuses.Tabs[ListTab.TabPages[i].Text].TabType != MyCommon.TabUsageType.Related) tabSetting.Tabs.Add(_statuses.Tabs[ListTab.TabPages[i].Text]);
7889             }
7890             tabSetting.Save();
7891         }
7892
7893         private async void OpenURLFileMenuItem_Click(object sender, EventArgs e)
7894         {
7895             string inputText;
7896             var ret = InputDialog.Show(this, Properties.Resources.OpenURL_InputText, Properties.Resources.OpenURL_Caption, out inputText);
7897             if (ret != DialogResult.OK)
7898                 return;
7899
7900             var match = Twitter.StatusUrlRegex.Match(inputText);
7901             if (!match.Success)
7902             {
7903                 MessageBox.Show(this, Properties.Resources.OpenURL_InvalidFormat,
7904                     Properties.Resources.OpenURL_Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
7905                 return;
7906             }
7907
7908             var statusId = long.Parse(match.Groups["StatusId"].Value);
7909
7910             var post = this._statuses[statusId];
7911             if (post == null)
7912             {
7913                 try
7914                 {
7915                     post = await Task.Run(() =>
7916                     {
7917                         PostClass newPost = null;
7918
7919                         var err = this.tw.GetStatusApi(false, statusId, ref newPost);
7920                         if (!string.IsNullOrEmpty(err))
7921                             throw new WebApiException(err);
7922
7923                         return newPost;
7924                     });
7925                 }
7926                 catch (WebApiException ex)
7927                 {
7928                     var message = ex.Message;
7929                     MessageBox.Show(this, string.Format(Properties.Resources.OpenURL_LoadFailed, message),
7930                         Properties.Resources.OpenURL_Caption, MessageBoxButtons.OK, MessageBoxIcon.Error);
7931                     return;
7932                 }
7933             }
7934
7935             try
7936             {
7937                 this.OpenRelatedTab(post);
7938             }
7939             catch (TabException ex)
7940             {
7941                 MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
7942             }
7943         }
7944
7945         private void SaveLogMenuItem_Click(object sender, EventArgs e)
7946         {
7947             DialogResult rslt = MessageBox.Show(string.Format(Properties.Resources.SaveLogMenuItem_ClickText1, Environment.NewLine),
7948                     Properties.Resources.SaveLogMenuItem_ClickText2,
7949                     MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
7950             if (rslt == DialogResult.Cancel) return;
7951
7952             SaveFileDialog1.FileName = MyCommon.GetAssemblyName() + "Posts" + DateTime.Now.ToString("yyMMdd-HHmmss") + ".tsv";
7953             SaveFileDialog1.InitialDirectory = Application.ExecutablePath;
7954             SaveFileDialog1.Filter = Properties.Resources.SaveLogMenuItem_ClickText3;
7955             SaveFileDialog1.FilterIndex = 0;
7956             SaveFileDialog1.Title = Properties.Resources.SaveLogMenuItem_ClickText4;
7957             SaveFileDialog1.RestoreDirectory = true;
7958
7959             if (SaveFileDialog1.ShowDialog() == DialogResult.OK)
7960             {
7961                 if (!SaveFileDialog1.ValidateNames) return;
7962                 using (StreamWriter sw = new StreamWriter(SaveFileDialog1.FileName, false, Encoding.UTF8))
7963                 {
7964                     if (rslt == DialogResult.Yes)
7965                     {
7966                         //All
7967                         for (int idx = 0; idx < _curList.VirtualListSize; idx++)
7968                         {
7969                             PostClass post = _statuses[_curTab.Text, idx];
7970                             string protect = "";
7971                             if (post.IsProtect) protect = "Protect";
7972                             sw.WriteLine(post.Nickname + "\t" +
7973                                      "\"" + post.TextFromApi.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" +
7974                                      post.CreatedAt.ToString() + "\t" +
7975                                      post.ScreenName + "\t" +
7976                                      post.StatusId.ToString() + "\t" +
7977                                      post.ImageUrl + "\t" +
7978                                      "\"" + post.Text.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" +
7979                                      protect);
7980                         }
7981                     }
7982                     else
7983                     {
7984                         foreach (int idx in _curList.SelectedIndices)
7985                         {
7986                             PostClass post = _statuses[_curTab.Text, idx];
7987                             string protect = "";
7988                             if (post.IsProtect) protect = "Protect";
7989                             sw.WriteLine(post.Nickname + "\t" +
7990                                      "\"" + post.TextFromApi.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" +
7991                                      post.CreatedAt.ToString() + "\t" +
7992                                      post.ScreenName + "\t" +
7993                                      post.StatusId.ToString() + "\t" +
7994                                      post.ImageUrl + "\t" +
7995                                      "\"" + post.Text.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" +
7996                                      protect);
7997                         }
7998                     }
7999                 }
8000             }
8001             this.TopMost = SettingDialog.AlwaysTop;
8002         }
8003
8004         private void PostBrowser_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
8005         {
8006             ModifierState State = GetModifierState(e.Control, e.Shift, e.Alt);
8007             if (State == ModifierState.NotFlags) return;
8008             bool KeyRes = CommonKeyDown(e.KeyCode, FocusedControl.PostBrowser, State);
8009             if (KeyRes)
8010             {
8011                 e.IsInputKey = true;
8012             }
8013         }
8014         public bool TabRename(ref string tabName)
8015         {
8016             //タブ名変更
8017             string newTabText = null;
8018             using (InputTabName inputName = new InputTabName())
8019             {
8020                 inputName.TabName = tabName;
8021                 inputName.ShowDialog();
8022                 if (inputName.DialogResult == DialogResult.Cancel) return false;
8023                 newTabText = inputName.TabName;
8024             }
8025             this.TopMost = SettingDialog.AlwaysTop;
8026             if (!string.IsNullOrEmpty(newTabText))
8027             {
8028                 //新タブ名存在チェック
8029                 for (int i = 0; i < ListTab.TabCount; i++)
8030                 {
8031                     if (ListTab.TabPages[i].Text == newTabText)
8032                     {
8033                         string tmp = string.Format(Properties.Resources.Tabs_DoubleClickText1, newTabText);
8034                         MessageBox.Show(tmp, Properties.Resources.Tabs_DoubleClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
8035                         return false;
8036                     }
8037                 }
8038                 //タブ名のリスト作り直し(デフォルトタブ以外は再作成)
8039                 for (int i = 0; i < ListTab.TabCount; i++)
8040                 {
8041                     if (ListTab.TabPages[i].Text == tabName)
8042                     {
8043                         ListTab.TabPages[i].Text = newTabText;
8044                     }
8045                 }
8046                 _statuses.RenameTab(tabName, newTabText);
8047
8048                 for (int i = 0; i < ListTab.TabCount; i++)
8049                 {
8050                     if (_statuses.IsDistributableTab(ListTab.TabPages[i].Text))
8051                     {
8052                         if (ListTab.TabPages[i].Text == tabName)
8053                         {
8054                             ListTab.TabPages[i].Text = newTabText;
8055                         }
8056                     }
8057                 }
8058                 SaveConfigsCommon();
8059                 SaveConfigsTabs();
8060                 _rclickTabName = newTabText;
8061                 tabName = newTabText;
8062                 return true;
8063             }
8064             else
8065             {
8066                 return false;
8067             }
8068         }
8069
8070         private void ListTab_MouseClick(object sender, MouseEventArgs e)
8071         {
8072             if (e.Button == MouseButtons.Middle)
8073             {
8074                 for (int i = 0; i < this.ListTab.TabPages.Count; i++)
8075                 {
8076                     if (this.ListTab.GetTabRect(i).Contains(e.Location))
8077                     {
8078                         this.RemoveSpecifiedTab(this.ListTab.TabPages[i].Text, true);
8079                         this.SaveConfigsTabs();
8080                         break;
8081                     }
8082                 }
8083             }
8084         }
8085
8086         private void ListTab_DoubleClick(object sender, MouseEventArgs e)
8087         {
8088             string tn = ListTab.SelectedTab.Text;
8089             TabRename(ref tn);
8090         }
8091
8092         private void ListTab_MouseDown(object sender, MouseEventArgs e)
8093         {
8094             if (SettingDialog.TabMouseLock) return;
8095             Point cpos = new Point(e.X, e.Y);
8096             if (e.Button == MouseButtons.Left)
8097             {
8098                 for (int i = 0; i < ListTab.TabPages.Count; i++)
8099                 {
8100                     if (this.ListTab.GetTabRect(i).Contains(e.Location))
8101                     {
8102                         _tabDrag = true;
8103                         _tabMouseDownPoint = e.Location;
8104                         break;
8105                     }
8106                 }
8107             }
8108             else
8109             {
8110                 _tabDrag = false;
8111             }
8112         }
8113
8114         private void ListTab_DragEnter(object sender, DragEventArgs e)
8115         {
8116             if (e.Data.GetDataPresent(typeof(TabPage)))
8117                 e.Effect = DragDropEffects.Move;
8118             else
8119                 e.Effect = DragDropEffects.None;
8120         }
8121
8122         private void ListTab_DragDrop(object sender, DragEventArgs e)
8123         {
8124             if (!e.Data.GetDataPresent(typeof(TabPage))) return;
8125
8126             _tabDrag = false;
8127             string tn = "";
8128             bool bef = false;
8129             Point cpos = new Point(e.X, e.Y);
8130             Point spos = ListTab.PointToClient(cpos);
8131             int i;
8132             for (i = 0; i < ListTab.TabPages.Count; i++)
8133             {
8134                 Rectangle rect = ListTab.GetTabRect(i);
8135                 if (rect.Left <= spos.X && spos.X <= rect.Right &&
8136                     rect.Top <= spos.Y && spos.Y <= rect.Bottom)
8137                 {
8138                     tn = ListTab.TabPages[i].Text;
8139                     if (spos.X <= (rect.Left + rect.Right) / 2)
8140                         bef = true;
8141                     else
8142                         bef = false;
8143
8144                     break;
8145                 }
8146             }
8147
8148             //タブのないところにドロップ->最後尾へ移動
8149             if (string.IsNullOrEmpty(tn))
8150             {
8151                 tn = ListTab.TabPages[ListTab.TabPages.Count - 1].Text;
8152                 bef = false;
8153                 i = ListTab.TabPages.Count - 1;
8154             }
8155
8156             TabPage tp = (TabPage)e.Data.GetData(typeof(TabPage));
8157             if (tp.Text == tn) return;
8158
8159             ReOrderTab(tp.Text, tn, bef);
8160         }
8161
8162         public void ReOrderTab(string targetTabText, string baseTabText, bool isBeforeBaseTab)
8163         {
8164             int baseIndex = 0;
8165             for (baseIndex = 0; baseIndex < ListTab.TabPages.Count; baseIndex++)
8166             {
8167                 if (ListTab.TabPages[baseIndex].Text == baseTabText) break;
8168             }
8169
8170             using (ControlTransaction.Layout(this.ListTab))
8171             {
8172                 TabPage mTp = null;
8173                 for (int j = 0; j < ListTab.TabPages.Count; j++)
8174                 {
8175                     if (ListTab.TabPages[j].Text == targetTabText)
8176                     {
8177                         mTp = ListTab.TabPages[j];
8178                         ListTab.TabPages.Remove(mTp);
8179                         if (j < baseIndex) baseIndex -= 1;
8180                         break;
8181                     }
8182                 }
8183                 if (isBeforeBaseTab)
8184                     ListTab.TabPages.Insert(baseIndex, mTp);
8185                 else
8186                     ListTab.TabPages.Insert(baseIndex + 1, mTp);
8187             }
8188
8189             SaveConfigsTabs();
8190         }
8191
8192         private void MakeReplyOrDirectStatus(bool isAuto = true, bool isReply = true, bool isAll = false)
8193         {
8194             //isAuto:true=先頭に挿入、false=カーソル位置に挿入
8195             //isReply:true=@,false=DM
8196             if (!StatusText.Enabled) return;
8197             if (_curList == null) return;
8198             if (_curTab == null) return;
8199             if (!this.ExistCurrentPost) return;
8200
8201             // 複数あてリプライはReplyではなく通常ポスト
8202             //↑仕様変更で全部リプライ扱いでOK(先頭ドット付加しない)
8203             //090403暫定でドットを付加しないようにだけ修正。単独と複数の処理は統合できると思われる。
8204             //090513 all @ replies 廃止の仕様変更によりドット付加に戻し(syo68k)
8205
8206             if (_curList.SelectedIndices.Count > 0)
8207             {
8208                 // アイテムが1件以上選択されている
8209                 if (_curList.SelectedIndices.Count == 1 && !isAll && this.ExistCurrentPost)
8210                 {
8211                     // 単独ユーザー宛リプライまたはDM
8212                     if ((_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage && isAuto) || (!isAuto && !isReply))
8213                     {
8214                         // ダイレクトメッセージ
8215                         StatusText.Text = "D " + _curPost.ScreenName + " " + StatusText.Text;
8216                         StatusText.SelectionStart = StatusText.Text.Length;
8217                         StatusText.Focus();
8218                         _reply_to_id = null;
8219                         _reply_to_name = null;
8220                         return;
8221                     }
8222                     if (string.IsNullOrEmpty(StatusText.Text))
8223                     {
8224                         //空の場合
8225
8226                         // ステータステキストが入力されていない場合先頭に@ユーザー名を追加する
8227                         StatusText.Text = "@" + _curPost.ScreenName + " ";
8228                         if (_curPost.RetweetedId != null)
8229                         {
8230                             _reply_to_id = _curPost.RetweetedId.Value;
8231                         }
8232                         else
8233                         {
8234                             _reply_to_id = _curPost.StatusId;
8235                         }
8236                         _reply_to_name = _curPost.ScreenName;
8237                     }
8238                     else
8239                     {
8240                         //何か入力済の場合
8241
8242                         if (isAuto)
8243                         {
8244                             //1件選んでEnter or DoubleClick
8245                             if (StatusText.Text.Contains("@" + _curPost.ScreenName + " "))
8246                             {
8247                                 if (_reply_to_id != null && _reply_to_name == _curPost.ScreenName)
8248                                 {
8249                                     //返信先書き換え
8250                                     if (_curPost.RetweetedId != null)
8251                                     {
8252                                         _reply_to_id = _curPost.RetweetedId.Value;
8253                                     }
8254                                     else
8255                                     {
8256                                         _reply_to_id = _curPost.StatusId;
8257                                     }
8258                                     _reply_to_name = _curPost.ScreenName;
8259                                 }
8260                                 return;
8261                             }
8262                             if (!StatusText.Text.StartsWith("@"))
8263                             {
8264                                 //文頭@以外
8265                                 if (StatusText.Text.StartsWith(". "))
8266                                 {
8267                                     // 複数リプライ
8268                                     StatusText.Text = StatusText.Text.Insert(2, "@" + _curPost.ScreenName + " ");
8269                                     _reply_to_id = null;
8270                                     _reply_to_name = null;
8271                                 }
8272                                 else
8273                                 {
8274                                     // 単独リプライ
8275                                     StatusText.Text = "@" + _curPost.ScreenName + " " + StatusText.Text;
8276                                     if (_curPost.RetweetedId != null)
8277                                     {
8278                                         _reply_to_id = _curPost.RetweetedId.Value;
8279                                     }
8280                                     else
8281                                     {
8282                                         _reply_to_id = _curPost.StatusId;
8283                                     }
8284                                     _reply_to_name = _curPost.ScreenName;
8285                                 }
8286                             }
8287                             else
8288                             {
8289                                 //文頭@
8290                                 // 複数リプライ
8291                                 StatusText.Text = ". @" + _curPost.ScreenName + " " + StatusText.Text;
8292                                 //StatusText.Text = "@" + _curPost.ScreenName + " " + StatusText.Text;
8293                                 _reply_to_id = null;
8294                                 _reply_to_name = null;
8295                             }
8296                         }
8297                         else
8298                         {
8299                             //1件選んでCtrl-Rの場合(返信先操作せず)
8300                             int sidx = StatusText.SelectionStart;
8301                             string id = "@" + _curPost.ScreenName + " ";
8302                             if (sidx > 0)
8303                             {
8304                                 if (StatusText.Text.Substring(sidx - 1, 1) != " ")
8305                                 {
8306                                     id = " " + id;
8307                                 }
8308                             }
8309                             StatusText.Text = StatusText.Text.Insert(sidx, id);
8310                             sidx += id.Length;
8311                             //if (StatusText.Text.StartsWith("@"))
8312                             //{
8313                             //    //複数リプライ
8314                             //    StatusText.Text = ". " + StatusText.Text.Insert(sidx, " @" + _curPost.ScreenName + " ");
8315                             //    sidx += 5 + _curPost.ScreenName.Length;
8316                             //}
8317                             //else
8318                             //{
8319                             //    // 複数リプライ
8320                             //    StatusText.Text = StatusText.Text.Insert(sidx, " @" + _curPost.ScreenName + " ");
8321                             //    sidx += 3 + _curPost.ScreenName.Length;
8322                             //}
8323                             StatusText.SelectionStart = sidx;
8324                             StatusText.Focus();
8325                             //_reply_to_id = 0;
8326                             //_reply_to_name = null;
8327                             return;
8328                         }
8329                     }
8330                 }
8331                 else
8332                 {
8333                     // 複数リプライ
8334                     if (!isAuto && !isReply) return;
8335
8336                     //C-S-rか、複数の宛先を選択中にEnter/DoubleClick/C-r/C-S-r
8337
8338                     if (isAuto)
8339                     {
8340                         //Enter or DoubleClick
8341
8342                         string sTxt = StatusText.Text;
8343                         if (!sTxt.StartsWith(". "))
8344                         {
8345                             sTxt = ". " + sTxt;
8346                             _reply_to_id = null;
8347                             _reply_to_name = null;
8348                         }
8349                         for (int cnt = 0; cnt < _curList.SelectedIndices.Count; cnt++)
8350                         {
8351                             PostClass post = _statuses[_curTab.Text, _curList.SelectedIndices[cnt]];
8352                             if (!sTxt.Contains("@" + post.ScreenName + " "))
8353                             {
8354                                 sTxt = sTxt.Insert(2, "@" + post.ScreenName + " ");
8355                                 //sTxt = "@" + post.ScreenName + " " + sTxt;
8356                             }
8357                         }
8358                         StatusText.Text = sTxt;
8359                     }
8360                     else
8361                     {
8362                         //C-S-r or C-r
8363                         if (_curList.SelectedIndices.Count > 1)
8364                         {
8365                             //複数ポスト選択
8366
8367                             string ids = "";
8368                             int sidx = StatusText.SelectionStart;
8369                             for (int cnt = 0; cnt < _curList.SelectedIndices.Count; cnt++)
8370                             {
8371                                 PostClass post = _statuses[_curTab.Text, _curList.SelectedIndices[cnt]];
8372                                 if (!ids.Contains("@" + post.ScreenName + " ") &&
8373                                     !post.ScreenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase))
8374                                 {
8375                                     ids += "@" + post.ScreenName + " ";
8376                                 }
8377                                 if (isAll)
8378                                 {
8379                                     foreach (string nm in post.ReplyToList)
8380                                     {
8381                                         if (!ids.Contains("@" + nm + " ") &&
8382                                             !nm.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase))
8383                                         {
8384                                             Match m = Regex.Match(post.TextFromApi, "[@@](?<id>" + nm + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase);
8385                                             if (m.Success)
8386                                                 ids += "@" + m.Result("${id}") + " ";
8387                                             else
8388                                                 ids += "@" + nm + " ";
8389                                         }
8390                                     }
8391                                 }
8392                             }
8393                             if (ids.Length == 0) return;
8394                             if (!StatusText.Text.StartsWith(". "))
8395                             {
8396                                 StatusText.Text = ". " + StatusText.Text;
8397                                 sidx += 2;
8398                                 _reply_to_id = null;
8399                                 _reply_to_name = null;
8400                             }
8401                             if (sidx > 0)
8402                             {
8403                                 if (StatusText.Text.Substring(sidx - 1, 1) != " ")
8404                                 {
8405                                     ids = " " + ids;
8406                                 }
8407                             }
8408                             StatusText.Text = StatusText.Text.Insert(sidx, ids);
8409                             sidx += ids.Length;
8410                             //if (StatusText.Text.StartsWith("@"))
8411                             //{
8412                             //    StatusText.Text = ". " + StatusText.Text.Insert(sidx, ids);
8413                             //    sidx += 2 + ids.Length;
8414                             //}
8415                             //else
8416                             //{
8417                             //    StatusText.Text = StatusText.Text.Insert(sidx, ids);
8418                             //    sidx += 1 + ids.Length;
8419                             //}
8420                             StatusText.SelectionStart = sidx;
8421                             StatusText.Focus();
8422                             return;
8423                         }
8424                         else
8425                         {
8426                             //1件のみ選択のC-S-r(返信元付加する可能性あり)
8427
8428                             string ids = "";
8429                             int sidx = StatusText.SelectionStart;
8430                             PostClass post = _curPost;
8431                             if (!ids.Contains("@" + post.ScreenName + " ") &&
8432                                 !post.ScreenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase))
8433                             {
8434                                 ids += "@" + post.ScreenName + " ";
8435                             }
8436                             foreach (string nm in post.ReplyToList)
8437                             {
8438                                 if (!ids.Contains("@" + nm + " ") &&
8439                                     !nm.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase))
8440                                 {
8441                                     Match m = Regex.Match(post.TextFromApi, "[@@](?<id>" + nm + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase);
8442                                     if (m.Success)
8443                                         ids += "@" + m.Result("${id}") + " ";
8444                                     else
8445                                         ids += "@" + nm + " ";
8446                                 }
8447                             }
8448                             if (!string.IsNullOrEmpty(post.RetweetedBy))
8449                             {
8450                                 if (!ids.Contains("@" + post.RetweetedBy + " ") &&
8451                                    !post.RetweetedBy.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase))
8452                                 {
8453                                     ids += "@" + post.RetweetedBy + " ";
8454                                 }
8455                             }
8456                             if (ids.Length == 0) return;
8457                             if (string.IsNullOrEmpty(StatusText.Text))
8458                             {
8459                                 //未入力の場合のみ返信先付加
8460                                 StatusText.Text = ids;
8461                                 StatusText.SelectionStart = ids.Length;
8462                                 StatusText.Focus();
8463                                 if (post.RetweetedId != null)
8464                                 {
8465                                     _reply_to_id = post.RetweetedId.Value;
8466                                 }
8467                                 else
8468                                 {
8469                                     _reply_to_id = post.StatusId;
8470                                 }
8471                                 _reply_to_name = post.ScreenName;
8472                                 return;
8473                             }
8474
8475                             if (sidx > 0)
8476                             {
8477                                 if (StatusText.Text.Substring(sidx - 1, 1) != " ")
8478                                 {
8479                                     ids = " " + ids;
8480                                 }
8481                             }
8482                             StatusText.Text = StatusText.Text.Insert(sidx, ids);
8483                             sidx += ids.Length;
8484                             StatusText.SelectionStart = sidx;
8485                             StatusText.Focus();
8486                             return;
8487                         }
8488                     }
8489                 }
8490                 StatusText.SelectionStart = StatusText.Text.Length;
8491                 StatusText.Focus();
8492             }
8493         }
8494
8495         private void ListTab_MouseUp(object sender, MouseEventArgs e)
8496         {
8497             _tabDrag = false;
8498         }
8499
8500         private static int iconCnt = 0;
8501         private static int blinkCnt = 0;
8502         private static bool blink = false;
8503         private static bool idle = false;
8504
8505         private void RefreshTasktrayIcon(bool forceRefresh)
8506         {
8507             if (_colorize) Colorize();
8508             if (!TimerRefreshIcon.Enabled) return;
8509             //Static usCheckCnt As int = 0
8510
8511             //Static iconDlListTopItem As ListViewItem = null
8512
8513             if (forceRefresh) idle = false;
8514
8515             //if (((ListView)ListTab.SelectedTab.Tag).TopItem == iconDlListTopItem)
8516             //    ((ImageDictionary)this.TIconDic).PauseGetImage = false;
8517             //else
8518             //    ((ImageDictionary)this.TIconDic).PauseGetImage = true;
8519             //
8520             //iconDlListTopItem = ((ListView)ListTab.SelectedTab.Tag).TopItem;
8521
8522             iconCnt += 1;
8523             blinkCnt += 1;
8524             //usCheckCnt += 1;
8525
8526             //if (usCheckCnt > 300)    //1min
8527             //{
8528             //    usCheckCnt = 0;
8529             //    if (!this.IsReceivedUserStream)
8530             //    {
8531             //        TraceOut("ReconnectUserStream");
8532             //        tw.ReconnectUserStream();
8533             //    }
8534             //}
8535
8536             bool busy = false;
8537             foreach (BackgroundWorker bw in this._bw)
8538             {
8539                 if (bw != null && bw.IsBusy)
8540                 {
8541                     busy = true;
8542                     break;
8543                 }
8544             }
8545
8546             if (iconCnt > 3)
8547             {
8548                 iconCnt = 0;
8549             }
8550             if (blinkCnt > 10)
8551             {
8552                 blinkCnt = 0;
8553                 //未保存の変更を保存
8554                 SaveConfigsAll(true);
8555             }
8556
8557             if (busy)
8558             {
8559                 NotifyIcon1.Icon = NIconRefresh[iconCnt];
8560                 idle = false;
8561                 _myStatusError = false;
8562                 return;
8563             }
8564
8565             TabClass tb = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
8566             if (SettingDialog.ReplyIconState != MyCommon.REPLY_ICONSTATE.None && tb != null && tb.UnreadCount > 0)
8567             {
8568                 if (blinkCnt > 0) return;
8569                 blink = !blink;
8570                 if (blink || SettingDialog.ReplyIconState == MyCommon.REPLY_ICONSTATE.StaticIcon)
8571                 {
8572                     NotifyIcon1.Icon = ReplyIcon;
8573                 }
8574                 else
8575                 {
8576                     NotifyIcon1.Icon = ReplyIconBlink;
8577                 }
8578                 idle = false;
8579                 return;
8580             }
8581
8582             if (idle) return;
8583             idle = true;
8584             //優先度:エラー→オフライン→アイドル
8585             //エラーは更新アイコンでクリアされる
8586             if (_myStatusError)
8587             {
8588                 NotifyIcon1.Icon = NIconAtRed;
8589                 return;
8590             }
8591             if (_myStatusOnline)
8592             {
8593                 NotifyIcon1.Icon = NIconAt;
8594             }
8595             else
8596             {
8597                 NotifyIcon1.Icon = NIconAtSmoke;
8598             }
8599         }
8600
8601         private void TimerRefreshIcon_Tick(object sender, EventArgs e)
8602         {
8603             //200ms
8604             this.RefreshTasktrayIcon(false);
8605         }
8606
8607         private void ContextMenuTabProperty_Opening(object sender, CancelEventArgs e)
8608         {
8609             //右クリックの場合はタブ名が設定済。アプリケーションキーの場合は現在のタブを対象とする
8610             if (string.IsNullOrEmpty(_rclickTabName) || sender != ContextMenuTabProperty)
8611             {
8612                 if (ListTab != null && ListTab.SelectedTab != null)
8613                     _rclickTabName = ListTab.SelectedTab.Text;
8614                 else
8615                     return;
8616             }
8617
8618             if (_statuses == null) return;
8619             if (_statuses.Tabs == null) return;
8620
8621             TabClass tb = _statuses.Tabs[_rclickTabName];
8622             if (tb == null) return;
8623
8624             NotifyDispMenuItem.Checked = tb.Notify;
8625             this.NotifyTbMenuItem.Checked = tb.Notify;
8626
8627             soundfileListup = true;
8628             SoundFileComboBox.Items.Clear();
8629             this.SoundFileTbComboBox.Items.Clear();
8630             SoundFileComboBox.Items.Add("");
8631             this.SoundFileTbComboBox.Items.Add("");
8632             DirectoryInfo oDir = new DirectoryInfo(Application.StartupPath + Path.DirectorySeparatorChar);
8633             if (Directory.Exists(Path.Combine(Application.StartupPath, "Sounds")))
8634             {
8635                 oDir = oDir.GetDirectories("Sounds")[0];
8636             }
8637             foreach (FileInfo oFile in oDir.GetFiles("*.wav"))
8638             {
8639                 SoundFileComboBox.Items.Add(oFile.Name);
8640                 this.SoundFileTbComboBox.Items.Add(oFile.Name);
8641             }
8642             int idx = SoundFileComboBox.Items.IndexOf(tb.SoundFile);
8643             if (idx == -1) idx = 0;
8644             SoundFileComboBox.SelectedIndex = idx;
8645             this.SoundFileTbComboBox.SelectedIndex = idx;
8646             soundfileListup = false;
8647             UreadManageMenuItem.Checked = tb.UnreadManage;
8648             this.UnreadMngTbMenuItem.Checked = tb.UnreadManage;
8649
8650             TabMenuControl(_rclickTabName);
8651         }
8652
8653         private void TabMenuControl(string tabName)
8654         {
8655             this.FilterEditMenuItem.Enabled = true;
8656             this.EditRuleTbMenuItem.Enabled = true;
8657
8658             if (_statuses.IsDefaultTab(tabName))
8659             {
8660                 this.ProtectTabMenuItem.Enabled = false;
8661                 this.ProtectTbMenuItem.Enabled = false;
8662             }
8663             else
8664             {
8665                 this.ProtectTabMenuItem.Enabled = true;
8666                 this.ProtectTbMenuItem.Enabled = true;
8667             }
8668
8669             if (_statuses.IsDefaultTab(tabName) || _statuses.Tabs[tabName].Protected)
8670             {
8671                 this.ProtectTabMenuItem.Checked = true;
8672                 this.ProtectTbMenuItem.Checked = true;
8673                 this.DeleteTabMenuItem.Enabled = false;
8674                 this.DeleteTbMenuItem.Enabled = false;
8675             }
8676             else
8677             {
8678                 this.ProtectTabMenuItem.Checked = false;
8679                 this.ProtectTbMenuItem.Checked = false;
8680                 this.DeleteTabMenuItem.Enabled = true;
8681                 this.DeleteTbMenuItem.Enabled = true;
8682             }
8683         }
8684
8685         private void ProtectTabMenuItem_Click(object sender, EventArgs e)
8686         {
8687             var checkState = ((ToolStripMenuItem)sender).Checked;
8688
8689             // チェック状態を同期
8690             this.ProtectTbMenuItem.Checked = checkState;
8691             this.ProtectTabMenuItem.Checked = checkState;
8692
8693             // ロック中はタブの削除を無効化
8694             this.DeleteTabMenuItem.Enabled = !checkState;
8695             this.DeleteTbMenuItem.Enabled = !checkState;
8696
8697             if (string.IsNullOrEmpty(_rclickTabName)) return;
8698             _statuses.Tabs[_rclickTabName].Protected = checkState;
8699
8700             SaveConfigsTabs();
8701         }
8702
8703         private void UreadManageMenuItem_Click(object sender, EventArgs e)
8704         {
8705             UreadManageMenuItem.Checked = ((ToolStripMenuItem)sender).Checked;
8706             this.UnreadMngTbMenuItem.Checked = UreadManageMenuItem.Checked;
8707
8708             if (string.IsNullOrEmpty(_rclickTabName)) return;
8709             ChangeTabUnreadManage(_rclickTabName, UreadManageMenuItem.Checked);
8710
8711             SaveConfigsTabs();
8712         }
8713
8714         public void ChangeTabUnreadManage(string tabName, bool isManage)
8715         {
8716             int idx;
8717             for (idx = 0; idx < ListTab.TabCount; idx++)
8718             {
8719                 if (ListTab.TabPages[idx].Text == tabName) break;
8720             }
8721
8722             _statuses.SetTabUnreadManage(tabName, isManage);
8723             if (SettingDialog.TabIconDisp)
8724             {
8725                 if (_statuses.Tabs[tabName].UnreadCount > 0)
8726                     ListTab.TabPages[idx].ImageIndex = 0;
8727                 else
8728                     ListTab.TabPages[idx].ImageIndex = -1;
8729             }
8730
8731             if (_curTab.Text == tabName)
8732             {
8733                 this.PurgeListViewItemCache();
8734                 _curList.Refresh();
8735             }
8736
8737             SetMainWindowTitle();
8738             SetStatusLabelUrl();
8739             if (!SettingDialog.TabIconDisp) ListTab.Refresh();
8740         }
8741
8742         private void NotifyDispMenuItem_Click(object sender, EventArgs e)
8743         {
8744             NotifyDispMenuItem.Checked = ((ToolStripMenuItem)sender).Checked;
8745             this.NotifyTbMenuItem.Checked = NotifyDispMenuItem.Checked;
8746
8747             if (string.IsNullOrEmpty(_rclickTabName)) return;
8748
8749             _statuses.Tabs[_rclickTabName].Notify = NotifyDispMenuItem.Checked;
8750
8751             SaveConfigsTabs();
8752         }
8753
8754         private void SoundFileComboBox_SelectedIndexChanged(object sender, EventArgs e)
8755         {
8756             if (soundfileListup || string.IsNullOrEmpty(_rclickTabName)) return;
8757
8758             _statuses.Tabs[_rclickTabName].SoundFile = (string)((ToolStripComboBox)sender).SelectedItem;
8759
8760             SaveConfigsTabs();
8761         }
8762
8763         private void DeleteTabMenuItem_Click(object sender, EventArgs e)
8764         {
8765             if (string.IsNullOrEmpty(_rclickTabName) || sender == this.DeleteTbMenuItem) _rclickTabName = ListTab.SelectedTab.Text;
8766
8767             RemoveSpecifiedTab(_rclickTabName, true);
8768             SaveConfigsTabs();
8769         }
8770
8771         private void FilterEditMenuItem_Click(object sender, EventArgs e)
8772         {
8773             if (string.IsNullOrEmpty(_rclickTabName)) _rclickTabName = _statuses.GetTabByType(MyCommon.TabUsageType.Home).TabName;
8774             fltDialog.SetCurrent(_rclickTabName);
8775             fltDialog.ShowDialog(this);
8776             this.TopMost = SettingDialog.AlwaysTop;
8777
8778             this.ApplyPostFilters();
8779             SaveConfigsTabs();
8780         }
8781
8782         private void AddTabMenuItem_Click(object sender, EventArgs e)
8783         {
8784             string tabName = null;
8785             MyCommon.TabUsageType tabUsage;
8786             using (InputTabName inputName = new InputTabName())
8787             {
8788                 inputName.TabName = _statuses.GetUniqueTabName();
8789                 inputName.IsShowUsage = true;
8790                 inputName.ShowDialog();
8791                 if (inputName.DialogResult == DialogResult.Cancel) return;
8792                 tabName = inputName.TabName;
8793                 tabUsage = inputName.Usage;
8794             }
8795             this.TopMost = SettingDialog.AlwaysTop;
8796             if (!string.IsNullOrEmpty(tabName))
8797             {
8798                 //List対応
8799                 ListElement list = null;
8800                 if (tabUsage == MyCommon.TabUsageType.Lists)
8801                 {
8802                     using (ListAvailable listAvail = new ListAvailable())
8803                     {
8804                         if (listAvail.ShowDialog(this) == DialogResult.Cancel) return;
8805                         if (listAvail.SelectedList == null) return;
8806                         list = listAvail.SelectedList;
8807                     }
8808                 }
8809                 if (!_statuses.AddTab(tabName, tabUsage, list) || !AddNewTab(tabName, false, tabUsage, list))
8810                 {
8811                     string tmp = string.Format(Properties.Resources.AddTabMenuItem_ClickText1, tabName);
8812                     MessageBox.Show(tmp, Properties.Resources.AddTabMenuItem_ClickText2, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
8813                 }
8814                 else
8815                 {
8816                     //成功
8817                     SaveConfigsTabs();
8818                     if (tabUsage == MyCommon.TabUsageType.PublicSearch)
8819                     {
8820                         ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
8821                         ListTabSelect(ListTab.TabPages[ListTab.TabPages.Count - 1]);
8822                         ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus();
8823                     }
8824                     if (tabUsage == MyCommon.TabUsageType.Lists)
8825                     {
8826                         ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
8827                         ListTabSelect(ListTab.TabPages[ListTab.TabPages.Count - 1]);
8828                         GetTimeline(MyCommon.WORKERTYPE.List, 1, tabName);
8829                     }
8830                 }
8831             }
8832         }
8833
8834         private void TabMenuItem_Click(object sender, EventArgs e)
8835         {
8836             //選択発言を元にフィルタ追加
8837             foreach (int idx in _curList.SelectedIndices)
8838             {
8839                 string tabName;
8840                 //タブ選択(or追加)
8841                 if (!SelectTab(out tabName)) return;
8842
8843                 fltDialog.SetCurrent(tabName);
8844                 if (_statuses[_curTab.Text, idx].RetweetedId == null)
8845                 {
8846                     fltDialog.AddNewFilter(_statuses[_curTab.Text, idx].ScreenName, _statuses[_curTab.Text, idx].TextFromApi);
8847                 }
8848                 else
8849                 {
8850                     fltDialog.AddNewFilter(_statuses[_curTab.Text, idx].RetweetedBy, _statuses[_curTab.Text, idx].TextFromApi);
8851                 }
8852                 fltDialog.ShowDialog(this);
8853                 this.TopMost = SettingDialog.AlwaysTop;
8854             }
8855
8856             this.ApplyPostFilters();
8857             SaveConfigsTabs();
8858             if (this.ListTab.SelectedTab != null &&
8859                 ((DetailsListView)this.ListTab.SelectedTab.Tag).SelectedIndices.Count > 0)
8860             {
8861                 _curPost = _statuses[this.ListTab.SelectedTab.Text, ((DetailsListView)this.ListTab.SelectedTab.Tag).SelectedIndices[0]];
8862             }
8863         }
8864
8865         protected override bool ProcessDialogKey(Keys keyData)
8866         {
8867             //TextBox1でEnterを押してもビープ音が鳴らないようにする
8868             if ((keyData & Keys.KeyCode) == Keys.Enter)
8869             {
8870                 if (StatusText.Focused)
8871                 {
8872                     bool _NewLine = false;
8873                     bool _Post = false;
8874
8875                     if (this._cfgCommon.PostCtrlEnter) //Ctrl+Enter投稿時
8876                     {
8877                         if (StatusText.Multiline)
8878                         {
8879                             if ((keyData & Keys.Shift) == Keys.Shift && (keyData & Keys.Control) != Keys.Control) _NewLine = true;
8880
8881                             if ((keyData & Keys.Control) == Keys.Control) _Post = true;
8882                         }
8883                         else
8884                         {
8885                             if (((keyData & Keys.Control) == Keys.Control)) _Post = true;
8886                         }
8887
8888                     }
8889                     else if (this._cfgCommon.PostShiftEnter) //SHift+Enter投稿時
8890                     {
8891                         if (StatusText.Multiline)
8892                         {
8893                             if ((keyData & Keys.Control) == Keys.Control && (keyData & Keys.Shift) != Keys.Shift) _NewLine = true;
8894
8895                             if ((keyData & Keys.Shift) == Keys.Shift) _Post = true;
8896                         }
8897                         else
8898                         {
8899                             if (((keyData & Keys.Shift) == Keys.Shift)) _Post = true;
8900                         }
8901
8902                     }
8903                     else //Enter投稿時
8904                     {
8905                         if (StatusText.Multiline)
8906                         {
8907                             if ((keyData & Keys.Shift) == Keys.Shift && (keyData & Keys.Control) != Keys.Control) _NewLine = true;
8908
8909                             if (((keyData & Keys.Control) != Keys.Control && (keyData & Keys.Shift) != Keys.Shift) ||
8910                                 ((keyData & Keys.Control) == Keys.Control && (keyData & Keys.Shift) == Keys.Shift)) _Post = true;
8911                         }
8912                         else
8913                         {
8914                             if (((keyData & Keys.Shift) == Keys.Shift) ||
8915                                 (((keyData & Keys.Control) != Keys.Control) &&
8916                                 ((keyData & Keys.Shift) != Keys.Shift))) _Post = true;
8917                         }
8918                     }
8919
8920                     if (_NewLine)
8921                     {
8922                         int pos1 = StatusText.SelectionStart;
8923                         if (StatusText.SelectionLength > 0)
8924                         {
8925                             StatusText.Text = StatusText.Text.Remove(pos1, StatusText.SelectionLength);  //選択状態文字列削除
8926                         }
8927                         StatusText.Text = StatusText.Text.Insert(pos1, Environment.NewLine);  //改行挿入
8928                         StatusText.SelectionStart = pos1 + Environment.NewLine.Length;    //カーソルを改行の次の文字へ移動
8929                         return true;
8930                     }
8931                     else if (_Post)
8932                     {
8933                         PostButton_Click(null, null);
8934                         return true;
8935                     }
8936                 }
8937                 else if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch &&
8938                          (ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focused ||
8939                          ListTab.SelectedTab.Controls["panelSearch"].Controls["comboLang"].Focused))
8940                 {
8941                     this.SearchButton_Click(ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"], null);
8942                     return true;
8943                 }
8944             }
8945
8946             return base.ProcessDialogKey(keyData);
8947         }
8948
8949         private void ReplyAllStripMenuItem_Click(object sender, EventArgs e)
8950         {
8951             MakeReplyOrDirectStatus(false, true, true);
8952         }
8953
8954         private void IDRuleMenuItem_Click(object sender, EventArgs e)
8955         {
8956             string tabName;
8957
8958             //未選択なら処理終了
8959             if (_curList.SelectedIndices.Count == 0) return;
8960
8961             //タブ選択(or追加)
8962             if (!SelectTab(out tabName)) return;
8963
8964             bool mv = false;
8965             bool mk = false;
8966             MoveOrCopy(ref mv, ref mk);
8967
8968             List<string> ids = new List<string>();
8969             foreach (int idx in _curList.SelectedIndices)
8970             {
8971                 PostClass post = _statuses[_curTab.Text, idx];
8972                 if (!ids.Contains(post.ScreenName))
8973                 {
8974                     PostFilterRule fc = new PostFilterRule();
8975                     ids.Add(post.ScreenName);
8976                     if (post.RetweetedId == null)
8977                     {
8978                         fc.FilterName = post.ScreenName;
8979                     }
8980                     else
8981                     {
8982                         fc.FilterName = post.RetweetedBy;
8983                     }
8984                     fc.UseNameField = true;
8985                     fc.MoveMatches = mv;
8986                     fc.MarkMatches = mk;
8987                     fc.UseRegex = false;
8988                     fc.FilterByUrl = false;
8989                     _statuses.Tabs[tabName].AddFilter(fc);
8990                 }
8991             }
8992             if (ids.Count != 0)
8993             {
8994                 List<string> atids = new List<string>();
8995                 foreach (string id in ids)
8996                 {
8997                     atids.Add("@" + id);
8998                 }
8999                 int cnt = AtIdSupl.ItemCount;
9000                 AtIdSupl.AddRangeItem(atids.ToArray());
9001                 if (AtIdSupl.ItemCount != cnt) _modifySettingAtId = true;
9002             }
9003
9004             this.ApplyPostFilters();
9005             SaveConfigsTabs();
9006         }
9007
9008         private bool SelectTab(out string tabName)
9009         {
9010             do
9011             {
9012                 tabName = null;
9013
9014                 //振り分け先タブ選択
9015                 using (var dialog = new TabsDialog(_statuses))
9016                 {
9017                     if (dialog.ShowDialog(this) == DialogResult.Cancel) return false;
9018
9019                     var selectedTab = dialog.SelectedTab;
9020                     tabName = selectedTab == null ? null : selectedTab.TabName;
9021                 }
9022
9023                 ListTab.SelectedTab.Focus();
9024                 //新規タブを選択→タブ作成
9025                 if (tabName == null)
9026                 {
9027                     using (InputTabName inputName = new InputTabName())
9028                     {
9029                         inputName.TabName = _statuses.GetUniqueTabName();
9030                         inputName.ShowDialog();
9031                         if (inputName.DialogResult == DialogResult.Cancel) return false;
9032                         tabName = inputName.TabName;
9033                     }
9034                     this.TopMost = SettingDialog.AlwaysTop;
9035                     if (!string.IsNullOrEmpty(tabName))
9036                     {
9037                         if (!_statuses.AddTab(tabName, MyCommon.TabUsageType.UserDefined, null) || !AddNewTab(tabName, false, MyCommon.TabUsageType.UserDefined))
9038                         {
9039                             string tmp = string.Format(Properties.Resources.IDRuleMenuItem_ClickText2, tabName);
9040                             MessageBox.Show(tmp, Properties.Resources.IDRuleMenuItem_ClickText3, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
9041                             //もう一度タブ名入力
9042                         }
9043                         else
9044                         {
9045                             return true;
9046                         }
9047                     }
9048                 }
9049                 else
9050                 {
9051                     //既存タブを選択
9052                     return true;
9053                 }
9054             }
9055             while (true);
9056         }
9057
9058         private void MoveOrCopy(ref bool move, ref bool mark)
9059         {
9060             {
9061                 //移動するか?
9062                 string _tmp = string.Format(Properties.Resources.IDRuleMenuItem_ClickText4, Environment.NewLine);
9063                 if (MessageBox.Show(_tmp, Properties.Resources.IDRuleMenuItem_ClickText5, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
9064                     move = false;
9065                 else
9066                     move = true;
9067             }
9068             if (!move)
9069             {
9070                 //マークするか?
9071                 string _tmp = string.Format(Properties.Resources.IDRuleMenuItem_ClickText6, Environment.NewLine);
9072                 if (MessageBox.Show(_tmp, Properties.Resources.IDRuleMenuItem_ClickText7, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
9073                     mark = true;
9074                 else
9075                     mark = false;
9076             }
9077         }
9078         private void CopySTOTMenuItem_Click(object sender, EventArgs e)
9079         {
9080             this.CopyStot();
9081         }
9082
9083         private void CopyURLMenuItem_Click(object sender, EventArgs e)
9084         {
9085             this.CopyIdUri();
9086         }
9087
9088         private void SelectAllMenuItem_Click(object sender, EventArgs e)
9089         {
9090             if (StatusText.Focused)
9091             {
9092                 // 発言欄でのCtrl+A
9093                 StatusText.SelectAll();
9094             }
9095             else
9096             {
9097                 // ListView上でのCtrl+A
9098                 for (int i = 0; i < _curList.VirtualListSize; i++)
9099                 {
9100                     _curList.SelectedIndices.Add(i);
9101                 }
9102             }
9103         }
9104
9105         private void MoveMiddle()
9106         {
9107             ListViewItem _item;
9108             int idx1;
9109             int idx2;
9110
9111             if (_curList.SelectedIndices.Count == 0) return;
9112
9113             int idx = _curList.SelectedIndices[0];
9114
9115             _item = _curList.GetItemAt(0, 25);
9116             if (_item == null)
9117                 idx1 = 0;
9118             else
9119                 idx1 = _item.Index;
9120
9121             _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1);
9122             if (_item == null)
9123                 idx2 = _curList.VirtualListSize - 1;
9124             else
9125                 idx2 = _item.Index;
9126
9127             idx -= Math.Abs(idx1 - idx2) / 2;
9128             if (idx < 0) idx = 0;
9129
9130             _curList.EnsureVisible(_curList.VirtualListSize - 1);
9131             _curList.EnsureVisible(idx);
9132         }
9133
9134         private void OpenURLMenuItem_Click(object sender, EventArgs e)
9135         {
9136             if (PostBrowser.Document.Links.Count > 0)
9137             {
9138                 UrlDialog.ClearUrl();
9139
9140                 string openUrlStr = "";
9141
9142                 if (PostBrowser.Document.Links.Count == 1)
9143                 {
9144                     string urlStr = "";
9145                     try
9146                     {
9147                         urlStr = MyCommon.IDNEncode(PostBrowser.Document.Links[0].GetAttribute("href"));
9148                     }
9149                     catch (ArgumentException)
9150                     {
9151                         //変なHTML?
9152                         return;
9153                     }
9154                     catch (Exception)
9155                     {
9156                         return;
9157                     }
9158                     if (string.IsNullOrEmpty(urlStr)) return;
9159                     openUrlStr = MyCommon.urlEncodeMultibyteChar(urlStr);
9160                 }
9161                 else
9162                 {
9163                     foreach (HtmlElement linkElm in PostBrowser.Document.Links)
9164                     {
9165                         string urlStr = "";
9166                         string linkText = "";
9167                         string href = "";
9168                         try
9169                         {
9170                             urlStr = linkElm.GetAttribute("title");
9171                             href = MyCommon.IDNEncode(linkElm.GetAttribute("href"));
9172                             if (string.IsNullOrEmpty(urlStr)) urlStr = href;
9173                             linkText = linkElm.InnerText;
9174                             if (!linkText.StartsWith("http") && !linkText.StartsWith("#") && !linkText.Contains("."))
9175                             {
9176                                 linkText = "@" + linkText;
9177                             }
9178                         }
9179                         catch (ArgumentException)
9180                         {
9181                             //変なHTML?
9182                             return;
9183                         }
9184                         catch (Exception)
9185                         {
9186                             return;
9187                         }
9188                         if (string.IsNullOrEmpty(urlStr)) continue;
9189                         UrlDialog.AddUrl(new OpenUrlItem(linkText, MyCommon.urlEncodeMultibyteChar(urlStr), href));
9190                     }
9191                     try
9192                     {
9193                         if (UrlDialog.ShowDialog() == DialogResult.OK)
9194                         {
9195                             openUrlStr = UrlDialog.SelectedUrl;
9196                         }
9197                     }
9198                     catch (Exception)
9199                     {
9200                         return;
9201                     }
9202                     this.TopMost = SettingDialog.AlwaysTop;
9203                 }
9204                 if (string.IsNullOrEmpty(openUrlStr)) return;
9205
9206                 if (openUrlStr.StartsWith("http://twitter.com/search?q=") ||
9207                     openUrlStr.StartsWith("https://twitter.com/search?q="))
9208                 {
9209                     //ハッシュタグの場合は、タブで開く
9210                     string urlStr = Uri.UnescapeDataString(openUrlStr);
9211                     string hash = urlStr.Substring(urlStr.IndexOf("#"));
9212                     HashSupl.AddItem(hash);
9213                     HashMgr.AddHashToHistory(hash.Trim(), false);
9214                     AddNewTabForSearch(hash);
9215                     return;
9216                 }
9217                 else
9218                 {
9219                     Match m = Regex.Match(openUrlStr, "^https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)$");
9220                     if (SettingDialog.OpenUserTimeline && m.Success && IsTwitterId(m.Result("${ScreenName}")))
9221                         this.AddNewTabForUserTimeline(m.Result("${ScreenName}"));
9222                     else
9223                         OpenUriAsync(openUrlStr);
9224                     return;
9225                 }
9226             }
9227         }
9228
9229         private void ClearTabMenuItem_Click(object sender, EventArgs e)
9230         {
9231             if (string.IsNullOrEmpty(_rclickTabName)) return;
9232             ClearTab(_rclickTabName, true);
9233         }
9234
9235         private void ClearTab(string tabName, bool showWarning)
9236         {
9237             if (showWarning)
9238             {
9239                 string tmp = string.Format(Properties.Resources.ClearTabMenuItem_ClickText1, Environment.NewLine);
9240                 if (MessageBox.Show(tmp, tabName + " " + Properties.Resources.ClearTabMenuItem_ClickText2, MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
9241                 {
9242                     return;
9243                 }
9244             }
9245
9246             _statuses.ClearTabIds(tabName);
9247             if (ListTab.SelectedTab.Text == tabName)
9248             {
9249                 _anchorPost = null;
9250                 _anchorFlag = false;
9251                 this.PurgeListViewItemCache();
9252                 _curItemIndex = -1;
9253                 _curPost = null;
9254             }
9255             foreach (TabPage tb in ListTab.TabPages)
9256             {
9257                 if (tb.Text == tabName)
9258                 {
9259                     tb.ImageIndex = -1;
9260                     ((DetailsListView)tb.Tag).VirtualListSize = 0;
9261                     break;
9262                 }
9263             }
9264             if (!SettingDialog.TabIconDisp) ListTab.Refresh();
9265
9266             SetMainWindowTitle();
9267             SetStatusLabelUrl();
9268         }
9269
9270         private static long followers = 0;
9271
9272         private void SetMainWindowTitle()
9273         {
9274             //メインウインドウタイトルの書き換え
9275             StringBuilder ttl = new StringBuilder(256);
9276             int ur = 0;
9277             int al = 0;
9278             if (SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.None &&
9279                 SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.Post &&
9280                 SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.Ver &&
9281                 SettingDialog.DispLatestPost != MyCommon.DispTitleEnum.OwnStatus)
9282             {
9283                 foreach (var tab in _statuses.Tabs.Values)
9284                 {
9285                     ur += tab.UnreadCount;
9286                     al += tab.AllCount;
9287                 }
9288             }
9289
9290             if (SettingDialog.DispUsername) ttl.Append(tw.Username).Append(" - ");
9291             ttl.Append(Application.ProductName);
9292             ttl.Append("  ");
9293             switch (SettingDialog.DispLatestPost)
9294             {
9295                 case MyCommon.DispTitleEnum.Ver:
9296                     ttl.Append("Ver:").Append(MyCommon.GetReadableVersion());
9297                     break;
9298                 case MyCommon.DispTitleEnum.Post:
9299                     if (_history != null && _history.Count > 1)
9300                         ttl.Append(_history[_history.Count - 2].status.Replace("\r\n", " "));
9301                     break;
9302                 case MyCommon.DispTitleEnum.UnreadRepCount:
9303                     ttl.AppendFormat(Properties.Resources.SetMainWindowTitleText1, _statuses.GetTabByType(MyCommon.TabUsageType.Mentions).UnreadCount + _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage).UnreadCount);
9304                     break;
9305                 case MyCommon.DispTitleEnum.UnreadAllCount:
9306                     ttl.AppendFormat(Properties.Resources.SetMainWindowTitleText2, ur);
9307                     break;
9308                 case MyCommon.DispTitleEnum.UnreadAllRepCount:
9309                     ttl.AppendFormat(Properties.Resources.SetMainWindowTitleText3, ur, _statuses.GetTabByType(MyCommon.TabUsageType.Mentions).UnreadCount + _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage).UnreadCount);
9310                     break;
9311                 case MyCommon.DispTitleEnum.UnreadCountAllCount:
9312                     ttl.AppendFormat(Properties.Resources.SetMainWindowTitleText4, ur, al);
9313                     break;
9314                 case MyCommon.DispTitleEnum.OwnStatus:
9315                     if (followers == 0 && tw.FollowersCount > 0) followers = tw.FollowersCount;
9316                     ttl.AppendFormat(Properties.Resources.OwnStatusTitle, tw.StatusesCount, tw.FriendsCount, tw.FollowersCount, tw.FollowersCount - followers);
9317                     break;
9318             }
9319
9320             try
9321             {
9322                 this.Text = ttl.ToString();
9323             }
9324             catch (AccessViolationException)
9325             {
9326                 //原因不明。ポスト内容に依存か?たまーに発生するが再現せず。
9327             }
9328         }
9329
9330         private string GetStatusLabelText()
9331         {
9332             //ステータス欄にカウント表示
9333             //タブ未読数/タブ発言数 全未読数/総発言数 (未読@+未読DM数)
9334             if (_statuses == null) return "";
9335             TabClass tbRep = _statuses.GetTabByType(MyCommon.TabUsageType.Mentions);
9336             TabClass tbDm = _statuses.GetTabByType(MyCommon.TabUsageType.DirectMessage);
9337             if (tbRep == null || tbDm == null) return "";
9338             int urat = tbRep.UnreadCount + tbDm.UnreadCount;
9339             int ur = 0;
9340             int al = 0;
9341             int tur = 0;
9342             int tal = 0;
9343             StringBuilder slbl = new StringBuilder(256);
9344             try
9345             {
9346                 foreach (var tab in _statuses.Tabs.Values)
9347                 {
9348                     ur += tab.UnreadCount;
9349                     al += tab.AllCount;
9350                     if (_curTab != null && tab.TabName.Equals(_curTab.Text))
9351                     {
9352                         tur = tab.UnreadCount;
9353                         tal = tab.AllCount;
9354                     }
9355                 }
9356             }
9357             catch (Exception)
9358             {
9359                 return "";
9360             }
9361
9362             UnreadCounter = ur;
9363             UnreadAtCounter = urat;
9364
9365             slbl.AppendFormat(Properties.Resources.SetStatusLabelText1, tur, tal, ur, al, urat, _postTimestamps.Count, _favTimestamps.Count, _tlCount);
9366             if (this._cfgCommon.TimelinePeriod == 0)
9367             {
9368                 slbl.Append(Properties.Resources.SetStatusLabelText2);
9369             }
9370             else
9371             {
9372                 slbl.Append(this._cfgCommon.TimelinePeriod + Properties.Resources.SetStatusLabelText3);
9373             }
9374             return slbl.ToString();
9375         }
9376
9377         private void TwitterApiStatus_AccessLimitUpdated(object sender, EventArgs e)
9378         {
9379             try
9380             {
9381                 if (this.InvokeRequired && !this.IsDisposed)
9382                 {
9383                     this.Invoke((MethodInvoker)(() => this.TwitterApiStatus_AccessLimitUpdated(sender, e)));
9384                 }
9385                 else
9386                 {
9387                     var endpointName = (e as TwitterApiStatus.AccessLimitUpdatedEventArgs).EndpointName;
9388                     if (endpointName == "/statuses/home_timeline" || endpointName == null)
9389                     {
9390                         this._apiGauge.ApiLimit = MyCommon.TwitterApiInfo.AccessLimit["/statuses/home_timeline"];
9391                     }
9392                 }
9393             }
9394             catch (ObjectDisposedException)
9395             {
9396                 return;
9397             }
9398             catch (InvalidOperationException)
9399             {
9400                 return;
9401             }
9402         }
9403
9404         private void SetStatusLabelUrl()
9405         {
9406             StatusLabelUrl.Text = GetStatusLabelText();
9407         }
9408
9409         public void SetStatusLabel(string text)
9410         {
9411             StatusLabel.Text = text;
9412         }
9413
9414         private static StringBuilder ur = new StringBuilder(64);
9415
9416         private void SetNotifyIconText()
9417         {
9418             // タスクトレイアイコンのツールチップテキスト書き換え
9419             // Tween [未読/@]
9420             ur.Remove(0, ur.Length);
9421             if (SettingDialog.DispUsername)
9422             {
9423                 ur.Append(tw.Username);
9424                 ur.Append(" - ");
9425             }
9426             ur.Append(Application.ProductName);
9427 #if DEBUG
9428             ur.Append("(Debug Build)");
9429 #endif
9430             if (UnreadCounter != -1 && UnreadAtCounter != -1)
9431             {
9432                 ur.Append(" [");
9433                 ur.Append(UnreadCounter);
9434                 ur.Append("/@");
9435                 ur.Append(UnreadAtCounter);
9436                 ur.Append("]");
9437             }
9438             NotifyIcon1.Text = ur.ToString();
9439         }
9440
9441         internal void CheckReplyTo(string StatusText)
9442         {
9443             MatchCollection m;
9444             //ハッシュタグの保存
9445             m = Regex.Matches(StatusText, Twitter.HASHTAG, RegexOptions.IgnoreCase);
9446             string hstr = "";
9447             foreach (Match hm in m)
9448             {
9449                 if (!hstr.Contains("#" + hm.Result("$3") + " "))
9450                 {
9451                     hstr += "#" + hm.Result("$3") + " ";
9452                     HashSupl.AddItem("#" + hm.Result("$3"));
9453                 }
9454             }
9455             if (!string.IsNullOrEmpty(HashMgr.UseHash) && !hstr.Contains(HashMgr.UseHash + " "))
9456             {
9457                 hstr += HashMgr.UseHash;
9458             }
9459             if (!string.IsNullOrEmpty(hstr)) HashMgr.AddHashToHistory(hstr.Trim(), false);
9460
9461             // 本当にリプライ先指定すべきかどうかの判定
9462             m = Regex.Matches(StatusText, "(^|[ -/:-@[-^`{-~])(?<id>@[a-zA-Z0-9_]+)");
9463
9464             if (this._cfgCommon.UseAtIdSupplement)
9465             {
9466                 int bCnt = AtIdSupl.ItemCount;
9467                 foreach (Match mid in m)
9468                 {
9469                     AtIdSupl.AddItem(mid.Result("${id}"));
9470                 }
9471                 if (bCnt != AtIdSupl.ItemCount) _modifySettingAtId = true;
9472             }
9473
9474             // リプライ先ステータスIDの指定がない場合は指定しない
9475             if (_reply_to_id == null) return;
9476
9477             // リプライ先ユーザー名がない場合も指定しない
9478             if (string.IsNullOrEmpty(_reply_to_name))
9479             {
9480                 _reply_to_id = null;
9481                 return;
9482             }
9483
9484             // 通常Reply
9485             // 次の条件を満たす場合に in_reply_to_status_id 指定
9486             // 1. Twitterによりリンクと判定される @idが文中に1つ含まれる (2009/5/28 リンク化される@IDのみカウントするように修正)
9487             // 2. リプライ先ステータスIDが設定されている(リストをダブルクリックで返信している)
9488             // 3. 文中に含まれた@idがリプライ先のポスト者のIDと一致する
9489
9490             if (m != null)
9491             {
9492                 if (StatusText.StartsWith("@"))
9493                 {
9494                     if (StatusText.StartsWith("@" + _reply_to_name)) return;
9495                 }
9496                 else
9497                 {
9498                     foreach (Match mid in m)
9499                     {
9500                         if (StatusText.Contains("QT " + mid.Result("${id}") + ":") && mid.Result("${id}") == "@" + _reply_to_name) return;
9501                     }
9502                 }
9503             }
9504
9505             _reply_to_id = null;
9506             _reply_to_name = null;
9507
9508         }
9509
9510         private void TweenMain_Resize(object sender, EventArgs e)
9511         {
9512             if (!_initialLayout && SettingDialog.MinimizeToTray && WindowState == FormWindowState.Minimized)
9513             {
9514                 this.Visible = false;
9515             }
9516             if (_initialLayout && _cfgLocal != null && this.WindowState == FormWindowState.Normal && this.Visible)
9517             {
9518                 this.ClientSize = _cfgLocal.FormSize;
9519                 //_mySize = this.ClientSize;                     //サイズ保持(最小化・最大化されたまま終了した場合の対応用)
9520                 this.DesktopLocation = _cfgLocal.FormLocation;
9521                 //_myLoc = this.DesktopLocation;                        //位置保持(最小化・最大化されたまま終了した場合の対応用)
9522                 if (_cfgLocal.SplitterDistance > this.SplitContainer1.Panel1MinSize &&
9523                     _cfgLocal.SplitterDistance < this.SplitContainer1.Height - this.SplitContainer1.Panel2MinSize - this.SplitContainer1.SplitterWidth)
9524                 {
9525                     this.SplitContainer1.SplitterDistance = _cfgLocal.SplitterDistance; //Splitterの位置設定
9526                 }
9527                 //発言欄複数行
9528                 StatusText.Multiline = _cfgLocal.StatusMultiline;
9529                 if (StatusText.Multiline)
9530                 {
9531                     int dis = SplitContainer2.Height - _cfgLocal.StatusTextHeight - SplitContainer2.SplitterWidth;
9532                     if (dis > SplitContainer2.Panel1MinSize && dis < SplitContainer2.Height - SplitContainer2.Panel2MinSize - SplitContainer2.SplitterWidth)
9533                     {
9534                         SplitContainer2.SplitterDistance = SplitContainer2.Height - _cfgLocal.StatusTextHeight - SplitContainer2.SplitterWidth;
9535                     }
9536                     StatusText.Height = _cfgLocal.StatusTextHeight;
9537                 }
9538                 else
9539                 {
9540                     if (SplitContainer2.Height - SplitContainer2.Panel2MinSize - SplitContainer2.SplitterWidth > 0)
9541                     {
9542                         SplitContainer2.SplitterDistance = SplitContainer2.Height - SplitContainer2.Panel2MinSize - SplitContainer2.SplitterWidth;
9543                     }
9544                 }
9545                 if (_cfgLocal.PreviewDistance > this.SplitContainer3.Panel1MinSize && _cfgLocal.PreviewDistance < this.SplitContainer3.Width - this.SplitContainer3.Panel2MinSize - this.SplitContainer3.SplitterWidth)
9546                 {
9547                     this.SplitContainer3.SplitterDistance = _cfgLocal.PreviewDistance;
9548                 }
9549                 _initialLayout = false;
9550             }
9551             if (this.WindowState != FormWindowState.Minimized)
9552             {
9553                 _formWindowState = this.WindowState;
9554             }
9555         }
9556
9557         private void PlaySoundMenuItem_CheckedChanged(object sender, EventArgs e)
9558         {
9559             PlaySoundMenuItem.Checked = ((ToolStripMenuItem)sender).Checked;
9560             this.PlaySoundFileMenuItem.Checked = PlaySoundMenuItem.Checked;
9561             if (PlaySoundMenuItem.Checked)
9562             {
9563                 SettingDialog.PlaySound = true;
9564             }
9565             else
9566             {
9567                 SettingDialog.PlaySound = false;
9568             }
9569             _modifySettingCommon = true;
9570         }
9571
9572         private void SplitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
9573         {
9574             if (this.WindowState == FormWindowState.Normal && !_initialLayout)
9575             {
9576                 _mySpDis = SplitContainer1.SplitterDistance;
9577                 if (StatusText.Multiline) _mySpDis2 = StatusText.Height;
9578                 _modifySettingLocal = true;
9579             }
9580         }
9581
9582         private void doRepliedStatusOpen()
9583         {
9584             if (this.ExistCurrentPost && _curPost.InReplyToUser != null && _curPost.InReplyToStatusId != null)
9585             {
9586                 if (MyCommon.IsKeyDown(Keys.Shift))
9587                 {
9588                     OpenUriAsync(MyCommon.GetStatusUrl(_curPost.InReplyToUser, _curPost.InReplyToStatusId.Value));
9589                     return;
9590                 }
9591                 if (_statuses.ContainsKey(_curPost.InReplyToStatusId.Value))
9592                 {
9593                     PostClass repPost = _statuses[_curPost.InReplyToStatusId.Value];
9594                     MessageBox.Show(repPost.ScreenName + " / " + repPost.Nickname + "   (" + repPost.CreatedAt.ToString() + ")" + Environment.NewLine + repPost.TextFromApi);
9595                 }
9596                 else
9597                 {
9598                     foreach (TabClass tb in _statuses.GetTabsByType(MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.PublicSearch))
9599                     {
9600                         if (tb == null || !tb.Contains(_curPost.InReplyToStatusId.Value)) break;
9601                         PostClass repPost = _statuses[_curPost.InReplyToStatusId.Value];
9602                         MessageBox.Show(repPost.ScreenName + " / " + repPost.Nickname + "   (" + repPost.CreatedAt.ToString() + ")" + Environment.NewLine + repPost.TextFromApi);
9603                         return;
9604                     }
9605                     OpenUriAsync(MyCommon.GetStatusUrl(_curPost.InReplyToUser, _curPost.InReplyToStatusId.Value));
9606                 }
9607             }
9608         }
9609
9610         private void RepliedStatusOpenMenuItem_Click(object sender, EventArgs e)
9611         {
9612             doRepliedStatusOpen();
9613         }
9614
9615         /// <summary>
9616         /// UserPicture.Image に設定されている画像を破棄します。
9617         /// </summary>
9618         private void ClearUserPicture()
9619         {
9620             if (this.UserPicture.Image != null)
9621             {
9622                 var oldImage = this.UserPicture.Image;
9623                 this.UserPicture.Image = null;
9624                 oldImage.Dispose();
9625             }
9626         }
9627
9628         private void ContextMenuUserPicture_Opening(object sender, CancelEventArgs e)
9629         {
9630             //発言詳細のアイコン右クリック時のメニュー制御
9631             if (_curList.SelectedIndices.Count > 0 && _curPost != null)
9632             {
9633                 string name = _curPost.ImageUrl;
9634                 if (name != null && name.Length > 0)
9635                 {
9636                     int idx = name.LastIndexOf('/');
9637                     if (idx != -1)
9638                     {
9639                         name = Path.GetFileName(name.Substring(idx));
9640                         if (name.Contains("_normal.") || name.EndsWith("_normal"))
9641                         {
9642                             name = name.Replace("_normal", "");
9643                             this.IconNameToolStripMenuItem.Text = name;
9644                             this.IconNameToolStripMenuItem.Enabled = true;
9645                         }
9646                         else
9647                         {
9648                             this.IconNameToolStripMenuItem.Enabled = false;
9649                             this.IconNameToolStripMenuItem.Text = Properties.Resources.ContextMenuStrip3_OpeningText1;
9650                         }
9651                     }
9652                     else
9653                     {
9654                         this.IconNameToolStripMenuItem.Enabled = false;
9655                         this.IconNameToolStripMenuItem.Text = Properties.Resources.ContextMenuStrip3_OpeningText1;
9656                     }
9657
9658                     this.ReloadIconToolStripMenuItem.Enabled = true;
9659
9660                     if (this.IconCache.TryGetFromCache(_curPost.ImageUrl) != null)
9661                     {
9662                         this.SaveIconPictureToolStripMenuItem.Enabled = true;
9663                     }
9664                     else
9665                     {
9666                         this.SaveIconPictureToolStripMenuItem.Enabled = false;
9667                     }
9668                 }
9669                 else
9670                 {
9671                     this.IconNameToolStripMenuItem.Enabled = false;
9672                     this.ReloadIconToolStripMenuItem.Enabled = false;
9673                     this.SaveIconPictureToolStripMenuItem.Enabled = false;
9674                     this.IconNameToolStripMenuItem.Text = Properties.Resources.ContextMenuStrip3_OpeningText1;
9675                 }
9676             }
9677             else
9678             {
9679                 this.IconNameToolStripMenuItem.Enabled = false;
9680                 this.ReloadIconToolStripMenuItem.Enabled = false;
9681                 this.SaveIconPictureToolStripMenuItem.Enabled = false;
9682                 this.IconNameToolStripMenuItem.Text = Properties.Resources.ContextMenuStrip3_OpeningText2;
9683             }
9684             if (NameLabel.Tag != null)
9685             {
9686                 string id = (string)NameLabel.Tag;
9687                 if (id == tw.Username)
9688                 {
9689                     FollowToolStripMenuItem.Enabled = false;
9690                     UnFollowToolStripMenuItem.Enabled = false;
9691                     ShowFriendShipToolStripMenuItem.Enabled = false;
9692                     ShowUserStatusToolStripMenuItem.Enabled = true;
9693                     SearchPostsDetailNameToolStripMenuItem.Enabled = true;
9694                     SearchAtPostsDetailNameToolStripMenuItem.Enabled = false;
9695                     ListManageUserContextToolStripMenuItem3.Enabled = true;
9696                 }
9697                 else
9698                 {
9699                     FollowToolStripMenuItem.Enabled = true;
9700                     UnFollowToolStripMenuItem.Enabled = true;
9701                     ShowFriendShipToolStripMenuItem.Enabled = true;
9702                     ShowUserStatusToolStripMenuItem.Enabled = true;
9703                     SearchPostsDetailNameToolStripMenuItem.Enabled = true;
9704                     SearchAtPostsDetailNameToolStripMenuItem.Enabled = true;
9705                     ListManageUserContextToolStripMenuItem3.Enabled = true;
9706                 }
9707             }
9708             else
9709             {
9710                 FollowToolStripMenuItem.Enabled = false;
9711                 UnFollowToolStripMenuItem.Enabled = false;
9712                 ShowFriendShipToolStripMenuItem.Enabled = false;
9713                 ShowUserStatusToolStripMenuItem.Enabled = false;
9714                 SearchPostsDetailNameToolStripMenuItem.Enabled = false;
9715                 SearchAtPostsDetailNameToolStripMenuItem.Enabled = false;
9716                 ListManageUserContextToolStripMenuItem3.Enabled = false;
9717             }
9718         }
9719
9720         private void IconNameToolStripMenuItem_Click(object sender, EventArgs e)
9721         {
9722             if (_curPost == null) return;
9723             string name = _curPost.ImageUrl;
9724             OpenUriAsync(name.Remove(name.LastIndexOf("_normal"), 7)); // "_normal".Length
9725         }
9726
9727         private async void ReloadIconToolStripMenuItem_Click(object sender, EventArgs e)
9728         {
9729             if (this._curPost == null) return;
9730
9731             await this.UserPicture.SetImageFromTask(async () =>
9732             {
9733                 var imageUrl = this._curPost.ImageUrl;
9734
9735                 var image = await this.IconCache.DownloadImageAsync(imageUrl, force: true)
9736                     .ConfigureAwait(false);
9737
9738                 return await image.CloneAsync()
9739                     .ConfigureAwait(false);
9740             });
9741         }
9742
9743         private void SaveOriginalSizeIconPictureToolStripMenuItem_Click(object sender, EventArgs e)
9744         {
9745             if (_curPost == null) return;
9746             string name = _curPost.ImageUrl;
9747             name = Path.GetFileNameWithoutExtension(name.Substring(name.LastIndexOf('/')));
9748
9749             this.SaveFileDialog1.FileName = name.Substring(0, name.Length - 8); // "_normal".Length + 1
9750
9751             if (this.SaveFileDialog1.ShowDialog() == DialogResult.OK)
9752             {
9753                 // STUB
9754             }
9755         }
9756
9757         private void SaveIconPictureToolStripMenuItem_Click(object sender, EventArgs e)
9758         {
9759             if (_curPost == null) return;
9760             string name = _curPost.ImageUrl;
9761
9762             this.SaveFileDialog1.FileName = name.Substring(name.LastIndexOf('/') + 1);
9763
9764             if (this.SaveFileDialog1.ShowDialog() == DialogResult.OK)
9765             {
9766                 try
9767                 {
9768                     using (Image orgBmp = new Bitmap(IconCache.TryGetFromCache(name).Image))
9769                     {
9770                         using (Bitmap bmp2 = new Bitmap(orgBmp.Size.Width, orgBmp.Size.Height))
9771                         {
9772                             using (Graphics g = Graphics.FromImage(bmp2))
9773                             {
9774                                 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
9775                                 g.DrawImage(orgBmp, 0, 0, orgBmp.Size.Width, orgBmp.Size.Height);
9776                             }
9777                             bmp2.Save(this.SaveFileDialog1.FileName);
9778                         }
9779                     }
9780                 }
9781                 catch (Exception)
9782                 {
9783                     //処理中にキャッシュアウトする可能性あり
9784                 }
9785             }
9786         }
9787
9788         private void SplitContainer2_Panel2_Resize(object sender, EventArgs e)
9789         {
9790             this.StatusText.Multiline = this.SplitContainer2.Panel2.Height > this.SplitContainer2.Panel2MinSize + 2;
9791             MultiLineMenuItem.Checked = this.StatusText.Multiline;
9792             _modifySettingLocal = true;
9793         }
9794
9795         private void StatusText_MultilineChanged(object sender, EventArgs e)
9796         {
9797             if (this.StatusText.Multiline)
9798                 this.StatusText.ScrollBars = ScrollBars.Vertical;
9799             else
9800                 this.StatusText.ScrollBars = ScrollBars.None;
9801
9802             _modifySettingLocal = true;
9803         }
9804
9805         private void MultiLineMenuItem_Click(object sender, EventArgs e)
9806         {
9807             //発言欄複数行
9808             StatusText.Multiline = MultiLineMenuItem.Checked;
9809             _cfgLocal.StatusMultiline = MultiLineMenuItem.Checked;
9810             if (MultiLineMenuItem.Checked)
9811             {
9812                 if (SplitContainer2.Height - _mySpDis2 - SplitContainer2.SplitterWidth < 0)
9813                     SplitContainer2.SplitterDistance = 0;
9814                 else
9815                     SplitContainer2.SplitterDistance = SplitContainer2.Height - _mySpDis2 - SplitContainer2.SplitterWidth;
9816             }
9817             else
9818             {
9819                 SplitContainer2.SplitterDistance = SplitContainer2.Height - SplitContainer2.Panel2MinSize - SplitContainer2.SplitterWidth;
9820             }
9821             _modifySettingLocal = true;
9822         }
9823
9824         private async Task<bool> UrlConvertAsync(MyCommon.UrlConverter Converter_Type)
9825         {
9826             //t.coで投稿時自動短縮する場合は、外部サービスでの短縮禁止
9827             //if (SettingDialog.UrlConvertAuto && SettingDialog.ShortenTco) return;
9828
9829             //Converter_Type=Nicomsの場合は、nicovideoのみ短縮する
9830             //参考資料 RFC3986 Uniform Resource Identifier (URI): Generic Syntax
9831             //Appendix A.  Collected ABNF for URI
9832             //http://www.ietf.org/rfc/rfc3986.txt
9833
9834             string result = "";
9835
9836             const string nico = @"^https?://[a-z]+\.(nicovideo|niconicommons|nicolive)\.jp/[a-z]+/[a-z0-9]+$";
9837
9838             if (StatusText.SelectionLength > 0)
9839             {
9840                 string tmp = StatusText.SelectedText;
9841                 // httpから始まらない場合、ExcludeStringで指定された文字列で始まる場合は対象としない
9842                 if (tmp.StartsWith("http"))
9843                 {
9844                     // 文字列が選択されている場合はその文字列について処理
9845
9846                     //nico.ms使用、nicovideoにマッチしたら変換
9847                     if (SettingDialog.Nicoms && Regex.IsMatch(tmp, nico))
9848                     {
9849                         result = nicoms.Shorten(tmp);
9850                     }
9851                     else if (Converter_Type != MyCommon.UrlConverter.Nicoms)
9852                     {
9853                         //短縮URL変換 日本語を含むかもしれないのでURLエンコードする
9854                         try
9855                         {
9856                             var srcUri = new Uri(MyCommon.urlEncodeMultibyteChar(tmp));
9857                             var resultUri = await ShortUrl.Instance.ShortenUrlAsync(Converter_Type, srcUri);
9858                             result = resultUri.ToString();
9859                         }
9860                         catch (WebApiException e)
9861                         {
9862                             this.StatusLabel.Text = Converter_Type + ":" + e.Message;
9863                             return false;
9864                         }
9865                         catch (UriFormatException e)
9866                         {
9867                             this.StatusLabel.Text = Converter_Type + ":" + e.Message;
9868                             return false;
9869                         }
9870                     }
9871                     else
9872                     {
9873                         return true;
9874                     }
9875
9876                     if (!string.IsNullOrEmpty(result))
9877                     {
9878                         urlUndo undotmp = new urlUndo();
9879
9880                         StatusText.Select(StatusText.Text.IndexOf(tmp, StringComparison.Ordinal), tmp.Length);
9881                         StatusText.SelectedText = result;
9882
9883                         //undoバッファにセット
9884                         undotmp.Before = tmp;
9885                         undotmp.After = result;
9886
9887                         if (urlUndoBuffer == null)
9888                         {
9889                             urlUndoBuffer = new List<urlUndo>();
9890                             UrlUndoToolStripMenuItem.Enabled = true;
9891                         }
9892
9893                         urlUndoBuffer.Add(undotmp);
9894                     }
9895                 }
9896             }
9897             else
9898             {
9899                 const string url = @"(?<before>(?:[^\""':!=]|^|\:))" +
9900                                    @"(?<url>(?<protocol>https?://)" +
9901                                    @"(?<domain>(?:[\.-]|[^\p{P}\s])+\.[a-z]{2,}(?::[0-9]+)?)" +
9902                                    @"(?<path>/[a-z0-9!*//();:&=+$/%#\-_.,~@]*[a-z0-9)=#/]?)?" +
9903                                    @"(?<query>\?[a-z0-9!*//();:&=+$/%#\-_.,~@?]*[a-z0-9_&=#/])?)";
9904                 // 正規表現にマッチしたURL文字列をtinyurl化
9905                 foreach (Match mt in Regex.Matches(StatusText.Text, url, RegexOptions.IgnoreCase))
9906                 {
9907                     if (StatusText.Text.IndexOf(mt.Result("${url}"), StringComparison.Ordinal) == -1) continue;
9908                     string tmp = mt.Result("${url}");
9909                     if (tmp.StartsWith("w", StringComparison.OrdinalIgnoreCase)) tmp = "http://" + tmp;
9910                     urlUndo undotmp = new urlUndo();
9911
9912                     //選んだURLを選択(?)
9913                     StatusText.Select(StatusText.Text.IndexOf(mt.Result("${url}"), StringComparison.Ordinal), mt.Result("${url}").Length);
9914
9915                     //nico.ms使用、nicovideoにマッチしたら変換
9916                     if (SettingDialog.Nicoms && Regex.IsMatch(tmp, nico))
9917                     {
9918                         result = nicoms.Shorten(tmp);
9919                     }
9920                     else if (Converter_Type != MyCommon.UrlConverter.Nicoms)
9921                     {
9922                         //短縮URL変換 日本語を含むかもしれないのでURLエンコードする
9923                         try
9924                         {
9925                             var srcUri = new Uri(MyCommon.urlEncodeMultibyteChar(tmp));
9926                             var resultUri = await ShortUrl.Instance.ShortenUrlAsync(Converter_Type, srcUri);
9927                             result = resultUri.ToString();
9928                         }
9929                         catch (HttpRequestException e)
9930                         {
9931                             // 例外のメッセージが「Response status code does not indicate success: 500 (Internal Server Error).」
9932                             // のように長いので「:」が含まれていればそれ以降のみを抽出する
9933                             var message = e.Message.Split(new[] { ':' }, count: 2).Last();
9934
9935                             this.StatusLabel.Text = Converter_Type + ":" + message;
9936                             continue;
9937                         }
9938                         catch (WebApiException e)
9939                         {
9940                             this.StatusLabel.Text = Converter_Type + ":" + e.Message;
9941                             continue;
9942                         }
9943                         catch (UriFormatException e)
9944                         {
9945                             this.StatusLabel.Text = Converter_Type + ":" + e.Message;
9946                             continue;
9947                         }
9948                     }
9949                     else
9950                     {
9951                         continue;
9952                     }
9953
9954                     if (!string.IsNullOrEmpty(result))
9955                     {
9956                         StatusText.Select(StatusText.Text.IndexOf(mt.Result("${url}"), StringComparison.Ordinal), mt.Result("${url}").Length);
9957                         StatusText.SelectedText = result;
9958                         //undoバッファにセット
9959                         undotmp.Before = mt.Result("${url}");
9960                         undotmp.After = result;
9961
9962                         if (urlUndoBuffer == null)
9963                         {
9964                             urlUndoBuffer = new List<urlUndo>();
9965                             UrlUndoToolStripMenuItem.Enabled = true;
9966                         }
9967
9968                         urlUndoBuffer.Add(undotmp);
9969                     }
9970                 }
9971             }
9972
9973             return true;
9974         }
9975
9976         private void doUrlUndo()
9977         {
9978             if (urlUndoBuffer != null)
9979             {
9980                 string tmp = StatusText.Text;
9981                 foreach (urlUndo data in urlUndoBuffer)
9982                 {
9983                     tmp = tmp.Replace(data.After, data.Before);
9984                 }
9985                 StatusText.Text = tmp;
9986                 urlUndoBuffer = null;
9987                 UrlUndoToolStripMenuItem.Enabled = false;
9988                 StatusText.SelectionStart = 0;
9989                 StatusText.SelectionLength = 0;
9990             }
9991         }
9992
9993         private async void TinyURLToolStripMenuItem_Click(object sender, EventArgs e)
9994         {
9995             await UrlConvertAsync(MyCommon.UrlConverter.TinyUrl);
9996         }
9997
9998         private async void IsgdToolStripMenuItem_Click(object sender, EventArgs e)
9999         {
10000             await UrlConvertAsync(MyCommon.UrlConverter.Isgd);
10001         }
10002
10003         private async void TwurlnlToolStripMenuItem_Click(object sender, EventArgs e)
10004         {
10005             await UrlConvertAsync(MyCommon.UrlConverter.Twurl);
10006         }
10007
10008         private async void UxnuMenuItem_Click(object sender, EventArgs e)
10009         {
10010             await UrlConvertAsync(MyCommon.UrlConverter.Uxnu);
10011         }
10012
10013         private async void UrlConvertAutoToolStripMenuItem_Click(object sender, EventArgs e)
10014         {
10015             if (!await UrlConvertAsync(SettingDialog.AutoShortUrlFirst))
10016             {
10017                 MyCommon.UrlConverter svc = SettingDialog.AutoShortUrlFirst;
10018                 Random rnd = new Random();
10019                 // 前回使用した短縮URLサービス以外を選択する
10020                 do
10021                 {
10022                     svc = (MyCommon.UrlConverter)rnd.Next(System.Enum.GetNames(typeof(MyCommon.UrlConverter)).Length);
10023                 }
10024                 while (svc == SettingDialog.AutoShortUrlFirst || svc == MyCommon.UrlConverter.Nicoms || svc == MyCommon.UrlConverter.Unu);
10025                 await UrlConvertAsync(svc);
10026             }
10027         }
10028
10029         private void UrlUndoToolStripMenuItem_Click(object sender, EventArgs e)
10030         {
10031             doUrlUndo();
10032         }
10033
10034         private void NewPostPopMenuItem_CheckStateChanged(object sender, EventArgs e)
10035         {
10036             this.NotifyFileMenuItem.Checked = ((ToolStripMenuItem)sender).Checked;
10037             this.NewPostPopMenuItem.Checked = this.NotifyFileMenuItem.Checked;
10038             _cfgCommon.NewAllPop = NewPostPopMenuItem.Checked;
10039             _modifySettingCommon = true;
10040         }
10041
10042         private void ListLockMenuItem_CheckStateChanged(object sender, EventArgs e)
10043         {
10044             ListLockMenuItem.Checked = ((ToolStripMenuItem)sender).Checked;
10045             this.LockListFileMenuItem.Checked = ListLockMenuItem.Checked;
10046             _cfgCommon.ListLock = ListLockMenuItem.Checked;
10047             _modifySettingCommon = true;
10048         }
10049
10050         private void MenuStrip1_MenuActivate(object sender, EventArgs e)
10051         {
10052             // フォーカスがメニューに移る (MenuStrip1.Tag フラグを立てる)
10053             MenuStrip1.Tag = new Object();
10054             MenuStrip1.Select(); // StatusText がフォーカスを持っている場合 Leave が発生
10055         }
10056
10057         private void MenuStrip1_MenuDeactivate(object sender, EventArgs e)
10058         {
10059             if (this.Tag != null) // 設定された戻り先へ遷移
10060             {
10061                 if (this.Tag == this.ListTab.SelectedTab)
10062                     ((Control)this.ListTab.SelectedTab.Tag).Select();
10063                 else
10064                     ((Control)this.Tag).Select();
10065             }
10066             else // 戻り先が指定されていない (初期状態) 場合はタブに遷移
10067             {
10068                 if (ListTab.SelectedIndex > -1 && ListTab.SelectedTab.HasChildren)
10069                 {
10070                     this.Tag = ListTab.SelectedTab.Tag;
10071                     ((Control)this.Tag).Select();
10072                 }
10073             }
10074             // フォーカスがメニューに遷移したかどうかを表すフラグを降ろす
10075             MenuStrip1.Tag = null;
10076         }
10077
10078         private void MyList_ColumnReordered(object sender, ColumnReorderedEventArgs e)
10079         {
10080             DetailsListView lst = (DetailsListView)sender;
10081             if (_cfgLocal == null) return;
10082
10083             if (_iconCol)
10084             {
10085                 _cfgLocal.Width1 = lst.Columns[0].Width;
10086                 _cfgLocal.Width3 = lst.Columns[1].Width;
10087             }
10088             else
10089             {
10090                 int[] darr = new int[lst.Columns.Count];
10091                 for (int i = 0; i < lst.Columns.Count; i++)
10092                 {
10093                     darr[lst.Columns[i].DisplayIndex] = i;
10094                 }
10095                 MyCommon.MoveArrayItem(darr, e.OldDisplayIndex, e.NewDisplayIndex);
10096
10097                 for (int i = 0; i < lst.Columns.Count; i++)
10098                 {
10099                     switch (darr[i])
10100                     {
10101                         case 0:
10102                             _cfgLocal.DisplayIndex1 = i;
10103                             break;
10104                         case 1:
10105                             _cfgLocal.DisplayIndex2 = i;
10106                             break;
10107                         case 2:
10108                             _cfgLocal.DisplayIndex3 = i;
10109                             break;
10110                         case 3:
10111                             _cfgLocal.DisplayIndex4 = i;
10112                             break;
10113                         case 4:
10114                             _cfgLocal.DisplayIndex5 = i;
10115                             break;
10116                         case 5:
10117                             _cfgLocal.DisplayIndex6 = i;
10118                             break;
10119                         case 6:
10120                             _cfgLocal.DisplayIndex7 = i;
10121                             break;
10122                         case 7:
10123                             _cfgLocal.DisplayIndex8 = i;
10124                             break;
10125                     }
10126                 }
10127                 _cfgLocal.Width1 = lst.Columns[0].Width;
10128                 _cfgLocal.Width2 = lst.Columns[1].Width;
10129                 _cfgLocal.Width3 = lst.Columns[2].Width;
10130                 _cfgLocal.Width4 = lst.Columns[3].Width;
10131                 _cfgLocal.Width5 = lst.Columns[4].Width;
10132                 _cfgLocal.Width6 = lst.Columns[5].Width;
10133                 _cfgLocal.Width7 = lst.Columns[6].Width;
10134                 _cfgLocal.Width8 = lst.Columns[7].Width;
10135             }
10136             _modifySettingLocal = true;
10137             _isColumnChanged = true;
10138         }
10139
10140         private void MyList_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
10141         {
10142             DetailsListView lst = (DetailsListView)sender;
10143             if (_cfgLocal == null) return;
10144             if (_iconCol)
10145             {
10146                 if (_cfgLocal.Width1 != lst.Columns[0].Width)
10147                 {
10148                     _cfgLocal.Width1 = lst.Columns[0].Width;
10149                     _modifySettingLocal = true;
10150                     _isColumnChanged = true;
10151                 }
10152                 if (_cfgLocal.Width3 != lst.Columns[1].Width)
10153                 {
10154                     _cfgLocal.Width3 = lst.Columns[1].Width;
10155                     _modifySettingLocal = true;
10156                     _isColumnChanged = true;
10157                 }
10158             }
10159             else
10160             {
10161                 if (_cfgLocal.Width1 != lst.Columns[0].Width)
10162                 {
10163                     _cfgLocal.Width1 = lst.Columns[0].Width;
10164                     _modifySettingLocal = true;
10165                     _isColumnChanged = true;
10166                 }
10167                 if (_cfgLocal.Width2 != lst.Columns[1].Width)
10168                 {
10169                     _cfgLocal.Width2 = lst.Columns[1].Width;
10170                     _modifySettingLocal = true;
10171                     _isColumnChanged = true;
10172                 }
10173                 if (_cfgLocal.Width3 != lst.Columns[2].Width)
10174                 {
10175                     _cfgLocal.Width3 = lst.Columns[2].Width;
10176                     _modifySettingLocal = true;
10177                     _isColumnChanged = true;
10178                 }
10179                 if (_cfgLocal.Width4 != lst.Columns[3].Width)
10180                 {
10181                     _cfgLocal.Width4 = lst.Columns[3].Width;
10182                     _modifySettingLocal = true;
10183                     _isColumnChanged = true;
10184                 }
10185                 if (_cfgLocal.Width5 != lst.Columns[4].Width)
10186                 {
10187                     _cfgLocal.Width5 = lst.Columns[4].Width;
10188                     _modifySettingLocal = true;
10189                     _isColumnChanged = true;
10190                 }
10191                 if (_cfgLocal.Width6 != lst.Columns[5].Width)
10192                 {
10193                     _cfgLocal.Width6 = lst.Columns[5].Width;
10194                     _modifySettingLocal = true;
10195                     _isColumnChanged = true;
10196                 }
10197                 if (_cfgLocal.Width7 != lst.Columns[6].Width)
10198                 {
10199                     _cfgLocal.Width7 = lst.Columns[6].Width;
10200                     _modifySettingLocal = true;
10201                     _isColumnChanged = true;
10202                 }
10203                 if (_cfgLocal.Width8 != lst.Columns[7].Width)
10204                 {
10205                     _cfgLocal.Width8 = lst.Columns[7].Width;
10206                     _modifySettingLocal = true;
10207                     _isColumnChanged = true;
10208                 }
10209             }
10210             // 非表示の時にColumnChangedが呼ばれた場合はForm初期化処理中なので保存しない
10211             //if (changed)
10212             //{
10213             //    SaveConfigsLocal();
10214             //}
10215         }
10216
10217         public string WebBrowser_GetSelectionText(ref WebBrowser ComponentInstance)
10218         {
10219             //発言詳細で「選択文字列をコピー」を行う
10220             //WebBrowserコンポーネントのインスタンスを渡す
10221             Type typ = ComponentInstance.ActiveXInstance.GetType();
10222             object _SelObj = typ.InvokeMember("selection", BindingFlags.GetProperty, null, ComponentInstance.Document.DomDocument, null);
10223             object _objRange = _SelObj.GetType().InvokeMember("createRange", BindingFlags.InvokeMethod, null, _SelObj, null);
10224             return (string)_objRange.GetType().InvokeMember("text", BindingFlags.GetProperty, null, _objRange, null);
10225         }
10226
10227         private void SelectionCopyContextMenuItem_Click(object sender, EventArgs e)
10228         {
10229             //発言詳細で「選択文字列をコピー」
10230             string _selText = WebBrowser_GetSelectionText(ref PostBrowser);
10231             try
10232             {
10233                 Clipboard.SetDataObject(_selText, false, 5, 100);
10234             }
10235             catch (Exception ex)
10236             {
10237                 MessageBox.Show(ex.Message);
10238             }
10239         }
10240
10241         private void doSearchToolStrip(string url)
10242         {
10243             //発言詳細で「選択文字列で検索」(選択文字列取得)
10244             string _selText = WebBrowser_GetSelectionText(ref PostBrowser);
10245
10246             if (_selText != null)
10247             {
10248                 if (url == Properties.Resources.SearchItem4Url)
10249                 {
10250                     //公式検索
10251                     AddNewTabForSearch(_selText);
10252                     return;
10253                 }
10254
10255                 string tmp = string.Format(url, Uri.EscapeUriString(_selText));
10256                 OpenUriAsync(tmp);
10257             }
10258         }
10259
10260         private void SelectionAllContextMenuItem_Click(object sender, EventArgs e)
10261         {
10262             //発言詳細ですべて選択
10263             PostBrowser.Document.ExecCommand("SelectAll", false, null);
10264         }
10265
10266         private void SearchWikipediaContextMenuItem_Click(object sender, EventArgs e)
10267         {
10268             doSearchToolStrip(Properties.Resources.SearchItem1Url);
10269         }
10270
10271         private void SearchGoogleContextMenuItem_Click(object sender, EventArgs e)
10272         {
10273             doSearchToolStrip(Properties.Resources.SearchItem2Url);
10274         }
10275
10276         private void SearchPublicSearchContextMenuItem_Click(object sender, EventArgs e)
10277         {
10278             doSearchToolStrip(Properties.Resources.SearchItem4Url);
10279         }
10280
10281         private void UrlCopyContextMenuItem_Click(object sender, EventArgs e)
10282         {
10283             try
10284             {
10285                 MatchCollection mc = Regex.Matches(this.PostBrowser.DocumentText, @"<a[^>]*href=""(?<url>" + this._postBrowserStatusText.Replace(".", @"\.") + @")""[^>]*title=""(?<title>https?://[^""]+)""", RegexOptions.IgnoreCase);
10286                 foreach (Match m in mc)
10287                 {
10288                     if (m.Groups["url"].Value == this._postBrowserStatusText)
10289                     {
10290                         Clipboard.SetDataObject(m.Groups["title"].Value, false, 5, 100);
10291                         break;
10292                     }
10293                 }
10294                 if (mc.Count == 0)
10295                 {
10296                     Clipboard.SetDataObject(this._postBrowserStatusText, false, 5, 100);
10297                 }
10298                 //Clipboard.SetDataObject(this._postBrowserStatusText, false, 5, 100);
10299             }
10300             catch (Exception ex)
10301             {
10302                 MessageBox.Show(ex.Message);
10303             }
10304         }
10305
10306         private void ContextMenuPostBrowser_Opening(object ender, CancelEventArgs e)
10307         {
10308             // URLコピーの項目の表示/非表示
10309             if (PostBrowser.StatusText.StartsWith("http"))
10310             {
10311                 this._postBrowserStatusText = PostBrowser.StatusText;
10312                 string name = GetUserId();
10313                 UrlCopyContextMenuItem.Enabled = true;
10314                 if (name != null)
10315                 {
10316                     FollowContextMenuItem.Enabled = true;
10317                     RemoveContextMenuItem.Enabled = true;
10318                     FriendshipContextMenuItem.Enabled = true;
10319                     ShowUserStatusContextMenuItem.Enabled = true;
10320                     SearchPostsDetailToolStripMenuItem.Enabled = true;
10321                     IdFilterAddMenuItem.Enabled = true;
10322                     ListManageUserContextToolStripMenuItem.Enabled = true;
10323                     SearchAtPostsDetailToolStripMenuItem.Enabled = true;
10324                 }
10325                 else
10326                 {
10327                     FollowContextMenuItem.Enabled = false;
10328                     RemoveContextMenuItem.Enabled = false;
10329                     FriendshipContextMenuItem.Enabled = false;
10330                     ShowUserStatusContextMenuItem.Enabled = false;
10331                     SearchPostsDetailToolStripMenuItem.Enabled = false;
10332                     IdFilterAddMenuItem.Enabled = false;
10333                     ListManageUserContextToolStripMenuItem.Enabled = false;
10334                     SearchAtPostsDetailToolStripMenuItem.Enabled = false;
10335                 }
10336
10337                 if (Regex.IsMatch(this._postBrowserStatusText, @"^https?://twitter.com/search\?q=%23"))
10338                     UseHashtagMenuItem.Enabled = true;
10339                 else
10340                     UseHashtagMenuItem.Enabled = false;
10341             }
10342             else
10343             {
10344                 this._postBrowserStatusText = "";
10345                 UrlCopyContextMenuItem.Enabled = false;
10346                 FollowContextMenuItem.Enabled = false;
10347                 RemoveContextMenuItem.Enabled = false;
10348                 FriendshipContextMenuItem.Enabled = false;
10349                 ShowUserStatusContextMenuItem.Enabled = false;
10350                 SearchPostsDetailToolStripMenuItem.Enabled = false;
10351                 SearchAtPostsDetailToolStripMenuItem.Enabled = false;
10352                 UseHashtagMenuItem.Enabled = false;
10353                 IdFilterAddMenuItem.Enabled = false;
10354                 ListManageUserContextToolStripMenuItem.Enabled = false;
10355             }
10356             // 文字列選択されていないときは選択文字列関係の項目を非表示に
10357             string _selText = WebBrowser_GetSelectionText(ref PostBrowser);
10358             if (_selText == null)
10359             {
10360                 SelectionSearchContextMenuItem.Enabled = false;
10361                 SelectionCopyContextMenuItem.Enabled = false;
10362                 SelectionTranslationToolStripMenuItem.Enabled = false;
10363             }
10364             else
10365             {
10366                 SelectionSearchContextMenuItem.Enabled = true;
10367                 SelectionCopyContextMenuItem.Enabled = true;
10368                 SelectionTranslationToolStripMenuItem.Enabled = true;
10369             }
10370             //発言内に自分以外のユーザーが含まれてればフォロー状態全表示を有効に
10371             MatchCollection ma = Regex.Matches(this.PostBrowser.DocumentText, @"href=""https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/[0-9]+)?""");
10372             bool fAllFlag = false;
10373             foreach (Match mu in ma)
10374             {
10375                 if (mu.Result("${ScreenName}").ToLower() != tw.Username.ToLower())
10376                 {
10377                     fAllFlag = true;
10378                     break;
10379                 }
10380             }
10381             this.FriendshipAllMenuItem.Enabled = fAllFlag;
10382
10383             if (_curPost == null)
10384                 TranslationToolStripMenuItem.Enabled = false;
10385             else
10386                 TranslationToolStripMenuItem.Enabled = true;
10387
10388             e.Cancel = false;
10389         }
10390
10391         private void CurrentTabToolStripMenuItem_Click(object sender, EventArgs e)
10392         {
10393             //発言詳細の選択文字列で現在のタブを検索
10394             string _selText = WebBrowser_GetSelectionText(ref PostBrowser);
10395
10396             if (_selText != null)
10397             {
10398                 var searchOptions = new SearchWordDialog.SearchOptions(
10399                     SearchWordDialog.SearchType.Timeline,
10400                     _selText,
10401                     newTab: false,
10402                     caseSensitive: false,
10403                     useRegex: false);
10404
10405                 this.SearchDialog.ResultOptions = searchOptions;
10406
10407                 this.DoTabSearch(
10408                     searchOptions.Query,
10409                     searchOptions.CaseSensitive,
10410                     searchOptions.UseRegex,
10411                     SEARCHTYPE.NextSearch);
10412             }
10413         }
10414
10415         private void SplitContainer2_SplitterMoved(object sender, SplitterEventArgs e)
10416         {
10417             if (StatusText.Multiline) _mySpDis2 = StatusText.Height;
10418             _modifySettingLocal = true;
10419         }
10420
10421         private void TweenMain_DragDrop(object sender, DragEventArgs e)
10422         {
10423             if (e.Data.GetDataPresent(DataFormats.FileDrop))
10424             {
10425                 SelectMedia_DragDrop(e);
10426             }
10427             else if (e.Data.GetDataPresent("UniformResourceLocatorW"))
10428             {
10429                 var url = GetUrlFromDataObject(e.Data);
10430
10431                 string appendText;
10432                 if (url.Item2 == null)
10433                     appendText = url.Item1;
10434                 else
10435                     appendText = url.Item2 + " " + url.Item1;
10436
10437                 if (this.StatusText.TextLength == 0)
10438                     this.StatusText.Text = appendText;
10439                 else
10440                     this.StatusText.Text += " " + appendText;
10441             }
10442             else if (e.Data.GetDataPresent(DataFormats.StringFormat))
10443             {
10444                 string data = (string)e.Data.GetData(DataFormats.StringFormat, true);
10445                 if (data != null) StatusText.Text += data;
10446             }
10447         }
10448
10449         /// <summary>
10450         /// IDataObject から URL とタイトルの対を取得します
10451         /// </summary>
10452         /// <remarks>
10453         /// タイトルのみ取得できなかった場合は Value2 が null のタプルを返すことがあります。
10454         /// </remarks>
10455         /// <exception cref="ArgumentException">不正なフォーマットが入力された場合</exception>
10456         /// <exception cref="NotSupportedException">サポートされていないデータが入力された場合</exception>
10457         internal static Tuple<string, string> GetUrlFromDataObject(IDataObject data)
10458         {
10459             if (data.GetDataPresent("text/x-moz-url"))
10460             {
10461                 // Firefox, Google Chrome で利用可能
10462                 // 参照: https://developer.mozilla.org/ja/docs/DragDrop/Recommended_Drag_Types
10463
10464                 using (var stream = (MemoryStream)data.GetData("text/x-moz-url"))
10465                 {
10466                     var lines = Encoding.Unicode.GetString(stream.ToArray()).TrimEnd('\0').Split('\n');
10467                     if (lines.Length < 2)
10468                         throw new ArgumentException("不正な text/x-moz-url フォーマットです", "data");
10469
10470                     return new Tuple<string, string>(lines[0], lines[1]);
10471                 }
10472             }
10473             else if (data.GetDataPresent("IESiteModeToUrl"))
10474             {
10475                 // Internet Exproler 用
10476                 // 保護モードが有効なデフォルトの IE では DragDrop イベントが発火しないため使えない
10477
10478                 using (var stream = (MemoryStream)data.GetData("IESiteModeToUrl"))
10479                 {
10480                     var lines = Encoding.Unicode.GetString(stream.ToArray()).TrimEnd('\0').Split('\0');
10481                     if (lines.Length < 2)
10482                         throw new ArgumentException("不正な IESiteModeToUrl フォーマットです", "data");
10483
10484                     return new Tuple<string, string>(lines[0], lines[1]);
10485                 }
10486             }
10487             else if (data.GetDataPresent("UniformResourceLocatorW"))
10488             {
10489                 // それ以外のブラウザ向け
10490
10491                 using (var stream = (MemoryStream)data.GetData("UniformResourceLocatorW"))
10492                 {
10493                     var url = Encoding.Unicode.GetString(stream.ToArray()).TrimEnd('\0');
10494                     return new Tuple<string, string>(url, null);
10495                 }
10496             }
10497
10498             throw new NotSupportedException("サポートされていないデータ形式です: " + data.GetFormats()[0]);
10499         }
10500
10501         private void TweenMain_DragEnter(object sender, DragEventArgs e)
10502         {
10503             if (e.Data.GetDataPresent(DataFormats.FileDrop))
10504             {
10505                 SelectMedia_DragEnter(e);
10506             }
10507         }
10508
10509         private void TweenMain_DragOver(object sender, DragEventArgs e)
10510         {
10511             if (e.Data.GetDataPresent(DataFormats.FileDrop))
10512             {
10513                 SelectMedia_DragOver(e);
10514             }
10515             else if (e.Data.GetDataPresent("UniformResourceLocatorW"))
10516             {
10517                 e.Effect = DragDropEffects.Copy;
10518             }
10519             else if (e.Data.GetDataPresent(DataFormats.StringFormat))
10520             {
10521                 e.Effect = DragDropEffects.Copy;
10522             }
10523             else
10524             {
10525                 e.Effect = DragDropEffects.None;
10526             }
10527         }
10528
10529         public bool IsNetworkAvailable()
10530         {
10531             bool nw = true;
10532             nw = MyCommon.IsNetworkAvailable();
10533             _myStatusOnline = nw;
10534             return nw;
10535         }
10536
10537         public Task OpenUriAsync(string UriString)
10538         {
10539             return Task.Run(() =>
10540             {
10541                 string myPath = UriString;
10542
10543                 try
10544                 {
10545                     var configBrowserPath = SettingDialog.BrowserPath;
10546                     if (!string.IsNullOrEmpty(configBrowserPath))
10547                     {
10548                         if (configBrowserPath.StartsWith("\"") && configBrowserPath.Length > 2 && configBrowserPath.IndexOf("\"", 2) > -1)
10549                         {
10550                             int sep = configBrowserPath.IndexOf("\"", 2);
10551                             string browserPath = configBrowserPath.Substring(1, sep - 1);
10552                             string arg = "";
10553                             if (sep < configBrowserPath.Length - 1)
10554                             {
10555                                 arg = configBrowserPath.Substring(sep + 1);
10556                             }
10557                             myPath = arg + " " + myPath;
10558                             System.Diagnostics.Process.Start(browserPath, myPath);
10559                         }
10560                         else
10561                         {
10562                             System.Diagnostics.Process.Start(configBrowserPath, myPath);
10563                         }
10564                     }
10565                     else
10566                     {
10567                         System.Diagnostics.Process.Start(myPath);
10568                     }
10569                 }
10570                 catch (Exception)
10571                 {
10572                     //MessageBox.Show("ブラウザの起動に失敗、またはタイムアウトしました。" + ex.ToString());
10573                 }
10574             });
10575         }
10576
10577         private void ListTabSelect(TabPage _tab)
10578         {
10579             SetListProperty();
10580
10581             this.PurgeListViewItemCache();
10582
10583             _curTab = _tab;
10584             _curList = (DetailsListView)_tab.Tag;
10585             if (_curList.SelectedIndices.Count > 0)
10586             {
10587                 _curItemIndex = _curList.SelectedIndices[0];
10588                 _curPost = GetCurTabPost(_curItemIndex);
10589             }
10590             else
10591             {
10592                 _curItemIndex = -1;
10593                 _curPost = null;
10594             }
10595
10596             _anchorPost = null;
10597             _anchorFlag = false;
10598
10599             if (_iconCol)
10600             {
10601                 ((DetailsListView)_tab.Tag).Columns[1].Text = ColumnText[2];
10602             }
10603             else
10604             {
10605                 for (int i = 0; i < _curList.Columns.Count; i++)
10606                 {
10607                     ((DetailsListView)_tab.Tag).Columns[i].Text = ColumnText[i];
10608                 }
10609             }
10610         }
10611
10612         private void ListTab_Selecting(object sender, TabControlCancelEventArgs e)
10613         {
10614             ListTabSelect(e.TabPage);
10615         }
10616
10617         private void SelectListItem(DetailsListView LView, int Index)
10618         {
10619             //単一
10620             Rectangle bnd = new Rectangle();
10621             bool flg = false;
10622             var item = LView.FocusedItem;
10623             if (item != null)
10624             {
10625                 bnd = item.Bounds;
10626                 flg = true;
10627             }
10628
10629             do
10630             {
10631                 LView.SelectedIndices.Clear();
10632             }
10633             while (LView.SelectedIndices.Count > 0);
10634             item = LView.Items[Index];
10635             item.Selected = true;
10636             item.Focused = true;
10637
10638             if (flg) LView.Invalidate(bnd);
10639         }
10640
10641         private void SelectListItem(DetailsListView LView , int[] Index, int[] FocusedIndex)
10642         {
10643             //複数
10644             Rectangle bnd = new Rectangle();
10645             bool flg = false;
10646             var item = LView.FocusedItem;
10647             if (item != null)
10648             {
10649                 bnd = item.Bounds;
10650                 flg = true;
10651             }
10652
10653             int fIdx = -1;
10654             if (Index != null && !(Index.Length == 1 && Index[0] == -1))
10655             {
10656                 do
10657                 {
10658                     LView.SelectedIndices.Clear();
10659                 }
10660                 while (LView.SelectedIndices.Count > 0);
10661                 foreach (int idx in Index)
10662                 {
10663                     if (idx > -1 && LView.VirtualListSize > idx)
10664                     {
10665                         LView.SelectedIndices.Add(idx);
10666                         if (fIdx == -1) fIdx = idx;
10667                     }
10668                 }
10669             }
10670             if (FocusedIndex[1] > -1 && LView.VirtualListSize > FocusedIndex[1])
10671             {
10672                 LView.SelectionMark = FocusedIndex[1];
10673             }
10674             if (FocusedIndex[0] > -1 && LView.VirtualListSize > FocusedIndex[0])
10675             {
10676                 LView.Items[FocusedIndex[0]].Focused = true;
10677             }
10678             else if (fIdx > -1)
10679             {
10680                 LView.Items[fIdx].Focused = true;
10681             }
10682
10683             if (flg) LView.Invalidate(bnd);
10684         }
10685
10686         private void RunAsync(GetWorkerArg args)
10687         {
10688             BackgroundWorker bw = null;
10689             if (args.type != MyCommon.WORKERTYPE.Follower)
10690             {
10691                 for (int i = 0; i < _bw.Length; i++)
10692                 {
10693                     if (_bw[i] != null && !_bw[i].IsBusy)
10694                     {
10695                         bw = _bw[i];
10696                         break;
10697                     }
10698                 }
10699                 if (bw == null)
10700                 {
10701                     for (int i = 0; i < _bw.Length; i++)
10702                     {
10703                         if (_bw[i] == null)
10704                         {
10705                             _bw[i] = new BackgroundWorker();
10706                             bw = _bw[i];
10707                             bw.WorkerReportsProgress = true;
10708                             bw.WorkerSupportsCancellation = true;
10709                             bw.DoWork += GetTimelineWorker_DoWork;
10710                             bw.ProgressChanged += GetTimelineWorker_ProgressChanged;
10711                             bw.RunWorkerCompleted += GetTimelineWorker_RunWorkerCompleted;
10712                             break;
10713                         }
10714                     }
10715                 }
10716             }
10717             else
10718             {
10719                 if (_bwFollower == null)
10720                 {
10721                     _bwFollower = new BackgroundWorker();
10722                     bw = _bwFollower;
10723                     bw.WorkerReportsProgress = true;
10724                     bw.WorkerSupportsCancellation = true;
10725                     bw.DoWork += GetTimelineWorker_DoWork;
10726                     bw.ProgressChanged += GetTimelineWorker_ProgressChanged;
10727                     bw.RunWorkerCompleted += GetTimelineWorker_RunWorkerCompleted;
10728                 }
10729                 else
10730                 {
10731                     if (_bwFollower.IsBusy == false)
10732                         bw = _bwFollower;
10733                 }
10734             }
10735             if (bw == null) return;
10736
10737             bw.RunWorkerAsync(args);
10738         }
10739
10740         private void StartUserStream()
10741         {
10742             tw.NewPostFromStream += tw_NewPostFromStream;
10743             tw.UserStreamStarted += tw_UserStreamStarted;
10744             tw.UserStreamStopped += tw_UserStreamStopped;
10745             tw.PostDeleted += tw_PostDeleted;
10746             tw.UserStreamEventReceived += tw_UserStreamEventArrived;
10747
10748             MenuItemUserStream.Text = "&UserStream ■";
10749             MenuItemUserStream.Enabled = true;
10750             StopToolStripMenuItem.Text = "&Start";
10751             StopToolStripMenuItem.Enabled = true;
10752             if (this._cfgCommon.UserstreamStartup) tw.StartUserStream();
10753         }
10754
10755         private async void TweenMain_Shown(object sender, EventArgs e)
10756         {
10757             try
10758             {
10759                 PostBrowser.Url = new Uri("about:blank");
10760                 PostBrowser.DocumentText = "";       //発言詳細部初期化
10761             }
10762             catch (Exception)
10763             {
10764             }
10765
10766             NotifyIcon1.Visible = true;
10767
10768             if (this.IsNetworkAvailable())
10769             {
10770                 this.RefreshMuteUserIdsAsync();
10771                 GetTimeline(MyCommon.WORKERTYPE.BlockIds, 0, "");
10772                 GetTimeline(MyCommon.WORKERTYPE.NoRetweetIds, 0, "");
10773                 if (this._cfgCommon.StartupFollowers)
10774                 {
10775                     GetTimeline(MyCommon.WORKERTYPE.Follower, 0, "");
10776                 }
10777                 GetTimeline(MyCommon.WORKERTYPE.Configuration, 0, "");
10778                 StartUserStream();
10779                 _waitTimeline = true;
10780                 GetTimeline(MyCommon.WORKERTYPE.Timeline, 1, "");
10781                 _waitReply = true;
10782                 GetTimeline(MyCommon.WORKERTYPE.Reply, 1, "");
10783                 _waitDm = true;
10784                 GetTimeline(MyCommon.WORKERTYPE.DirectMessegeRcv, 1, "");
10785                 if (this._cfgCommon.GetFav)
10786                 {
10787                     _waitFav = true;
10788                     GetTimeline(MyCommon.WORKERTYPE.Favorites, 1, "");
10789                 }
10790                 _waitPubSearch = true;
10791                 GetTimeline(MyCommon.WORKERTYPE.PublicSearch, 1, "");  //tabname="":全タブ
10792                 _waitUserTimeline = true;
10793                 GetTimeline(MyCommon.WORKERTYPE.UserTimeline, 1, "");  //tabname="":全タブ
10794                 _waitLists = true;
10795                 GetTimeline(MyCommon.WORKERTYPE.List, 1, "");  //tabname="":全タブ
10796
10797                 var i = 0;
10798                 while (this.IsInitialRead())
10799                 {
10800                     await Task.Delay(5000);
10801
10802                     i += 1;
10803                     if (i > 24) break; // 120秒間初期処理が終了しなかったら強制的に打ち切る
10804
10805                     if (MyCommon._endingFlag)
10806                         return;
10807                 }
10808
10809                 if (MyCommon._endingFlag) return;
10810
10811                 if (ApplicationSettings.VersionInfoUrl != null)
10812                 {
10813                     //バージョンチェック(引数:起動時チェックの場合はtrue・・・チェック結果のメッセージを表示しない)
10814                     if (this._cfgCommon.StartupVersion)
10815                         await this.CheckNewVersion(true);
10816                 }
10817                 else
10818                 {
10819                     // ApplicationSetting.cs の設定により更新チェックが無効化されている場合
10820                     this.VerUpMenuItem.Enabled = false;
10821                     this.VerUpMenuItem.Available = false;
10822                     this.ToolStripSeparator16.Available = false; // VerUpMenuItem の一つ上にあるセパレータ
10823                 }
10824
10825                 // 取得失敗の場合は再試行する
10826                 if (!tw.GetFollowersSuccess && this._cfgCommon.StartupFollowers)
10827                     GetTimeline(MyCommon.WORKERTYPE.Follower, 0, "");
10828
10829                 // 取得失敗の場合は再試行する
10830                 if (!tw.GetNoRetweetSuccess)
10831                     GetTimeline(MyCommon.WORKERTYPE.NoRetweetIds, 0, "");
10832
10833                 // 取得失敗の場合は再試行する
10834                 if (SettingDialog.TwitterConfiguration.PhotoSizeLimit == 0)
10835                     GetTimeline(MyCommon.WORKERTYPE.Configuration, 0, "");
10836
10837                 // 権限チェック read/write権限(xAuthで取得したトークン)の場合は再認証を促す
10838                 if (MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.ReadWrite)
10839                 {
10840                     MessageBox.Show(Properties.Resources.ReAuthorizeText);
10841                     SettingStripMenuItem_Click(null, null);
10842                 }
10843
10844                 //
10845             }
10846             _initial = false;
10847
10848             TimerTimeline.Enabled = true;
10849         }
10850
10851         private bool IsInitialRead()
10852         {
10853             return _waitTimeline || _waitReply || _waitDm || _waitFav || _waitPubSearch || _waitUserTimeline || _waitLists;
10854         }
10855
10856         private void doGetFollowersMenu()
10857         {
10858             GetTimeline(MyCommon.WORKERTYPE.Follower, 1, "");
10859             DispSelectedPost(true);
10860         }
10861
10862         private void GetFollowersAllToolStripMenuItem_Click(object sender, EventArgs e)
10863         {
10864             doGetFollowersMenu();
10865         }
10866
10867         private void doReTweetUnofficial()
10868         {
10869             //RT @id:内容
10870             if (this.ExistCurrentPost)
10871             {
10872                 if (_curPost.IsDm ||
10873                     !StatusText.Enabled) return;
10874
10875                 if (_curPost.IsProtect)
10876                 {
10877                     MessageBox.Show("Protected.");
10878                     return;
10879                 }
10880                 string rtdata = _curPost.Text;
10881                 rtdata = CreateRetweetUnofficial(rtdata, this.StatusText.Multiline);
10882
10883                 this._reply_to_id = null;
10884                 this._reply_to_name = null;
10885
10886                 StatusText.Text = "RT @" + _curPost.ScreenName + ": " + rtdata;
10887
10888                 StatusText.SelectionStart = 0;
10889                 StatusText.Focus();
10890             }
10891         }
10892
10893         private void ReTweetStripMenuItem_Click(object sender, EventArgs e)
10894         {
10895             doReTweetUnofficial();
10896         }
10897
10898         private void doReTweetOfficial(bool isConfirm)
10899         {
10900             //公式RT
10901             if (this.ExistCurrentPost)
10902             {
10903                 if (_curPost.IsProtect)
10904                 {
10905                     MessageBox.Show("Protected.");
10906                     _DoFavRetweetFlags = false;
10907                     return;
10908                 }
10909                 if (_curList.SelectedIndices.Count > 15)
10910                 {
10911                     MessageBox.Show(Properties.Resources.RetweetLimitText);
10912                     _DoFavRetweetFlags = false;
10913                     return;
10914                 }
10915                 else if (_curList.SelectedIndices.Count > 1)
10916                 {
10917                     string QuestionText = Properties.Resources.RetweetQuestion2;
10918                     if (_DoFavRetweetFlags) QuestionText = Properties.Resources.FavoriteRetweetQuestionText1;
10919                     switch (MessageBox.Show(QuestionText, "Retweet", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question))
10920                     {
10921                         case DialogResult.Cancel:
10922                         case DialogResult.No:
10923                             _DoFavRetweetFlags = false;
10924                             return;
10925                     }
10926                 }
10927                 else
10928                 {
10929                     if (_curPost.IsDm || _curPost.IsMe)
10930                     {
10931                         _DoFavRetweetFlags = false;
10932                         return;
10933                     }
10934                     if (!this._cfgCommon.RetweetNoConfirm)
10935                     {
10936                         string Questiontext = Properties.Resources.RetweetQuestion1;
10937                         if (_DoFavRetweetFlags) Questiontext = Properties.Resources.FavoritesRetweetQuestionText2;
10938                         if (isConfirm && MessageBox.Show(Questiontext, "Retweet", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
10939                         {
10940                             _DoFavRetweetFlags = false;
10941                             return;
10942                         }
10943                     }
10944                 }
10945                 GetWorkerArg args = new GetWorkerArg();
10946                 args.ids = new List<long>();
10947                 args.sIds = new List<long>();
10948                 args.tName = _curTab.Text;
10949                 args.type = MyCommon.WORKERTYPE.Retweet;
10950                 foreach (int idx in _curList.SelectedIndices)
10951                 {
10952                     PostClass post = GetCurTabPost(idx);
10953                     if (!post.IsMe && !post.IsProtect && !post.IsDm) args.ids.Add(post.StatusId);
10954                 }
10955                 RunAsync(args);
10956             }
10957         }
10958
10959         private void ReTweetOriginalStripMenuItem_Click(object sender, EventArgs e)
10960         {
10961             doReTweetOfficial(true);
10962         }
10963
10964         private void FavoritesRetweetOriginal()
10965         {
10966             if (!this.ExistCurrentPost) return;
10967             _DoFavRetweetFlags = true;
10968             doReTweetOfficial(true);
10969             if (_DoFavRetweetFlags)
10970             {
10971                 _DoFavRetweetFlags = false;
10972                 FavoriteChange(true, false);
10973             }
10974         }
10975
10976         private void FavoritesRetweetUnofficial()
10977         {
10978             if (this.ExistCurrentPost && !_curPost.IsDm)
10979             {
10980                 _DoFavRetweetFlags = true;
10981                 FavoriteChange(true);
10982                 if (!_curPost.IsProtect && _DoFavRetweetFlags)
10983                 {
10984                     _DoFavRetweetFlags = false;
10985                     doReTweetUnofficial();
10986                 }
10987             }
10988         }
10989
10990         /// <summary>
10991         /// TweetFormatterクラスによって整形された状態のHTMLを、非公式RT用に元のツイートに復元します
10992         /// </summary>
10993         /// <param name="statusHtml">TweetFormatterによって整形された状態のHTML</param>
10994         /// <param name="multiline">trueであればBRタグを改行に、falseであればスペースに変換します</param>
10995         /// <returns>復元されたツイート本文</returns>
10996         internal static string CreateRetweetUnofficial(string statusHtml, bool multiline)
10997         {
10998             // TweetFormatterクラスによって整形された状態のHTMLを元のツイートに復元します
10999
11000             // 通常の URL
11001             statusHtml = Regex.Replace(statusHtml, "<a href=\"(?<href>.+?)\" title=\"(?<title>.+?)\">(?<text>.+?)</a>", "${title}");
11002             // メンション
11003             statusHtml = Regex.Replace(statusHtml, "<a class=\"mention\" href=\"(?<href>.+?)\">(?<text>.+?)</a>", "${text}");
11004             // ハッシュタグ
11005             statusHtml = Regex.Replace(statusHtml, "<a class=\"hashtag\" href=\"(?<href>.+?)\">(?<text>.+?)</a>", "${text}");
11006
11007             // <br> 除去
11008             if (multiline)
11009                 statusHtml = statusHtml.Replace("<br>", Environment.NewLine);
11010             else
11011                 statusHtml = statusHtml.Replace("<br>", " ");
11012
11013             // &nbsp; は本来であれば U+00A0 (NON-BREAK SPACE) に置換すべきですが、
11014             // 現状では半角スペースの代用として &nbsp; を使用しているため U+0020 に置換します
11015             statusHtml = statusHtml.Replace("&nbsp;", " ");
11016
11017             return WebUtility.HtmlDecode(statusHtml);
11018         }
11019
11020         private void DumpPostClassToolStripMenuItem_Click(object sender, EventArgs e)
11021         {
11022             if (_curPost != null)
11023                 DispSelectedPost(true);
11024         }
11025
11026         private void MenuItemHelp_DropDownOpening(object sender, EventArgs e)
11027         {
11028             if (MyCommon.DebugBuild || MyCommon.IsKeyDown(Keys.CapsLock, Keys.Control, Keys.Shift))
11029                 DebugModeToolStripMenuItem.Visible = true;
11030             else
11031                 DebugModeToolStripMenuItem.Visible = false;
11032         }
11033
11034         private void ToolStripMenuItemUrlAutoShorten_CheckedChanged(object sender, EventArgs e)
11035         {
11036             SettingDialog.UrlConvertAuto = ToolStripMenuItemUrlAutoShorten.Checked;
11037         }
11038
11039         private void ContextMenuPostMode_Opening(object sender, CancelEventArgs e)
11040         {
11041             ToolStripMenuItemUrlAutoShorten.Checked = SettingDialog.UrlConvertAuto;
11042         }
11043
11044         private void TraceOutToolStripMenuItem_Click(object sender, EventArgs e)
11045         {
11046             if (TraceOutToolStripMenuItem.Checked)
11047                 MyCommon.TraceFlag = true;
11048             else
11049                 MyCommon.TraceFlag = false;
11050         }
11051
11052         private void TweenMain_Deactivate(object sender, EventArgs e)
11053         {
11054             //画面が非アクティブになったら、発言欄の背景色をデフォルトへ
11055             this.StatusText_Leave(StatusText, System.EventArgs.Empty);
11056         }
11057
11058         private void TabRenameMenuItem_Click(object sender, EventArgs e)
11059         {
11060             if (string.IsNullOrEmpty(_rclickTabName)) return;
11061             TabRename(ref _rclickTabName);
11062         }
11063
11064         private async void BitlyToolStripMenuItem_Click(object sender, EventArgs e)
11065         {
11066             await UrlConvertAsync(MyCommon.UrlConverter.Bitly);
11067         }
11068
11069         private async void JmpToolStripMenuItem_Click(object sender, EventArgs e)
11070         {
11071             await UrlConvertAsync(MyCommon.UrlConverter.Jmp);
11072         }
11073
11074
11075         private void GetApiInfo_Dowork(object sender, DoWorkEventArgs e)
11076         {
11077             e.Result = tw.GetInfoApi();
11078         }
11079
11080         private void ApiUsageInfoMenuItem_Click(object sender, EventArgs e)
11081         {
11082             StringBuilder tmp = new StringBuilder();
11083
11084             using (FormInfo dlg = new FormInfo(this, Properties.Resources.ApiInfo6, GetApiInfo_Dowork))
11085             {
11086                 dlg.ShowDialog();
11087
11088                 var result = (TwitterApiStatus)dlg.Result;
11089
11090                 if (result == null)
11091                 {
11092                     var accessLevel = result.AccessLevel;
11093                     var timelineLimit = result.AccessLimit["/statuses/home_timeline"];
11094                     var mediaLimit = result.MediaUploadLimit;
11095
11096                     tmp.AppendLine(Properties.Resources.ApiInfo1 + timelineLimit.AccessLimitCount);
11097                     tmp.AppendLine(Properties.Resources.ApiInfo2 + timelineLimit.AccessLimitRemain);
11098                     tmp.AppendLine(Properties.Resources.ApiInfo3 + timelineLimit.AccessLimitResetDate);
11099                     tmp.AppendLine(Properties.Resources.ApiInfo7 + (tw.UserStreamEnabled ? Properties.Resources.Enable : Properties.Resources.Disable));
11100
11101                     tmp.AppendLine();
11102                     tmp.AppendLine(Properties.Resources.ApiInfo8 + accessLevel);
11103                     SetStatusLabelUrl();
11104
11105                     tmp.AppendLine();
11106                     tmp.AppendLine(Properties.Resources.ApiInfo9 + (mediaLimit == null ? Properties.Resources.ApiInfo91 : mediaLimit.AccessLimitCount.ToString()));
11107                     tmp.AppendLine(Properties.Resources.ApiInfo10 + (mediaLimit == null ? Properties.Resources.ApiInfo91 : mediaLimit.AccessLimitRemain.ToString()));
11108                     tmp.AppendLine(Properties.Resources.ApiInfo11 + (mediaLimit == null ? Properties.Resources.ApiInfo91 : mediaLimit.AccessLimitResetDate.ToString()));
11109                 }
11110                 else
11111                 {
11112                     tmp.Append(Properties.Resources.ApiInfo5);
11113                 }
11114             }
11115
11116             MessageBox.Show(tmp.ToString(), Properties.Resources.ApiInfo4, MessageBoxButtons.OK, MessageBoxIcon.Information);
11117         }
11118
11119         private void FollowCommandMenuItem_Click(object sender, EventArgs e)
11120         {
11121             string id = "";
11122             if (_curPost != null) id = _curPost.ScreenName;
11123             FollowCommand(id);
11124         }
11125
11126         private void FollowCommand_DoWork(object sender, DoWorkEventArgs e)
11127         {
11128             FollowRemoveCommandArgs arg = (FollowRemoveCommandArgs)e.Argument;
11129             e.Result = arg.tw.PostFollowCommand(arg.id);
11130         }
11131
11132         private void FollowCommand(string id)
11133         {
11134             using (InputTabName inputName = new InputTabName())
11135             {
11136                 inputName.FormTitle = "Follow";
11137                 inputName.FormDescription = Properties.Resources.FRMessage1;
11138                 inputName.TabName = id;
11139                 if (inputName.ShowDialog() == DialogResult.OK &&
11140                     !string.IsNullOrEmpty(inputName.TabName.Trim()))
11141                 {
11142                     FollowRemoveCommandArgs arg = new FollowRemoveCommandArgs();
11143                     arg.tw = tw;
11144                     arg.id = inputName.TabName.Trim();
11145                     using (FormInfo _info = new FormInfo(this, Properties.Resources.FollowCommandText1,
11146                                                          FollowCommand_DoWork,
11147                                                          null,
11148                                                          arg))
11149                     {
11150                         _info.ShowDialog();
11151                         string ret = (string)_info.Result;
11152                         if (!string.IsNullOrEmpty(ret))
11153                             MessageBox.Show(Properties.Resources.FRMessage2 + ret);
11154                         else
11155                             MessageBox.Show(Properties.Resources.FRMessage3);
11156                     }
11157                 }
11158             }
11159         }
11160
11161         private void RemoveCommandMenuItem_Click(object sender, EventArgs e)
11162         {
11163             string id = "";
11164             if (_curPost != null) id = _curPost.ScreenName;
11165             RemoveCommand(id, false);
11166         }
11167
11168         private class FollowRemoveCommandArgs
11169         {
11170             public Twitter tw;
11171             public string id;
11172         }
11173
11174         private void RemoveCommand_DoWork(object sender , DoWorkEventArgs e)
11175         {
11176             FollowRemoveCommandArgs arg = (FollowRemoveCommandArgs)e.Argument;
11177             e.Result = arg.tw.PostRemoveCommand(arg.id);
11178         }
11179
11180         private void RemoveCommand(string id, bool skipInput)
11181         {
11182             FollowRemoveCommandArgs arg = new FollowRemoveCommandArgs();
11183             arg.tw = tw;
11184             arg.id = id;
11185             if (!skipInput)
11186             {
11187                 using (InputTabName inputName = new InputTabName())
11188                 {
11189                     inputName.FormTitle = "Unfollow";
11190                     inputName.FormDescription = Properties.Resources.FRMessage1;
11191                     inputName.TabName = id;
11192                     if (inputName.ShowDialog() == DialogResult.OK &&
11193                         !string.IsNullOrEmpty(inputName.TabName.Trim()))
11194                     {
11195                         arg.tw = tw;
11196                         arg.id = inputName.TabName.Trim();
11197                     }
11198                     else
11199                     {
11200                         return;
11201                     }
11202                 }
11203             }
11204
11205             using (FormInfo _info = new FormInfo(this, Properties.Resources.RemoveCommandText1,
11206                                                  RemoveCommand_DoWork,
11207                                                  null,
11208                                                  arg))
11209             {
11210                 _info.ShowDialog();
11211                 string ret = (string)_info.Result;
11212                 if (!string.IsNullOrEmpty(ret))
11213                     MessageBox.Show(Properties.Resources.FRMessage2 + ret);
11214                 else
11215                     MessageBox.Show(Properties.Resources.FRMessage3);
11216             }
11217         }
11218
11219         private void FriendshipMenuItem_Click(object sender, EventArgs e)
11220         {
11221             string id = "";
11222             if (_curPost != null)
11223                 id = _curPost.ScreenName;
11224
11225             ShowFriendship(id);
11226         }
11227
11228         private class ShowFriendshipArgs
11229         {
11230             public Twitter tw;
11231             public class FriendshipInfo
11232             {
11233                 public string id = "";
11234                 public bool isFollowing = false;
11235                 public bool isFollowed = false;
11236                 public FriendshipInfo(string id)
11237                 {
11238                     this.id = id;
11239                 }
11240             }
11241             public List<FriendshipInfo> ids = new List<FriendshipInfo>();
11242         }
11243
11244         private void ShowFriendship_DoWork(object sender, DoWorkEventArgs e)
11245         {
11246             ShowFriendshipArgs arg = (ShowFriendshipArgs)e.Argument;
11247             string result = "";
11248             foreach (ShowFriendshipArgs.FriendshipInfo fInfo in arg.ids)
11249             {
11250                 string rt = arg.tw.GetFriendshipInfo(fInfo.id, ref fInfo.isFollowing, ref fInfo.isFollowed);
11251                 if (!string.IsNullOrEmpty(rt))
11252                 {
11253                     if (string.IsNullOrEmpty(result)) result = rt;
11254                 }
11255             }
11256             e.Result = result;
11257         }
11258
11259         private void ShowFriendship(string id)
11260         {
11261             ShowFriendshipArgs args = new ShowFriendshipArgs();
11262             args.tw = tw;
11263             using (InputTabName inputName = new InputTabName())
11264             {
11265                 inputName.FormTitle = "Show Friendships";
11266                 inputName.FormDescription = Properties.Resources.FRMessage1;
11267                 inputName.TabName = id;
11268                 if (inputName.ShowDialog() == DialogResult.OK &&
11269                     !string.IsNullOrEmpty(inputName.TabName.Trim()))
11270                 {
11271                     string ret = "";
11272                     args.ids.Add(new ShowFriendshipArgs.FriendshipInfo(inputName.TabName.Trim()));
11273                     using (FormInfo _info = new FormInfo(this, Properties.Resources.ShowFriendshipText1,
11274                                                          ShowFriendship_DoWork,
11275                                                          null,
11276                                                          args))
11277                     {
11278                         _info.ShowDialog();
11279                         ret = (string)_info.Result;
11280                     }
11281                     string result = "";
11282                     if (string.IsNullOrEmpty(ret))
11283                     {
11284                         if (args.ids[0].isFollowing)
11285                         {
11286                             result = Properties.Resources.GetFriendshipInfo1 + System.Environment.NewLine;
11287                         }
11288                         else
11289                         {
11290                             result = Properties.Resources.GetFriendshipInfo2 + System.Environment.NewLine;
11291                         }
11292                         if (args.ids[0].isFollowed)
11293                         {
11294                             result += Properties.Resources.GetFriendshipInfo3;
11295                         }
11296                         else
11297                         {
11298                             result += Properties.Resources.GetFriendshipInfo4;
11299                         }
11300                         result = args.ids[0].id + Properties.Resources.GetFriendshipInfo5 + System.Environment.NewLine + result;
11301                     }
11302                     else
11303                     {
11304                         result = ret;
11305                     }
11306                     MessageBox.Show(result);
11307                 }
11308             }
11309         }
11310
11311         private void ShowFriendship(string[] ids)
11312         {
11313             foreach (string id in ids)
11314             {
11315                 string ret = "";
11316                 ShowFriendshipArgs args = new ShowFriendshipArgs();
11317                 args.tw = tw;
11318                 args.ids.Add(new ShowFriendshipArgs.FriendshipInfo(id.Trim()));
11319                 using (FormInfo _info = new FormInfo(this, Properties.Resources.ShowFriendshipText1,
11320                                                      ShowFriendship_DoWork,
11321                                                      null,
11322                                                      args))
11323                 {
11324                     _info.ShowDialog();
11325                     ret = (string)_info.Result;
11326                 }
11327                 string result = "";
11328                 ShowFriendshipArgs.FriendshipInfo fInfo = args.ids[0];
11329                 string ff = "";
11330                 if (string.IsNullOrEmpty(ret))
11331                 {
11332                     ff = "  ";
11333                     if (fInfo.isFollowing)
11334                     {
11335                         ff += Properties.Resources.GetFriendshipInfo1;
11336                     }
11337                     else
11338                     {
11339                         ff += Properties.Resources.GetFriendshipInfo2;
11340                     }
11341
11342                     ff += System.Environment.NewLine + "  ";
11343                     if (fInfo.isFollowed)
11344                     {
11345                         ff += Properties.Resources.GetFriendshipInfo3;
11346                     }
11347                     else
11348                     {
11349                         ff += Properties.Resources.GetFriendshipInfo4;
11350                     }
11351                     result += fInfo.id + Properties.Resources.GetFriendshipInfo5 + System.Environment.NewLine + ff;
11352                     if (fInfo.isFollowing)
11353                     {
11354                         if (MessageBox.Show(
11355                             Properties.Resources.GetFriendshipInfo7 + System.Environment.NewLine + result, Properties.Resources.GetFriendshipInfo8,
11356                             MessageBoxButtons.YesNo,
11357                             MessageBoxIcon.Question,
11358                             MessageBoxDefaultButton.Button2) == DialogResult.Yes)
11359                         {
11360                             RemoveCommand(fInfo.id, true);
11361                         }
11362                     }
11363                     else
11364                     {
11365                         MessageBox.Show(result);
11366                     }
11367                 }
11368                 else
11369                 {
11370                     MessageBox.Show(ret);
11371                 }
11372             }
11373         }
11374
11375         private void OwnStatusMenuItem_Click(object sender, EventArgs e)
11376         {
11377             doShowUserStatus(tw.Username, false);
11378             //if (!string.IsNullOrEmpty(tw.UserInfoXml))
11379             //{
11380             //    doShowUserStatus(tw.Username, false);
11381             //}
11382             //else
11383             //{
11384             //    MessageBox.Show(Properties.Resources.ShowYourProfileText1, "Your status", MessageBoxButtons.OK, MessageBoxIcon.Information);
11385             //    return;
11386             //}
11387         }
11388
11389         // TwitterIDでない固定文字列を調べる(文字列検証のみ 実際に取得はしない)
11390         // URLから切り出した文字列を渡す
11391
11392         public bool IsTwitterId(string name)
11393         {
11394             if (SettingDialog.TwitterConfiguration.NonUsernamePaths == null || SettingDialog.TwitterConfiguration.NonUsernamePaths.Length == 0)
11395                 return !Regex.Match(name, @"^(about|jobs|tos|privacy|who_to_follow|download|messages)$", RegexOptions.IgnoreCase).Success;
11396             else
11397                 return !SettingDialog.TwitterConfiguration.NonUsernamePaths.Contains(name.ToLower());
11398         }
11399
11400         private string GetUserId()
11401         {
11402             Match m = Regex.Match(this._postBrowserStatusText, @"^https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/[0-9]+)?$");
11403             if (m.Success && IsTwitterId(m.Result("${ScreenName}")))
11404                 return m.Result("${ScreenName}");
11405             else
11406                 return null;
11407         }
11408
11409         private void FollowContextMenuItem_Click(object sender, EventArgs e)
11410         {
11411             string name = GetUserId();
11412             if (name != null) FollowCommand(name);
11413         }
11414
11415         private void RemoveContextMenuItem_Click(object sender, EventArgs e)
11416         {
11417             string name = GetUserId();
11418             if (name != null) RemoveCommand(name, false);
11419         }
11420
11421         private void FriendshipContextMenuItem_Click(object sender, EventArgs e)
11422         {
11423             string name = GetUserId();
11424             if (name != null) ShowFriendship(name);
11425         }
11426
11427         private void FriendshipAllMenuItem_Click(object sender, EventArgs e)
11428         {
11429             MatchCollection ma = Regex.Matches(this.PostBrowser.DocumentText, @"href=""https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/[0-9]+)?""");
11430             List<string> ids = new List<string>();
11431             foreach (Match mu in ma)
11432             {
11433                 if (mu.Result("${ScreenName}").ToLower() != tw.Username.ToLower())
11434                 {
11435                     ids.Add(mu.Result("${ScreenName}"));
11436                 }
11437             }
11438             ShowFriendship(ids.ToArray());
11439         }
11440
11441         private void ShowUserStatusContextMenuItem_Click(object sender, EventArgs e)
11442         {
11443             string name = GetUserId();
11444             if (name != null) ShowUserStatus(name);
11445         }
11446
11447         private void SearchPostsDetailToolStripMenuItem_Click(object sender, EventArgs e)
11448         {
11449             string name = GetUserId();
11450             if (name != null) AddNewTabForUserTimeline(name);
11451         }
11452
11453         private void SearchAtPostsDetailToolStripMenuItem_Click(object sender, EventArgs e)
11454         {
11455             string name = GetUserId();
11456             if (name != null) AddNewTabForSearch("@" + name);
11457         }
11458
11459         private void IdeographicSpaceToSpaceToolStripMenuItem_Click(object sender, EventArgs e)
11460         {
11461             _modifySettingCommon = true;
11462         }
11463
11464         private void ToolStripFocusLockMenuItem_CheckedChanged(object sender, EventArgs e)
11465         {
11466             _modifySettingCommon = true;
11467         }
11468
11469         private void doQuote()
11470         {
11471             //QT @id:内容
11472             //返信先情報付加
11473             if (this.ExistCurrentPost)
11474             {
11475                 if (_curPost.IsDm ||
11476                     !StatusText.Enabled) return;
11477
11478                 if (_curPost.IsProtect)
11479                 {
11480                     MessageBox.Show("Protected.");
11481                     return;
11482                 }
11483                 string rtdata = _curPost.Text;
11484                 rtdata = CreateRetweetUnofficial(rtdata, this.StatusText.Multiline);
11485
11486                 StatusText.Text = " QT @" + _curPost.ScreenName + ": " + rtdata;
11487                 if (_curPost.RetweetedId == null)
11488                 {
11489                     _reply_to_id = _curPost.StatusId;
11490                 }
11491                 else
11492                 {
11493                     _reply_to_id = _curPost.RetweetedId.Value;
11494                 }
11495                 _reply_to_name = _curPost.ScreenName;
11496
11497                 StatusText.SelectionStart = 0;
11498                 StatusText.Focus();
11499             }
11500         }
11501
11502         private void QuoteStripMenuItem_Click(object sender, EventArgs e) // Handles QuoteStripMenuItem.Click, QtOpMenuItem.Click
11503         {
11504             doQuote();
11505         }
11506
11507         private void SearchButton_Click(object sender, EventArgs e)
11508         {
11509             //公式検索
11510             Control pnl = ((Control)sender).Parent;
11511             if (pnl == null) return;
11512             string tbName = pnl.Parent.Text;
11513             TabClass tb = _statuses.Tabs[tbName];
11514             ComboBox cmb = (ComboBox)pnl.Controls["comboSearch"];
11515             ComboBox cmbLang = (ComboBox)pnl.Controls["comboLang"];
11516             cmb.Text = cmb.Text.Trim();
11517             // 検索式演算子 OR についてのみ大文字しか認識しないので強制的に大文字とする
11518             bool Quote = false;
11519             StringBuilder buf = new StringBuilder();
11520             char[] c = cmb.Text.ToCharArray();
11521             for (int cnt = 0; cnt < cmb.Text.Length; cnt++)
11522             {
11523                 if (cnt > cmb.Text.Length - 4)
11524                 {
11525                     buf.Append(cmb.Text.Substring(cnt));
11526                     break;
11527                 }
11528                 if (c[cnt] == '"')
11529                 {
11530                     Quote = !Quote;
11531                 }
11532                 else
11533                 {
11534                     if (!Quote && cmb.Text.Substring(cnt, 4).Equals(" or ", StringComparison.OrdinalIgnoreCase))
11535                     {
11536                         buf.Append(" OR ");
11537                         cnt += 3;
11538                         continue;
11539                     }
11540                 }
11541                 buf.Append(c[cnt]);
11542             }
11543             cmb.Text = buf.ToString();
11544
11545             tb.SearchWords = cmb.Text;
11546             tb.SearchLang = cmbLang.Text;
11547             if (string.IsNullOrEmpty(cmb.Text))
11548             {
11549                 ((DetailsListView)ListTab.SelectedTab.Tag).Focus();
11550                 SaveConfigsTabs();
11551                 return;
11552             }
11553             if (tb.IsQueryChanged())
11554             {
11555                 int idx = ((ComboBox)pnl.Controls["comboSearch"]).Items.IndexOf(tb.SearchWords);
11556                 if (idx > -1) ((ComboBox)pnl.Controls["comboSearch"]).Items.RemoveAt(idx);
11557                 ((ComboBox)pnl.Controls["comboSearch"]).Items.Insert(0, tb.SearchWords);
11558                 cmb.Text = tb.SearchWords;
11559                 cmb.SelectAll();
11560                 DetailsListView lst = (DetailsListView)pnl.Parent.Tag;
11561                 this.PurgeListViewItemCache();
11562                 lst.VirtualListSize = 0;
11563                 _statuses.ClearTabIds(tbName);
11564                 SaveConfigsTabs();   //検索条件の保存
11565             }
11566
11567             GetTimeline(MyCommon.WORKERTYPE.PublicSearch, 1, tbName);
11568             ((DetailsListView)ListTab.SelectedTab.Tag).Focus();
11569         }
11570
11571         private void RefreshMoreStripMenuItem_Click(object sender, EventArgs e)
11572         {
11573             //もっと前を取得
11574             DoRefreshMore();
11575         }
11576
11577         private void UndoRemoveTabMenuItem_Click(object sender, EventArgs e)
11578         {
11579             if (_statuses.RemovedTab.Count == 0)
11580             {
11581                 MessageBox.Show("There isn't removed tab.", "Undo", MessageBoxButtons.OK, MessageBoxIcon.Information);
11582                 return;
11583             }
11584             else
11585             {
11586                 TabClass tb = _statuses.RemovedTab.Pop();
11587                 if (tb.TabType == MyCommon.TabUsageType.Related)
11588                 {
11589                     var relatedTab = _statuses.GetTabByType(MyCommon.TabUsageType.Related);
11590                     if (relatedTab != null)
11591                     {
11592                         // 関連発言なら既存のタブを置き換える
11593                         tb.TabName = relatedTab.TabName;
11594                         this.ClearTab(tb.TabName, false);
11595                         _statuses.Tabs[tb.TabName] = tb;
11596                         for (int i = 0; i < ListTab.TabPages.Count; i++)
11597                         {
11598                             if (tb.TabName == ListTab.TabPages[i].Text)
11599                             {
11600                                 ListTab.SelectedIndex = i;
11601                                 ListTabSelect(ListTab.TabPages[i]);
11602                                 break;
11603                             }
11604                         }
11605                     }
11606                     else
11607                     {
11608                         const string TabName = "Related Tweets";
11609                         string renamed = TabName;
11610                         for (int i = 2; i <= 100; i++)
11611                         {
11612                             if (!_statuses.ContainsTab(renamed)) break;
11613                             renamed = TabName + i.ToString();
11614                         }
11615                         tb.TabName = renamed;
11616                         AddNewTab(renamed, false, tb.TabType, tb.ListInfo);
11617                         _statuses.Tabs.Add(renamed, tb);  // 後に
11618                         ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
11619                         ListTabSelect(ListTab.TabPages[ListTab.TabPages.Count - 1]);
11620                     }
11621                 }
11622                 else
11623                 {
11624                     string renamed = tb.TabName;
11625                     for (int i = 1; i < int.MaxValue; i++)
11626                     {
11627                         if (!_statuses.ContainsTab(renamed)) break;
11628                         renamed = tb.TabName + "(" + i.ToString() + ")";
11629                     }
11630                     tb.TabName = renamed;
11631                     _statuses.Tabs.Add(renamed, tb);  // 先に
11632                     AddNewTab(renamed, false, tb.TabType, tb.ListInfo);
11633                     ListTab.SelectedIndex = ListTab.TabPages.Count - 1;
11634                     ListTabSelect(ListTab.TabPages[ListTab.TabPages.Count - 1]);
11635                 }
11636                 SaveConfigsTabs();
11637             }
11638         }
11639
11640         private void doMoveToRTHome()
11641         {
11642             if (_curList.SelectedIndices.Count > 0)
11643             {
11644                 PostClass post = GetCurTabPost(_curList.SelectedIndices[0]);
11645                 if (post.RetweetedId != null)
11646                 {
11647                     OpenUriAsync("https://twitter.com/" + GetCurTabPost(_curList.SelectedIndices[0]).RetweetedBy);
11648                 }
11649             }
11650         }
11651
11652         private void MoveToRTHomeMenuItem_Click(object sender, EventArgs e)
11653         {
11654             doMoveToRTHome();
11655         }
11656
11657         private void IdFilterAddMenuItem_Click(object sender, EventArgs e)
11658         {
11659             string name = GetUserId();
11660             if (name != null)
11661             {
11662                 string tabName;
11663
11664                 //未選択なら処理終了
11665                 if (_curList.SelectedIndices.Count == 0) return;
11666
11667                 //タブ選択(or追加)
11668                 if (!SelectTab(out tabName)) return;
11669
11670                 bool mv = false;
11671                 bool mk = false;
11672                 MoveOrCopy(ref mv, ref mk);
11673
11674                 PostFilterRule fc = new PostFilterRule();
11675                 fc.FilterName = name;
11676                 fc.UseNameField = true;
11677                 fc.MoveMatches = mv;
11678                 fc.MarkMatches = mk;
11679                 fc.UseRegex = false;
11680                 fc.FilterByUrl = false;
11681                 _statuses.Tabs[tabName].AddFilter(fc);
11682
11683                 this.ApplyPostFilters();
11684                 SaveConfigsTabs();
11685             }
11686         }
11687
11688         private void ListManageUserContextToolStripMenuItem_Click(object sender, EventArgs e)
11689         {
11690             string user;
11691
11692             ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;
11693
11694             if (menuItem.Owner == this.ContextMenuPostBrowser)
11695             {
11696                 user = GetUserId();
11697                 if (user == null) return;
11698             }
11699             else if (this._curPost != null)
11700             {
11701                 user = this._curPost.ScreenName;
11702             }
11703             else
11704             {
11705                 return;
11706             }
11707
11708             if (TabInformations.GetInstance().SubscribableLists.Count == 0)
11709             {
11710                 string res = this.tw.GetListsApi();
11711
11712                 if (!string.IsNullOrEmpty(res))
11713                 {
11714                     MessageBox.Show("Failed to get lists. (" + res + ")");
11715                     return;
11716                 }
11717             }
11718
11719             using (MyLists listSelectForm = new MyLists(user, this.tw))
11720             {
11721                 listSelectForm.ShowDialog(this);
11722             }
11723         }
11724
11725         private void SearchControls_Enter(object sender, EventArgs e)
11726         {
11727             Control pnl = (Control)sender;
11728             foreach (Control ctl in pnl.Controls)
11729             {
11730                 ctl.TabStop = true;
11731             }
11732         }
11733
11734         private void SearchControls_Leave(object sender, EventArgs e)
11735         {
11736             Control pnl = (Control)sender;
11737             foreach (Control ctl in pnl.Controls)
11738             {
11739                 ctl.TabStop = false;
11740             }
11741         }
11742
11743         private void PublicSearchQueryMenuItem_Click(object sender, EventArgs e)
11744         {
11745             if (ListTab.SelectedTab != null)
11746             {
11747                 if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType != MyCommon.TabUsageType.PublicSearch) return;
11748                 ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus();
11749             }
11750         }
11751
11752         private void UseHashtagMenuItem_Click(object sender, EventArgs e)
11753         {
11754             Match m = Regex.Match(this._postBrowserStatusText, @"^https?://twitter.com/search\?q=%23(?<hash>.+)$");
11755             if (m.Success)
11756             {
11757                 HashMgr.SetPermanentHash("#" + Uri.UnescapeDataString(m.Result("${hash}")));
11758                 HashStripSplitButton.Text = HashMgr.UseHash;
11759                 HashToggleMenuItem.Checked = true;
11760                 HashToggleToolStripMenuItem.Checked = true;
11761                 //使用ハッシュタグとして設定
11762                 _modifySettingCommon = true;
11763             }
11764         }
11765
11766         private void StatusLabel_DoubleClick(object sender, EventArgs e)
11767         {
11768             MessageBox.Show(StatusLabel.TextHistory, "Logs", MessageBoxButtons.OK, MessageBoxIcon.None);
11769         }
11770
11771         private void HashManageMenuItem_Click(object sender, EventArgs e)
11772         {
11773             DialogResult rslt = DialogResult.Cancel;
11774             try
11775             {
11776                 rslt = HashMgr.ShowDialog();
11777             }
11778             catch (Exception)
11779             {
11780                 return;
11781             }
11782             this.TopMost = SettingDialog.AlwaysTop;
11783             if (rslt == DialogResult.Cancel) return;
11784             if (!string.IsNullOrEmpty(HashMgr.UseHash))
11785             {
11786                 HashStripSplitButton.Text = HashMgr.UseHash;
11787                 HashToggleMenuItem.Checked = true;
11788                 HashToggleToolStripMenuItem.Checked = true;
11789             }
11790             else
11791             {
11792                 HashStripSplitButton.Text = "#[-]";
11793                 HashToggleMenuItem.Checked = false;
11794                 HashToggleToolStripMenuItem.Checked = false;
11795             }
11796             //if (HashMgr.IsInsert && HashMgr.UseHash != "")
11797             //{
11798             //    int sidx = StatusText.SelectionStart;
11799             //    string hash = HashMgr.UseHash + " ";
11800             //    if (sidx > 0)
11801             //    {
11802             //        if (StatusText.Text.Substring(sidx - 1, 1) != " ")
11803             //            hash = " " + hash;
11804             //    }
11805             //    StatusText.Text = StatusText.Text.Insert(sidx, hash);
11806             //    sidx += hash.Length;
11807             //    StatusText.SelectionStart = sidx;
11808             //    StatusText.Focus();
11809             //}
11810             _modifySettingCommon = true;
11811             this.StatusText_TextChanged(null, null);
11812         }
11813
11814         private void HashToggleMenuItem_Click(object sender, EventArgs e)
11815         {
11816             HashMgr.ToggleHash();
11817             if (!string.IsNullOrEmpty(HashMgr.UseHash))
11818             {
11819                 HashStripSplitButton.Text = HashMgr.UseHash;
11820                 HashToggleMenuItem.Checked = true;
11821                 HashToggleToolStripMenuItem.Checked = true;
11822             }
11823             else
11824             {
11825                 HashStripSplitButton.Text = "#[-]";
11826                 HashToggleMenuItem.Checked = false;
11827                 HashToggleToolStripMenuItem.Checked = false;
11828             }
11829             _modifySettingCommon = true;
11830             this.StatusText_TextChanged(null, null);
11831         }
11832
11833         private void HashStripSplitButton_ButtonClick(object sender, EventArgs e)
11834         {
11835             HashToggleMenuItem_Click(null, null);
11836         }
11837
11838         private void MenuItemOperate_DropDownOpening(object sender, EventArgs e)
11839         {
11840             if (ListTab.SelectedTab == null) return;
11841             if (_statuses == null || _statuses.Tabs == null || !_statuses.Tabs.ContainsKey(ListTab.SelectedTab.Text)) return;
11842             if (!this.ExistCurrentPost)
11843             {
11844                 this.ReplyOpMenuItem.Enabled = false;
11845                 this.ReplyAllOpMenuItem.Enabled = false;
11846                 this.DmOpMenuItem.Enabled = false;
11847                 this.ShowProfMenuItem.Enabled = false;
11848                 this.ShowUserTimelineToolStripMenuItem.Enabled = false;
11849                 this.ListManageMenuItem.Enabled = false;
11850                 this.OpenFavOpMenuItem.Enabled = false;
11851                 this.CreateTabRuleOpMenuItem.Enabled = false;
11852                 this.CreateIdRuleOpMenuItem.Enabled = false;
11853                 this.ReadOpMenuItem.Enabled = false;
11854                 this.UnreadOpMenuItem.Enabled = false;
11855             }
11856             else
11857             {
11858                 this.ReplyOpMenuItem.Enabled = true;
11859                 this.ReplyAllOpMenuItem.Enabled = true;
11860                 this.DmOpMenuItem.Enabled = true;
11861                 this.ShowProfMenuItem.Enabled = true;
11862                 this.ShowUserTimelineToolStripMenuItem.Enabled = true;
11863                 this.ListManageMenuItem.Enabled = true;
11864                 this.OpenFavOpMenuItem.Enabled = true;
11865                 this.CreateTabRuleOpMenuItem.Enabled = true;
11866                 this.CreateIdRuleOpMenuItem.Enabled = true;
11867                 this.ReadOpMenuItem.Enabled = true;
11868                 this.UnreadOpMenuItem.Enabled = true;
11869             }
11870
11871             if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || _curPost.IsDm)
11872             {
11873                 this.FavOpMenuItem.Enabled = false;
11874                 this.UnFavOpMenuItem.Enabled = false;
11875                 this.OpenStatusOpMenuItem.Enabled = false;
11876                 this.OpenFavotterOpMenuItem.Enabled = false;
11877                 this.ShowRelatedStatusesMenuItem2.Enabled = false;
11878                 this.RtOpMenuItem.Enabled = false;
11879                 this.RtUnOpMenuItem.Enabled = false;
11880                 this.QtOpMenuItem.Enabled = false;
11881                 this.FavoriteRetweetMenuItem.Enabled = false;
11882                 this.FavoriteRetweetUnofficialMenuItem.Enabled = false;
11883                 if (this.ExistCurrentPost && _curPost.IsDm) this.DelOpMenuItem.Enabled = true;
11884             }
11885             else
11886             {
11887                 this.FavOpMenuItem.Enabled = true;
11888                 this.UnFavOpMenuItem.Enabled = true;
11889                 this.OpenStatusOpMenuItem.Enabled = true;
11890                 this.OpenFavotterOpMenuItem.Enabled = true;
11891                 this.ShowRelatedStatusesMenuItem2.Enabled = true;  //PublicSearchの時問題出るかも
11892
11893                 if (_curPost.IsMe)
11894                 {
11895                     this.RtOpMenuItem.Enabled = false;
11896                     this.FavoriteRetweetMenuItem.Enabled = false;
11897                     this.DelOpMenuItem.Enabled = true;
11898                 }
11899                 else
11900                 {
11901                     this.DelOpMenuItem.Enabled = false;
11902                     if (_curPost.IsProtect)
11903                     {
11904                         this.RtOpMenuItem.Enabled = false;
11905                         this.RtUnOpMenuItem.Enabled = false;
11906                         this.QtOpMenuItem.Enabled = false;
11907                         this.FavoriteRetweetMenuItem.Enabled = false;
11908                         this.FavoriteRetweetUnofficialMenuItem.Enabled = false;
11909                     }
11910                     else
11911                     {
11912                         this.RtOpMenuItem.Enabled = true;
11913                         this.RtUnOpMenuItem.Enabled = true;
11914                         this.QtOpMenuItem.Enabled = true;
11915                         this.FavoriteRetweetMenuItem.Enabled = true;
11916                         this.FavoriteRetweetUnofficialMenuItem.Enabled = true;
11917                     }
11918                 }
11919             }
11920
11921             if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType != MyCommon.TabUsageType.Favorites)
11922             {
11923                 this.RefreshPrevOpMenuItem.Enabled = true;
11924             }
11925             else
11926             {
11927                 this.RefreshPrevOpMenuItem.Enabled = false;
11928             }
11929             if (!this.ExistCurrentPost
11930                 || _curPost.InReplyToStatusId == null)
11931             {
11932                 OpenRepSourceOpMenuItem.Enabled = false;
11933             }
11934             else
11935             {
11936                 OpenRepSourceOpMenuItem.Enabled = true;
11937             }
11938             if (!this.ExistCurrentPost || string.IsNullOrEmpty(_curPost.RetweetedBy))
11939             {
11940                 OpenRterHomeMenuItem.Enabled = false;
11941             }
11942             else
11943             {
11944                 OpenRterHomeMenuItem.Enabled = true;
11945             }
11946         }
11947
11948         private void MenuItemTab_DropDownOpening(object sender, EventArgs e)
11949         {
11950             ContextMenuTabProperty_Opening(sender, null);
11951         }
11952
11953         public Twitter TwitterInstance
11954         {
11955             get { return tw; }
11956         }
11957
11958         private void SplitContainer3_SplitterMoved(object sender, SplitterEventArgs e)
11959         {
11960             if (this.WindowState == FormWindowState.Normal && !_initialLayout)
11961             {
11962                 _mySpDis3 = SplitContainer3.SplitterDistance;
11963                 _modifySettingLocal = true;
11964             }
11965         }
11966
11967         private void MenuItemEdit_DropDownOpening(object sender, EventArgs e)
11968         {
11969             if (_statuses.RemovedTab.Count == 0)
11970             {
11971                 UndoRemoveTabMenuItem.Enabled = false;
11972             }
11973             else
11974             {
11975                 UndoRemoveTabMenuItem.Enabled = true;
11976             }
11977             if (ListTab.SelectedTab != null)
11978             {
11979                 if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch)
11980                     PublicSearchQueryMenuItem.Enabled = true;
11981                 else
11982                     PublicSearchQueryMenuItem.Enabled = false;
11983             }
11984             else
11985             {
11986                 PublicSearchQueryMenuItem.Enabled = false;
11987             }
11988             if (!this.ExistCurrentPost)
11989             {
11990                 this.CopySTOTMenuItem.Enabled = false;
11991                 this.CopyURLMenuItem.Enabled = false;
11992                 this.CopyUserIdStripMenuItem.Enabled = false;
11993             }
11994             else
11995             {
11996                 this.CopySTOTMenuItem.Enabled = true;
11997                 this.CopyURLMenuItem.Enabled = true;
11998                 this.CopyUserIdStripMenuItem.Enabled = true;
11999                 if (_curPost.IsDm) this.CopyURLMenuItem.Enabled = false;
12000                 if (_curPost.IsProtect) this.CopySTOTMenuItem.Enabled = false;
12001             }
12002         }
12003
12004         private void NotifyIcon1_MouseMove(object sender, MouseEventArgs e)
12005         {
12006             SetNotifyIconText();
12007         }
12008
12009         private void UserStatusToolStripMenuItem_Click(object sender, EventArgs e)
12010         {
12011             string id = "";
12012             if (_curPost != null)
12013             {
12014                 id = _curPost.ScreenName;
12015             }
12016             ShowUserStatus(id);
12017         }
12018
12019         private class GetUserInfoArgs
12020         {
12021             public Twitter tw;
12022             public string id;
12023             public TwitterUser user;
12024         }
12025
12026         private void GetUserInfo_DoWork(object sender, DoWorkEventArgs e)
12027         {
12028             GetUserInfoArgs args = (GetUserInfoArgs)e.Argument;
12029             e.Result = args.tw.GetUserInfo(args.id, ref args.user);
12030         }
12031
12032         private void doShowUserStatus(string id, bool ShowInputDialog)
12033         {
12034             TwitterUser user = null;
12035             GetUserInfoArgs args = new GetUserInfoArgs();
12036             if (ShowInputDialog)
12037             {
12038                 using (InputTabName inputName = new InputTabName())
12039                 {
12040                     inputName.FormTitle = "Show UserStatus";
12041                     inputName.FormDescription = Properties.Resources.FRMessage1;
12042                     inputName.TabName = id;
12043                     if (inputName.ShowDialog() == DialogResult.OK &&
12044                         !string.IsNullOrEmpty(inputName.TabName.Trim()))
12045                     {
12046                         id = inputName.TabName.Trim();
12047                         args.tw = tw;
12048                         args.id = id;
12049                         args.user = user;
12050                         using (FormInfo _info = new FormInfo(this, Properties.Resources.doShowUserStatusText1,
12051                                                              GetUserInfo_DoWork,
12052                                                              null,
12053                                                              args))
12054                         {
12055                             _info.ShowDialog();
12056                             string ret = (string)_info.Result;
12057                             if (string.IsNullOrEmpty(ret))
12058                                 doShowUserStatus(args.user);
12059                             else
12060                                 MessageBox.Show(ret);
12061                         }
12062                     }
12063                 }
12064             }
12065             else
12066             {
12067                 args.tw = tw;
12068                 args.id = id;
12069                 args.user = user;
12070                 using (FormInfo _info = new FormInfo(this, Properties.Resources.doShowUserStatusText1,
12071                                                      GetUserInfo_DoWork,
12072                                                      null,
12073                                                      args))
12074                 {
12075                     _info.ShowDialog();
12076                     string ret = (string)_info.Result;
12077                     if (string.IsNullOrEmpty(ret))
12078                     {
12079                         doShowUserStatus(args.user);
12080                     }
12081                     else
12082                     {
12083                         MessageBox.Show(ret);
12084                     }
12085                 }
12086             }
12087         }
12088
12089         private async void doShowUserStatus(TwitterUser user)
12090         {
12091             using (var userDialog = new UserInfoDialog(this, this.tw))
12092             {
12093                 var showUserTask = userDialog.ShowUserAsync(user);
12094                 userDialog.ShowDialog(this);
12095
12096                 this.Activate();
12097                 this.BringToFront();
12098
12099                 // ユーザー情報の表示が完了するまで userDialog を破棄しない
12100                 await showUserTask;
12101             }
12102         }
12103
12104         private void ShowUserStatus(string id, bool ShowInputDialog)
12105         {
12106             doShowUserStatus(id, ShowInputDialog);
12107         }
12108
12109         private void ShowUserStatus(string id)
12110         {
12111             doShowUserStatus(id, true);
12112         }
12113
12114         private void FollowToolStripMenuItem_Click(object sender, EventArgs e)
12115         {
12116             if (NameLabel.Tag != null)
12117             {
12118                 string id = (string)NameLabel.Tag;
12119                 if (id != tw.Username)
12120                 {
12121                     FollowCommand(id);
12122                 }
12123             }
12124         }
12125
12126         private void UnFollowToolStripMenuItem_Click(object sender, EventArgs e)
12127         {
12128             if (NameLabel.Tag != null)
12129             {
12130                 string id = (string)NameLabel.Tag;
12131                 if (id != tw.Username)
12132                 {
12133                     RemoveCommand(id, false);
12134                 }
12135             }
12136         }
12137
12138         private void ShowFriendShipToolStripMenuItem_Click(object sender, EventArgs e)
12139         {
12140             if (NameLabel.Tag != null)
12141             {
12142                 string id = (string)NameLabel.Tag;
12143                 if (id != tw.Username)
12144                 {
12145                     ShowFriendship(id);
12146                 }
12147             }
12148         }
12149
12150         private void ShowUserStatusToolStripMenuItem_Click(object sender, EventArgs e)
12151         {
12152             if (NameLabel.Tag != null)
12153             {
12154                 string id = (string)NameLabel.Tag;
12155                 ShowUserStatus(id, false);
12156             }
12157         }
12158
12159         private void SearchPostsDetailNameToolStripMenuItem_Click(object sender, EventArgs e)
12160         {
12161             if (NameLabel.Tag != null)
12162             {
12163                 string id = (string)NameLabel.Tag;
12164                 AddNewTabForUserTimeline(id);
12165             }
12166         }
12167
12168         private void SearchAtPostsDetailNameToolStripMenuItem_Click(object sender, EventArgs e)
12169         {
12170             if (NameLabel.Tag != null)
12171             {
12172                 string id = (string)NameLabel.Tag;
12173                 AddNewTabForSearch("@" + id);
12174             }
12175         }
12176
12177         private void ShowProfileMenuItem_Click(object sender, EventArgs e)
12178         {
12179             if (_curPost != null)
12180             {
12181                 ShowUserStatus(_curPost.ScreenName, false);
12182             }
12183         }
12184
12185         private void GetRetweet_DoWork(object sender, DoWorkEventArgs e)
12186         {
12187             int counter = 0;
12188
12189             long statusid;
12190             if (_curPost.RetweetedId != null)
12191             {
12192                 statusid = _curPost.RetweetedId.Value;
12193             }
12194             else
12195             {
12196                 statusid = _curPost.StatusId;
12197             }
12198             tw.GetStatus_Retweeted_Count(statusid, ref counter);
12199
12200             e.Result = counter;
12201         }
12202
12203         private void RtCountMenuItem_Click(object sender, EventArgs e)
12204         {
12205             if (this.ExistCurrentPost)
12206             {
12207                 using (FormInfo _info = new FormInfo(this, Properties.Resources.RtCountMenuItem_ClickText1,
12208                                                      GetRetweet_DoWork))
12209                 {
12210                     int retweet_count = 0;
12211
12212                     // ダイアログ表示
12213                     _info.ShowDialog();
12214                     retweet_count = (int)_info.Result;
12215                     if (retweet_count < 0)
12216                     {
12217                         MessageBox.Show(Properties.Resources.RtCountText2);
12218                     }
12219                     else
12220                     {
12221                         MessageBox.Show(retweet_count.ToString() + Properties.Resources.RtCountText1);
12222                     }
12223                 }
12224             }
12225         }
12226
12227         private HookGlobalHotkey _hookGlobalHotkey;
12228         public TweenMain()
12229         {
12230             _hookGlobalHotkey = new HookGlobalHotkey(this);
12231
12232             // この呼び出しは、Windows フォーム デザイナで必要です。
12233             InitializeComponent();
12234
12235             // InitializeComponent() 呼び出しの後で初期化を追加します。
12236
12237             this.SettingDialog.IntervalChanged += this.TimerInterval_Changed;
12238             this.TimerTimeline.Elapsed += this.TimerTimeline_Elapsed;
12239             this._hookGlobalHotkey.HotkeyPressed += _hookGlobalHotkey_HotkeyPressed;
12240             this.gh.NotifyClicked += GrowlHelper_Callback;
12241
12242             // メイリオフォント指定時にタブの最小幅が広くなる問題の対策
12243             this.ListTab.HandleCreated += (s, e) => NativeMethods.SetMinTabWidth((TabControl)s, 40);
12244
12245             this._apiGauge = new ToolStripAPIGauge();
12246             this._apiGauge.BorderSides = ToolStripStatusLabelBorderSides.Right;
12247             this.StatusStrip1.Items.Insert(2, this._apiGauge);
12248
12249             this.ImageSelector.Visible = false;
12250             this.ImageSelector.Enabled = false;
12251             this.ImageSelector.FilePickDialog = OpenFileDialog1;
12252
12253             this.ReplaceAppName();
12254         }
12255
12256         private void _hookGlobalHotkey_HotkeyPressed(object sender, KeyEventArgs e)
12257         {
12258             if ((this.WindowState == FormWindowState.Normal || this.WindowState == FormWindowState.Maximized) && this.Visible && Form.ActiveForm == this)
12259             {
12260                 //アイコン化
12261                 this.Visible = false;
12262             }
12263             else if (Form.ActiveForm == null)
12264             {
12265                 this.Visible = true;
12266                 if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
12267                 this.Activate();
12268                 this.BringToFront();
12269                 this.StatusText.Focus();
12270             }
12271         }
12272
12273         private void UserPicture_MouseEnter(object sender, EventArgs e)
12274         {
12275             this.UserPicture.Cursor = Cursors.Hand;
12276         }
12277
12278         private void UserPicture_MouseLeave(object sender, EventArgs e)
12279         {
12280             this.UserPicture.Cursor = Cursors.Default;
12281         }
12282
12283         private void UserPicture_DoubleClick(object sender, EventArgs e)
12284         {
12285             if (NameLabel.Tag != null)
12286             {
12287                 OpenUriAsync(MyCommon.TwitterUrl + NameLabel.Tag.ToString());
12288             }
12289         }
12290
12291         private void SplitContainer2_MouseDoubleClick(object sender, MouseEventArgs e)
12292         {
12293             this.MultiLineMenuItem.PerformClick();
12294         }
12295
12296         public PostClass CurPost
12297         {
12298             get { return _curPost; }
12299         }
12300
12301         public bool IsPreviewEnable
12302         {
12303             get { return SettingDialog.PreviewEnable; }
12304         }
12305
12306 #region "画像投稿"
12307         private void ImageSelectMenuItem_Click(object sender, EventArgs e)
12308         {
12309             if (ImageSelector.Visible)
12310                 ImageSelector.EndSelection();
12311             else
12312                 ImageSelector.BeginSelection();
12313         }
12314
12315         private void SelectMedia_DragEnter(DragEventArgs e)
12316         {
12317             if (ImageSelector.HasUploadableService(((string[])e.Data.GetData(DataFormats.FileDrop, false))[0], true))
12318             {
12319                 e.Effect = DragDropEffects.Copy;
12320                 return;
12321             }
12322             e.Effect = DragDropEffects.None;
12323         }
12324
12325         private void SelectMedia_DragOver(DragEventArgs e)
12326         {
12327             //何も触らない
12328         }
12329
12330         private void SelectMedia_DragDrop(DragEventArgs e)
12331         {
12332             this.Activate();
12333             this.BringToFront();
12334             ImageSelector.BeginSelection(((string[])e.Data.GetData(DataFormats.FileDrop, false))[0]);
12335             StatusText.Focus();
12336         }
12337
12338         private void ImageSelector_BeginSelecting(object sender, EventArgs e)
12339         {
12340             TimelinePanel.Visible = false;
12341             TimelinePanel.Enabled = false;
12342         }
12343
12344         private void ImageSelector_EndSelecting(object sender, EventArgs e)
12345         {
12346             TimelinePanel.Visible = true;
12347             TimelinePanel.Enabled = true;
12348             ((DetailsListView)ListTab.SelectedTab.Tag).Focus();
12349         }
12350
12351         private void ImageSelector_FilePickDialogOpening(object sender, EventArgs e)
12352         {
12353             this.AllowDrop = false;
12354         }
12355
12356         private void ImageSelector_FilePickDialogClosed(object sender, EventArgs e)
12357         {
12358             this.AllowDrop = true;
12359         }
12360
12361         private void ImageSelector_SelectedServiceChanged(object sender, EventArgs e)
12362         {
12363             if (ImageSelector.Visible)
12364             {
12365                 _modifySettingCommon = true;
12366                 SaveConfigsAll(true);
12367
12368                 if (ImageSelector.ServiceName.Equals("Twitter"))
12369                     this.StatusText_TextChanged(null, null);
12370             }
12371         }
12372
12373         private void ImageSelector_VisibleChanged(object sender, EventArgs e)
12374         {
12375             this.StatusText_TextChanged(null, null);
12376         }
12377 #endregion
12378
12379         private void ListManageToolStripMenuItem_Click(object sender, EventArgs e)
12380         {
12381             using (ListManage form = new ListManage(tw))
12382             {
12383                 form.ShowDialog(this);
12384             }
12385         }
12386
12387         public bool ModifySettingCommon
12388         {
12389             set { _modifySettingCommon = value; }
12390         }
12391
12392         public bool ModifySettingLocal
12393         {
12394             set { _modifySettingLocal = value; }
12395         }
12396
12397         public bool ModifySettingAtId
12398         {
12399             set { _modifySettingAtId = value; }
12400         }
12401
12402         private void SourceLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
12403         {
12404             string link = (string)SourceLinkLabel.Tag;
12405             if (!string.IsNullOrEmpty(link) && e.Button == MouseButtons.Left)
12406             {
12407                 OpenUriAsync(link);
12408             }
12409         }
12410
12411         private void SourceLinkLabel_MouseEnter(object sender, EventArgs e)
12412         {
12413             string link = (string)SourceLinkLabel.Tag;
12414             if (!string.IsNullOrEmpty(link))
12415             {
12416                 StatusLabelUrl.Text = MyCommon.ConvertToReadableUrl(link);
12417             }
12418         }
12419
12420         private void SourceLinkLabel_MouseLeave(object sender, EventArgs e)
12421         {
12422             SetStatusLabelUrl();
12423         }
12424
12425         private void MenuItemCommand_DropDownOpening(object sender, EventArgs e)
12426         {
12427             if (this.ExistCurrentPost && !_curPost.IsDm)
12428                 RtCountMenuItem.Enabled = true;
12429             else
12430                 RtCountMenuItem.Enabled = false;
12431
12432             //if (SettingDialog.UrlConvertAuto && SettingDialog.ShortenTco)
12433             //    TinyUrlConvertToolStripMenuItem.Enabled = false;
12434             //else
12435             //    TinyUrlConvertToolStripMenuItem.Enabled = true;
12436         }
12437
12438         private void CopyUserIdStripMenuItem_Click(object sender, EventArgs e)
12439         {
12440             CopyUserId();
12441         }
12442
12443         private void CopyUserId()
12444         {
12445             if (_curPost == null) return;
12446             string clstr = _curPost.ScreenName;
12447             try
12448             {
12449                 Clipboard.SetDataObject(clstr, false, 5, 100);
12450             }
12451             catch (Exception ex)
12452             {
12453                 MessageBox.Show(ex.Message);
12454             }
12455         }
12456
12457         private void ShowRelatedStatusesMenuItem_Click(object sender, EventArgs e) // Handles ShowRelatedStatusesMenuItem.Click, ShowRelatedStatusesMenuItem2.Click
12458         {
12459             if (this.ExistCurrentPost && !_curPost.IsDm)
12460             {
12461                 try
12462                 {
12463                     this.OpenRelatedTab(this._curPost);
12464                 }
12465                 catch (TabException ex)
12466                 {
12467                     MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
12468                 }
12469             }
12470         }
12471
12472         /// <summary>
12473         /// 指定されたツイートに対する関連発言タブを開きます
12474         /// </summary>
12475         /// <param name="post">表示する対象となるツイート</param>
12476         /// <exception cref="TabException">名前の重複が多すぎてタブを作成できない場合</exception>
12477         private void OpenRelatedTab(PostClass post)
12478         {
12479             var tabRelated = this._statuses.GetTabByType(MyCommon.TabUsageType.Related);
12480             string tabName;
12481
12482             if (tabRelated == null)
12483             {
12484                 tabName = this._statuses.MakeTabName("Related Tweets");
12485
12486                 this.AddNewTab(tabName, false, MyCommon.TabUsageType.Related);
12487                 this._statuses.AddTab(tabName, MyCommon.TabUsageType.Related, null);
12488
12489                 tabRelated = this._statuses.GetTabByType(MyCommon.TabUsageType.Related);
12490                 tabRelated.UnreadManage = false;
12491                 tabRelated.Notify = false;
12492             }
12493             else
12494             {
12495                 tabName = tabRelated.TabName;
12496             }
12497
12498             tabRelated.RelationTargetPost = post;
12499             this.ClearTab(tabName, false);
12500
12501             for (int i = 0; i < this.ListTab.TabPages.Count; i++)
12502             {
12503                 var tabPage = this.ListTab.TabPages[i];
12504                 if (tabName == tabPage.Text)
12505                 {
12506                     this.ListTab.SelectedIndex = i;
12507                     this.ListTabSelect(tabPage);
12508                     break;
12509                 }
12510             }
12511
12512             this.GetTimeline(MyCommon.WORKERTYPE.Related, 1, tabName);
12513         }
12514
12515         private void CacheInfoMenuItem_Click(object sender, EventArgs e)
12516         {
12517             StringBuilder buf = new StringBuilder();
12518             //buf.AppendFormat("キャッシュメモリ容量         : {0}bytes({1}MB)" + Environment.NewLine, IconCache.CacheMemoryLimit, ((ImageDictionary)IconCache).CacheMemoryLimit / 1048576);
12519             //buf.AppendFormat("物理メモリ使用割合           : {0}%" + Environment.NewLine, IconCache.PhysicalMemoryLimit);
12520             buf.AppendFormat("キャッシュエントリ保持数     : {0}" + Environment.NewLine, IconCache.CacheCount);
12521             buf.AppendFormat("キャッシュエントリ破棄数     : {0}" + Environment.NewLine, IconCache.CacheRemoveCount);
12522             MessageBox.Show(buf.ToString(), "アイコンキャッシュ使用状況");
12523         }
12524
12525         private void tw_UserIdChanged()
12526         {
12527             this._modifySettingCommon = true;
12528         }
12529
12530 #region "Userstream"
12531         private bool _isActiveUserstream = false;
12532
12533         private void tw_PostDeleted(object sender, PostDeletedEventArgs e)
12534         {
12535             try
12536             {
12537                 if (InvokeRequired && !IsDisposed)
12538                 {
12539                     Invoke((Action) (() =>
12540                            {
12541                                _statuses.RemovePostReserve(e.StatusId);
12542                                if (_curTab != null && _statuses.Tabs[_curTab.Text].Contains(e.StatusId))
12543                                {
12544                                    this.PurgeListViewItemCache();
12545                                    ((DetailsListView)_curTab.Tag).Update();
12546                                    if (_curPost != null && _curPost.StatusId == e.StatusId) DispSelectedPost(true);
12547                                }
12548                            }));
12549                     return;
12550                 }
12551             }
12552             catch (ObjectDisposedException)
12553             {
12554                 return;
12555             }
12556             catch (InvalidOperationException)
12557             {
12558                 return;
12559             }
12560         }
12561
12562         private void tw_NewPostFromStream(object sender, EventArgs e)
12563         {
12564             if (SettingDialog.ReadOldPosts)
12565             {
12566                 _statuses.SetRead(); //新着時未読クリア
12567             }
12568
12569             int rsltAddCount = _statuses.DistributePosts();
12570             lock (_syncObject)
12571             {
12572                 DateTime tm = DateTime.Now;
12573                 if (_tlTimestamps.ContainsKey(tm))
12574                 {
12575                     _tlTimestamps[tm] += rsltAddCount;
12576                 }
12577                 else
12578                 {
12579                     _tlTimestamps.Add(tm, rsltAddCount);
12580                 }
12581                 DateTime oneHour = DateTime.Now.Subtract(new TimeSpan(1, 0, 0));
12582                 List<DateTime> keys = new List<DateTime>();
12583                 _tlCount = 0;
12584                 foreach (DateTime key in _tlTimestamps.Keys)
12585                 {
12586                     if (key.CompareTo(oneHour) < 0)
12587                         keys.Add(key);
12588                     else
12589                         _tlCount += _tlTimestamps[key];
12590                 }
12591                 foreach (DateTime key in keys)
12592                 {
12593                     _tlTimestamps.Remove(key);
12594                 }
12595                 keys.Clear();
12596
12597                 //Static DateTime before = Now;
12598                 //if (before.Subtract(Now).Seconds > -5) return;
12599                 //before = Now;
12600             }
12601
12602             if (this._cfgCommon.UserstreamPeriod > 0) return;
12603
12604             try
12605             {
12606                 if (InvokeRequired && !IsDisposed)
12607                 {
12608                     Invoke(new Action<bool>(RefreshTimeline), true);
12609                     return;
12610                 }
12611             }
12612             catch (ObjectDisposedException)
12613             {
12614                 return;
12615             }
12616             catch (InvalidOperationException)
12617             {
12618                 return;
12619             }
12620         }
12621
12622         private void tw_UserStreamStarted(object sender, EventArgs e)
12623         {
12624             this._isActiveUserstream = true;
12625             try
12626             {
12627                 if (InvokeRequired && !IsDisposed)
12628                 {
12629                     Invoke((Action)(() => this.tw_UserStreamStarted(sender, e)));
12630                     return;
12631                 }
12632             }
12633             catch (ObjectDisposedException)
12634             {
12635                 return;
12636             }
12637             catch (InvalidOperationException)
12638             {
12639                 return;
12640             }
12641
12642             MenuItemUserStream.Text = "&UserStream ▶";
12643             MenuItemUserStream.Enabled = true;
12644             StopToolStripMenuItem.Text = "&Stop";
12645             StopToolStripMenuItem.Enabled = true;
12646
12647             StatusLabel.Text = "UserStream Started.";
12648         }
12649
12650         private void tw_UserStreamStopped(object sender, EventArgs e)
12651         {
12652             this._isActiveUserstream = false;
12653             try
12654             {
12655                 if (InvokeRequired && !IsDisposed)
12656                 {
12657                     Invoke((Action)(() => this.tw_UserStreamStopped(sender, e)));
12658                     return;
12659                 }
12660             }
12661             catch (ObjectDisposedException)
12662             {
12663                 return;
12664             }
12665             catch (InvalidOperationException)
12666             {
12667                 return;
12668             }
12669
12670             MenuItemUserStream.Text = "&UserStream ■";
12671             MenuItemUserStream.Enabled = true;
12672             StopToolStripMenuItem.Text = "&Start";
12673             StopToolStripMenuItem.Enabled = true;
12674
12675             StatusLabel.Text = "UserStream Stopped.";
12676         }
12677
12678         private void tw_UserStreamEventArrived(object sender, UserStreamEventReceivedEventArgs e)
12679         {
12680             try
12681             {
12682                 if (InvokeRequired && !IsDisposed)
12683                 {
12684                     Invoke((Action)(() => this.tw_UserStreamEventArrived(sender, e)));
12685                     return;
12686                 }
12687             }
12688             catch (ObjectDisposedException)
12689             {
12690                 return;
12691             }
12692             catch (InvalidOperationException)
12693             {
12694                 return;
12695             }
12696             var ev = e.EventData;
12697             StatusLabel.Text = "Event: " + ev.Event;
12698             //if (ev.Event == "favorite")
12699             //{
12700             //    NotifyFavorite(ev);
12701             //}
12702             NotifyEvent(ev);
12703             if (ev.Event == "favorite" || ev.Event == "unfavorite")
12704             {
12705                 if (_curTab != null && _statuses.Tabs[_curTab.Text].Contains(ev.Id))
12706                 {
12707                     this.PurgeListViewItemCache();
12708                     ((DetailsListView)_curTab.Tag).Update();
12709                 }
12710                 if (ev.Event == "unfavorite" && ev.Username.ToLower().Equals(tw.Username.ToLower()))
12711                 {
12712                     RemovePostFromFavTab(new long[] {ev.Id});
12713                 }
12714             }
12715         }
12716
12717         private void NotifyEvent(Twitter.FormattedEvent ev)
12718         {
12719             //新着通知 
12720             if (BalloonRequired(ev))
12721             {
12722                 NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
12723                 //if (SettingDialog.DispUsername) NotifyIcon1.BalloonTipTitle = tw.Username + " - "; else NotifyIcon1.BalloonTipTitle = "";
12724                 //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [" + ev.Event.ToUpper() + "] by " + ((string)(!string.IsNullOrEmpty(ev.Username) ? ev.Username : ""), string);
12725                 StringBuilder title = new StringBuilder();
12726                 if (SettingDialog.DispUsername)
12727                 {
12728                     title.Append(tw.Username);
12729                     title.Append(" - ");
12730                 }
12731                 else
12732                 {
12733                     //title.Clear();
12734                 }
12735                 title.Append(Application.ProductName);
12736                 title.Append(" [");
12737                 title.Append(ev.Event.ToUpper());
12738                 title.Append("] by ");
12739                 if (!string.IsNullOrEmpty(ev.Username))
12740                 {
12741                     title.Append(ev.Username.ToString());
12742                 }
12743                 else
12744                 {
12745                     //title.Append("");
12746                 }
12747                 string text;
12748                 if (!string.IsNullOrEmpty(ev.Target))
12749                 {
12750                     //NotifyIcon1.BalloonTipText = ev.Target;
12751                     text = ev.Target;
12752                 }
12753                 else
12754                 {
12755                     //NotifyIcon1.BalloonTipText = " ";
12756                     text = " ";
12757                 }
12758                 //NotifyIcon1.ShowBalloonTip(500);
12759                 if (SettingDialog.IsNotifyUseGrowl)
12760                 {
12761                     gh.Notify(GrowlHelper.NotifyType.UserStreamEvent,
12762                               ev.Id.ToString(), title.ToString(), text);
12763                 }
12764                 else
12765                 {
12766                     NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
12767                     NotifyIcon1.BalloonTipTitle = title.ToString();
12768                     NotifyIcon1.BalloonTipText = text;
12769                     NotifyIcon1.ShowBalloonTip(500);
12770                 }
12771             }
12772
12773             //サウンド再生
12774             string snd = SettingDialog.EventSoundFile;
12775             if (!_initial && SettingDialog.PlaySound && !string.IsNullOrEmpty(snd))
12776             {
12777                 if ((ev.Eventtype & SettingDialog.EventNotifyFlag) != 0 && IsMyEventNotityAsEventType(ev))
12778                 {
12779                     try
12780                     {
12781                         string dir = Application.StartupPath;
12782                         if (Directory.Exists(Path.Combine(dir, "Sounds")))
12783                         {
12784                             dir = Path.Combine(dir, "Sounds");
12785                         }
12786                         using (SoundPlayer player = new SoundPlayer(Path.Combine(dir, snd)))
12787                         {
12788                             player.Play();
12789                         }
12790                     }
12791                     catch (Exception)
12792                     {
12793                     }
12794                 }
12795             }
12796         }
12797
12798         private void StopToolStripMenuItem_Click(object sender, EventArgs e)
12799         {
12800             MenuItemUserStream.Enabled = false;
12801             if (StopRefreshAllMenuItem.Checked)
12802             {
12803                 StopRefreshAllMenuItem.Checked = false;
12804                 return;
12805             }
12806             if (this._isActiveUserstream)
12807             {
12808                 tw.StopUserStream();
12809             }
12810             else
12811             {
12812                 tw.StartUserStream();
12813             }
12814         }
12815
12816         private static string inputTrack = "";
12817
12818         private void TrackToolStripMenuItem_Click(object sender, EventArgs e)
12819         {
12820             if (TrackToolStripMenuItem.Checked)
12821             {
12822                 using (InputTabName inputForm = new InputTabName())
12823                 {
12824                     inputForm.TabName = inputTrack;
12825                     inputForm.FormTitle = "Input track word";
12826                     inputForm.FormDescription = "Track word";
12827                     if (inputForm.ShowDialog() != DialogResult.OK)
12828                     {
12829                         TrackToolStripMenuItem.Checked = false;
12830                         return;
12831                     }
12832                     inputTrack = inputForm.TabName.Trim();
12833                 }
12834                 if (!inputTrack.Equals(tw.TrackWord))
12835                 {
12836                     tw.TrackWord = inputTrack;
12837                     this._modifySettingCommon = true;
12838                     TrackToolStripMenuItem.Checked = !string.IsNullOrEmpty(inputTrack);
12839                     tw.ReconnectUserStream();
12840                 }
12841             }
12842             else
12843             {
12844                 tw.TrackWord = "";
12845                 tw.ReconnectUserStream();
12846             }
12847             this._modifySettingCommon = true;
12848         }
12849
12850         private void AllrepliesToolStripMenuItem_Click(object sender, EventArgs e)
12851         {
12852             tw.AllAtReply = AllrepliesToolStripMenuItem.Checked;
12853             this._modifySettingCommon = true;
12854             tw.ReconnectUserStream();
12855         }
12856
12857         private void EventViewerMenuItem_Click(object sender, EventArgs e)
12858         {
12859             if (evtDialog == null || evtDialog.IsDisposed)
12860             {
12861                 evtDialog = null;
12862                 evtDialog = new EventViewerDialog();
12863                 evtDialog.Owner = this;
12864                 //親の中央に表示
12865                 Point pos = evtDialog.Location;
12866                 pos.X = Convert.ToInt32(this.Location.X + this.Size.Width / 2 - evtDialog.Size.Width / 2);
12867                 pos.Y = Convert.ToInt32(this.Location.Y + this.Size.Height / 2 - evtDialog.Size.Height / 2);
12868                 evtDialog.Location = pos;
12869             }
12870             evtDialog.EventSource = tw.StoredEvent;
12871             if (!evtDialog.Visible)
12872             {
12873                 evtDialog.Show(this);
12874             }
12875             else
12876             {
12877                 evtDialog.Activate();
12878             }
12879             this.TopMost = this.SettingDialog.AlwaysTop;
12880         }
12881 #endregion
12882
12883         private void TweenRestartMenuItem_Click(object sender, EventArgs e)
12884         {
12885             MyCommon._endingFlag = true;
12886             try
12887             {
12888                 this.Close();
12889                 Application.Restart();
12890             }
12891             catch (Exception)
12892             {
12893                 MessageBox.Show("Failed to restart. Please run " + Application.ProductName + " manually.");
12894             }
12895         }
12896
12897         private void OpenOwnFavedMenuItem_Click(object sender, EventArgs e)
12898         {
12899             if (!string.IsNullOrEmpty(tw.Username)) OpenUriAsync(Properties.Resources.FavstarUrl + "users/" + tw.Username + "/recent");
12900         }
12901
12902         private void OpenOwnHomeMenuItem_Click(object sender, EventArgs e)
12903         {
12904             OpenUriAsync(MyCommon.TwitterUrl + tw.Username);
12905         }
12906
12907         private async Task doTranslation(string str)
12908         {
12909             if (string.IsNullOrEmpty(str))
12910                 return;
12911
12912             var bing = new Bing();
12913             try
12914             {
12915                 var translatedText = await bing.TranslateAsync(str,
12916                     langFrom: null,
12917                     langTo: this.SettingDialog.TranslateLanguage);
12918
12919                 this.PostBrowser.DocumentText = this.createDetailHtml(translatedText);
12920             }
12921             catch (HttpRequestException e)
12922             {
12923                 this.StatusLabel.Text = "Err:" + e.Message;
12924             }
12925         }
12926
12927         private async void TranslationToolStripMenuItem_Click(object sender, EventArgs e)
12928         {
12929             if (!this.ExistCurrentPost)
12930                 return;
12931
12932             await this.doTranslation(this._curPost.TextFromApi);
12933         }
12934
12935         private async void SelectionTranslationToolStripMenuItem_Click(object sender, EventArgs e)
12936         {
12937             var text = this.WebBrowser_GetSelectionText(ref this.PostBrowser);
12938             await this.doTranslation(text);
12939         }
12940
12941         private bool ExistCurrentPost
12942         {
12943             get
12944             {
12945                 if (_curPost == null) return false;
12946                 if (_curPost.IsDeleted) return false;
12947                 return true;
12948             }
12949         }
12950
12951         private void ShowUserTimelineToolStripMenuItem_Click(object sender, EventArgs e)
12952         {
12953             ShowUserTimeline();
12954         }
12955
12956         public bool FavEventChangeUnread
12957         {
12958             get { return SettingDialog.FavEventUnread; }
12959         }
12960
12961         private string GetUserIdFromCurPostOrInput(string caption)
12962         {
12963             string id = "";
12964             if (_curPost != null)
12965             {
12966                 id = _curPost.ScreenName;
12967             }
12968             using (InputTabName inputName = new InputTabName())
12969             {
12970                 inputName.FormTitle = caption;
12971                 inputName.FormDescription = Properties.Resources.FRMessage1;
12972                 inputName.TabName = id;
12973                 if (inputName.ShowDialog() == DialogResult.OK &&
12974                     !string.IsNullOrEmpty(inputName.TabName.Trim()))
12975                 {
12976                     id = inputName.TabName.Trim();
12977                 }
12978                 else
12979                 {
12980                     id = "";
12981                 }
12982             }
12983             return id;
12984         }
12985
12986         private void UserTimelineToolStripMenuItem_Click(object sender, EventArgs e)
12987         {
12988             string id = GetUserIdFromCurPostOrInput("Show UserTimeline");
12989             if (!string.IsNullOrEmpty(id))
12990             {
12991                 AddNewTabForUserTimeline(id);
12992             }
12993         }
12994
12995         private void UserFavorareToolStripMenuItem_Click(object sender, EventArgs e)
12996         {
12997             string id = GetUserIdFromCurPostOrInput("Show Favstar");
12998             if (!string.IsNullOrEmpty(id))
12999             {
13000                 OpenUriAsync(Properties.Resources.FavstarUrl + "users/" + id + "/recent");
13001             }
13002         }
13003
13004         private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e)
13005         {
13006             if (e.Mode == Microsoft.Win32.PowerModes.Resume) osResumed = true;
13007         }
13008
13009         private void TimelineRefreshEnableChange(bool isEnable)
13010         {
13011             if (isEnable)
13012             {
13013                 tw.StartUserStream();
13014             }
13015             else
13016             {
13017                 tw.StopUserStream();
13018             }
13019             TimerTimeline.Enabled = isEnable;
13020         }
13021
13022         private void StopRefreshAllMenuItem_CheckedChanged(object sender, EventArgs e)
13023         {
13024             TimelineRefreshEnableChange(!StopRefreshAllMenuItem.Checked);
13025         }
13026
13027         private void OpenUserAppointUrl()
13028         {
13029             if (SettingDialog.UserAppointUrl != null)
13030             {
13031                 if (SettingDialog.UserAppointUrl.Contains("{ID}") || SettingDialog.UserAppointUrl.Contains("{STATUS}"))
13032                 {
13033                     if (_curPost != null)
13034                     {
13035                         string xUrl = SettingDialog.UserAppointUrl;
13036                         xUrl = xUrl.Replace("{ID}", _curPost.ScreenName);
13037                         if (_curPost.RetweetedId != null)
13038                         {
13039                             xUrl = xUrl.Replace("{STATUS}", _curPost.RetweetedId.ToString());
13040                         }
13041                         else
13042                         {
13043                             xUrl = xUrl.Replace("{STATUS}", _curPost.StatusId.ToString());
13044                         }
13045                         OpenUriAsync(xUrl);
13046                     }
13047                 }
13048                 else
13049                 {
13050                     OpenUriAsync(SettingDialog.UserAppointUrl);
13051                 }
13052             }
13053         }
13054
13055         private void OpenUserSpecifiedUrlMenuItem_Click(object sender, EventArgs e)
13056         {
13057             OpenUserAppointUrl();
13058         }
13059
13060         private void SourceCopyMenuItem_Click(object sender, EventArgs e)
13061         {
13062             string selText = SourceLinkLabel.Text;
13063             try
13064             {
13065                 Clipboard.SetDataObject(selText, false, 5, 100);
13066             }
13067             catch (Exception ex)
13068             {
13069                 MessageBox.Show(ex.Message);
13070             }
13071         }
13072
13073         private void SourceUrlCopyMenuItem_Click(object sender, EventArgs e)
13074         {
13075             string selText = (string)SourceLinkLabel.Tag;
13076             try
13077             {
13078                 Clipboard.SetDataObject(selText, false, 5, 100);
13079             }
13080             catch (Exception ex)
13081             {
13082                 MessageBox.Show(ex.Message);
13083             }
13084         }
13085
13086         private void ContextMenuSource_Opening(object sender, CancelEventArgs e)
13087         {
13088             if (_curPost == null || !ExistCurrentPost || _curPost.IsDm)
13089             {
13090                 SourceCopyMenuItem.Enabled = false;
13091                 SourceUrlCopyMenuItem.Enabled = false;
13092             }
13093             else
13094             {
13095                 SourceCopyMenuItem.Enabled = true;
13096                 SourceUrlCopyMenuItem.Enabled = true;
13097             }
13098         }
13099
13100         private void GrowlHelper_Callback(object sender, GrowlHelper.NotifyCallbackEventArgs e)
13101         {
13102             if (Form.ActiveForm == null)
13103             {
13104                 this.BeginInvoke((Action) (() =>
13105                 {
13106                     this.Visible = true;
13107                     if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
13108                     this.Activate();
13109                     this.BringToFront();
13110                     if (e.NotifyType == GrowlHelper.NotifyType.DirectMessage)
13111                     {
13112                         if (!this.GoDirectMessage(e.StatusId)) this.StatusText.Focus();
13113                     }
13114                     else
13115                     {
13116                         if (!this.GoStatus(e.StatusId)) this.StatusText.Focus();
13117                     }
13118                 }));
13119             }
13120         }
13121
13122         private void ReplaceAppName()
13123         {
13124             MatomeMenuItem.Text = MyCommon.ReplaceAppName(MatomeMenuItem.Text);
13125             AboutMenuItem.Text = MyCommon.ReplaceAppName(AboutMenuItem.Text);
13126         }
13127
13128         private void tweetThumbnail1_ThumbnailLoading(object sender, EventArgs e)
13129         {
13130             this.SplitContainer3.Panel2Collapsed = false;
13131
13132             // PreviewDistance が起動のたびに広がっていく問題の回避策
13133             // FixedPanel が Panel2 に設定された状態で Panel2 を開くと、初回だけ SplitterDistance が再計算されておかしくなるため、
13134             // None で開いた後に設定するようにする
13135             if (this.SplitContainer3.FixedPanel == FixedPanel.None)
13136                 this.SplitContainer3.FixedPanel = FixedPanel.Panel2;
13137         }
13138
13139         private void tweetThumbnail1_ThumbnailDoubleClick(object sender, ThumbnailDoubleClickEventArgs e)
13140         {
13141             this.OpenThumbnailPicture(e.Thumbnail);
13142         }
13143
13144         private void tweetThumbnail1_ThumbnailImageSearchClick(object sender, ThumbnailImageSearchEventArgs e)
13145         {
13146             this.OpenUriAsync(e.ImageUrl);
13147         }
13148
13149         private void OpenThumbnailPicture(ThumbnailInfo thumbnail)
13150         {
13151             this.OpenUriAsync(Uri.EscapeUriString(thumbnail.ImageUrl));
13152         }
13153
13154         private void TwitterApiStatusToolStripMenuItem_Click(object sender, EventArgs e)
13155         {
13156             this.OpenUriAsync(Twitter.ServiceAvailabilityStatusUrl);
13157         }
13158
13159         private void PostButton_KeyDown(object sender, KeyEventArgs e)
13160         {
13161             if (e.KeyCode == Keys.Space)
13162             {
13163                 this.JumpUnreadMenuItem_Click(null, null);
13164
13165                 e.SuppressKeyPress = true;
13166             }
13167         }
13168
13169         private void ContextMenuColumnHeader_Opening(object sender, CancelEventArgs e)
13170         {
13171             this.IconSizeNoneToolStripMenuItem.Checked = this._cfgCommon.IconSize == MyCommon.IconSizes.IconNone;
13172             this.IconSize16ToolStripMenuItem.Checked = this._cfgCommon.IconSize == MyCommon.IconSizes.Icon16;
13173             this.IconSize24ToolStripMenuItem.Checked = this._cfgCommon.IconSize == MyCommon.IconSizes.Icon24;
13174             this.IconSize48ToolStripMenuItem.Checked = this._cfgCommon.IconSize == MyCommon.IconSizes.Icon48;
13175             this.IconSize48_2ToolStripMenuItem.Checked = this._cfgCommon.IconSize == MyCommon.IconSizes.Icon48_2;
13176
13177             this.LockListSortOrderToolStripMenuItem.Checked = this._cfgCommon.SortOrderLock;
13178         }
13179
13180         private void IconSizeNoneToolStripMenuItem_Click(object sender, EventArgs e)
13181         {
13182             ChangeListViewIconSize(MyCommon.IconSizes.IconNone);
13183         }
13184
13185         private void IconSize16ToolStripMenuItem_Click(object sender, EventArgs e)
13186         {
13187             ChangeListViewIconSize(MyCommon.IconSizes.Icon16);
13188         }
13189
13190         private void IconSize24ToolStripMenuItem_Click(object sender, EventArgs e)
13191         {
13192             ChangeListViewIconSize(MyCommon.IconSizes.Icon24);
13193         }
13194
13195         private void IconSize48ToolStripMenuItem_Click(object sender, EventArgs e)
13196         {
13197             ChangeListViewIconSize(MyCommon.IconSizes.Icon48);
13198         }
13199
13200         private void IconSize48_2ToolStripMenuItem_Click(object sender, EventArgs e)
13201         {
13202             ChangeListViewIconSize(MyCommon.IconSizes.Icon48_2);
13203         }
13204
13205         private void ChangeListViewIconSize(MyCommon.IconSizes iconSize)
13206         {
13207             if (this._cfgCommon.IconSize == iconSize) return;
13208
13209             var oldIconCol = _iconCol;
13210
13211             this._cfgCommon.IconSize = iconSize;
13212             ApplyListViewIconSize(iconSize);
13213
13214             if (_iconCol != oldIconCol)
13215             {
13216                 foreach (TabPage tp in ListTab.TabPages)
13217                 {
13218                     ResetColumns((DetailsListView)tp.Tag);
13219                 }
13220             }
13221
13222             if (_curList != null) _curList.Refresh();
13223
13224             _modifySettingCommon = true;
13225         }
13226
13227         private void LockListSortToolStripMenuItem_Click(object sender, EventArgs e)
13228         {
13229             var state = this.LockListSortOrderToolStripMenuItem.Checked;
13230             if (this._cfgCommon.SortOrderLock == state) return;
13231
13232             this._cfgCommon.SortOrderLock = state;
13233
13234             _modifySettingCommon = true;
13235         }
13236     }
13237 }