OSDN Git Service

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