OSDN Git Service

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