OSDN Git Service

SouceLinkLabelのTabStopを常にFalseに
[opentween/open-tween.git] / Tween / Tween.vb
index 2b71d1e..53ee4cc 100644 (file)
@@ -132,7 +132,7 @@ Public Class TweenMain
     Private _anchorPost As PostClass
     Private _anchorFlag As Boolean        'True:関連発言移動中(関連移動以外のオペレーションをするとFalseへ。Trueだとリスト背景色をアンカー発言選択中として描画)
 
-    Private _history As New List(Of String)   '発言履歴
+    Private _history As New List(Of PostingStatus)   '発言履歴
     Private _hisIdx As Integer                  '発言履歴カレントインデックス
 
     '発言投稿時のAPI引数(発言編集時に設定。手書きreplyでは設定されない)
@@ -189,12 +189,12 @@ Public Class TweenMain
     Private SecurityManager As InternetSecurityManager
     Private Thumbnail As Thumbnail
 
-    Private _homeCounter As Integer = 0
-    Private _homeCounterAdjuster As Integer = 0
-    Private _mentionCounter As Integer = 0
-    Private _dmCounter As Integer = 0
-    Private _pubSearchCounter As Integer = 0
-    Private _listsCounter As Integer = 0
+    'Private _homeCounter As Integer = 0
+    'Private _homeCounterAdjuster As Integer = 0
+    'Private _mentionCounter As Integer = 0
+    'Private _dmCounter As Integer = 0
+    'Private _pubSearchCounter As Integer = 0
+    'Private _listsCounter As Integer = 0
 
     Private UnreadCounter As Integer = -1
     Private UnreadAtCounter As Integer = -1
@@ -277,6 +277,14 @@ Public Class TweenMain
         Public inReplyToName As String = ""
         Public imageService As String = ""      '画像投稿サービス名
         Public imagePath As String = ""
+        Public Sub New()
+
+        End Sub
+        Public Sub New(ByVal status As String, ByVal replyToId As Long, ByVal replyToName As String)
+            Me.status = status
+            Me.inReplyToId = replyToId
+            Me.inReplyToName = replyToName
+        End Sub
     End Class
 
     Private Class SpaceKeyCanceler
@@ -321,11 +329,6 @@ Public Class TweenMain
         fDialog.Dispose()
         UrlDialog.Dispose()
         _spaceKeyCanceler.Dispose()
-        If TIconDic IsNot Nothing AndAlso TIconDic.Keys.Count > 0 Then
-            For Each value As Image In TIconDic.Values
-                value.Dispose()
-            Next
-        End If
         If NIconAt IsNot Nothing Then NIconAt.Dispose()
         If NIconAtRed IsNot Nothing Then NIconAtRed.Dispose()
         If NIconAtSmoke IsNot Nothing Then NIconAtSmoke.Dispose()
@@ -364,6 +367,7 @@ Public Class TweenMain
             _bwFollower.Dispose()
         End If
         Me._apiGauge.Dispose()
+        If TIconDic IsNot Nothing Then DirectCast(TIconDic, IDisposable).Dispose()
     End Sub
 
     Private Sub LoadIcon(ByRef IconInstance As Icon, ByVal FileName As String)
@@ -523,7 +527,7 @@ Public Class TweenMain
         TabDialog.Owner = Me
         UrlDialog.Owner = Me
 
-        _history.Add("")
+        _history.Add(New PostingStatus)
         _hisIdx = 0
         _reply_to_id = 0
         _reply_to_name = ""
@@ -763,6 +767,8 @@ Public Class TweenMain
         '    _FirstRefreshFlags = True
         '    _FirstListsRefreshFlags = True
         'End If
+        SettingDialog.UserstreamStartup = _cfgCommon.UserstreamStartup
+        SettingDialog.UserstreamPeriodInt = _cfgCommon.UserstreamPeriod
 
         'ハッシュタグ関連
         HashSupl = New AtIdSupplement(_cfgCommon.HashTags, "#")
@@ -881,6 +887,10 @@ Public Class TweenMain
         ShortUrl.BitlyKey = SettingDialog.BitlyPwd
         HttpTwitter.TwitterUrl = _cfgCommon.TwitterUrl
         HttpTwitter.TwitterSearchUrl = _cfgCommon.TwitterSearchUrl
+        tw.TrackWord = _cfgCommon.TrackWord
+        TrackToolStripMenuItem.Checked = Not String.IsNullOrEmpty(tw.TrackWord)
+        tw.AllAtReply = _cfgCommon.AllAtReply
+        AllrepliesToolStripMenuItem.Checked = tw.AllAtReply
 
         Outputz.Key = SettingDialog.OutputzKey
         Outputz.Enabled = SettingDialog.OutputzEnabled
@@ -1007,7 +1017,7 @@ Public Class TweenMain
         End If
 
         'アイコンリスト作成
-        TIconDic = New Dictionary(Of String, Image)
+        TIconDic = New ImageDictionary(5000)
 
         tw.DetailIcon = TIconDic
 
@@ -1179,45 +1189,59 @@ Public Class TweenMain
 #If DEBUG Then
         Dim sw As Stopwatch = Stopwatch.StartNew()
 #End If
-        If _homeCounter > 0 Then Interlocked.Decrement(_homeCounter)
-        If _mentionCounter > 0 Then Interlocked.Decrement(_mentionCounter)
-        If _dmCounter > 0 Then Interlocked.Decrement(_dmCounter)
-        If _pubSearchCounter > 0 Then Interlocked.Decrement(_pubSearchCounter)
-        If _listsCounter > 0 Then Interlocked.Decrement(_listsCounter)
+        Static homeCounter As Integer = 0
+        Static mentionCounter As Integer = 0
+        Static dmCounter As Integer = 0
+        Static pubSearchCounter As Integer = 0
+        Static listsCounter As Integer = 0
+        Static usCounter As Integer = 0
+
+        If homeCounter > 0 Then Interlocked.Decrement(homeCounter)
+        If mentionCounter > 0 Then Interlocked.Decrement(mentionCounter)
+        If dmCounter > 0 Then Interlocked.Decrement(dmCounter)
+        If pubSearchCounter > 0 Then Interlocked.Decrement(pubSearchCounter)
+        If listsCounter > 0 Then Interlocked.Decrement(listsCounter)
+        If usCounter > 0 Then Interlocked.Decrement(usCounter)
 
         ''タイマー初期化
-        If _homeCounter <= 0 AndAlso SettingDialog.TimelinePeriodInt > 0 Then
-            Dim period As Integer
-            Interlocked.Exchange(period, 0)
-            Interlocked.Add(period, SettingDialog.TimelinePeriodInt)
-            Interlocked.Add(period, -_homeCounterAdjuster)
-            Interlocked.Exchange(_homeCounter, period)
+        If homeCounter <= 0 AndAlso SettingDialog.TimelinePeriodInt > 0 Then
+            'Dim period As Integer
+            'Interlocked.Exchange(period, 0)
+            'Interlocked.Add(period, SettingDialog.TimelinePeriodInt)
+            'Interlocked.Add(period, -_homeCounterAdjuster)
+            'Interlocked.Exchange(_homeCounter, period)
+            Interlocked.Exchange(homeCounter, SettingDialog.TimelinePeriodInt)
             GetTimeline(WORKERTYPE.Timeline, 1, 0, "")
         End If
-        If _mentionCounter <= 0 AndAlso SettingDialog.ReplyPeriodInt > 0 Then
-            Interlocked.Exchange(_mentionCounter, SettingDialog.ReplyPeriodInt)
+        If mentionCounter <= 0 AndAlso SettingDialog.ReplyPeriodInt > 0 Then
+            Interlocked.Exchange(mentionCounter, SettingDialog.ReplyPeriodInt)
             GetTimeline(WORKERTYPE.Reply, 1, 0, "")
         End If
-        If _dmCounter <= 0 AndAlso SettingDialog.DMPeriodInt > 0 Then
-            Interlocked.Exchange(_dmCounter, SettingDialog.DMPeriodInt)
+        If dmCounter <= 0 AndAlso SettingDialog.DMPeriodInt > 0 Then
+            Interlocked.Exchange(dmCounter, SettingDialog.DMPeriodInt)
             GetTimeline(WORKERTYPE.DirectMessegeRcv, 1, 0, "")
         End If
-        If _pubSearchCounter <= 0 AndAlso SettingDialog.PubSearchPeriodInt > 0 Then
-            Interlocked.Exchange(_pubSearchCounter, SettingDialog.PubSearchPeriodInt)
+        If pubSearchCounter <= 0 AndAlso SettingDialog.PubSearchPeriodInt > 0 Then
+            Interlocked.Exchange(pubSearchCounter, SettingDialog.PubSearchPeriodInt)
             GetTimeline(WORKERTYPE.PublicSearch, 1, 0, "")
         End If
-        If _listsCounter <= 0 AndAlso SettingDialog.ListsPeriodInt > 0 Then
-            Interlocked.Exchange(_listsCounter, SettingDialog.ListsPeriodInt)
+        If listsCounter <= 0 AndAlso SettingDialog.ListsPeriodInt > 0 Then
+            Interlocked.Exchange(listsCounter, SettingDialog.ListsPeriodInt)
             GetTimeline(WORKERTYPE.List, 1, 0, "")
         End If
+        If usCounter <= 0 AndAlso SettingDialog.UserstreamPeriodInt > 0 Then
+            Interlocked.Exchange(usCounter, SettingDialog.UserstreamPeriodInt)
+            RefreshTimeline(True)
+        End If
+
 #If DEBUG Then
         sw.Stop()
-        Console.WriteLine("Counter: Home {0} Reply {1} Dm {2} Search {3} Lists {4}", _homeCounter, _mentionCounter, _dmCounter, _pubSearchCounter, _listsCounter)
+        Console.WriteLine("Counter: Home {0} Reply {1} Dm {2} Search {3} Lists {4}", homeCounter, mentionCounter, dmCounter, pubSearchCounter, listsCounter)
         Console.WriteLine(sw.Elapsed)
 #End If
     End Sub
 
-    Private Sub RefreshTimeline()
+    Private Sub RefreshTimeline(ByVal isUserStream As Boolean)
         'スクロール制御準備
         Dim smode As Integer = -1    '-1:制御しない,-2:最新へ,その他:topitem使用
         Dim topId As Long = GetScrollPos(smode)
@@ -1236,7 +1260,7 @@ Public Class TweenMain
         Dim soundFile As String = ""
         Dim addCount As Integer = 0
         Dim isMention As Boolean = False
-        addCount = _statuses.SubmitUpdate(soundFile, notifyPosts, isMention)
+        addCount = _statuses.SubmitUpdate(soundFile, notifyPosts, isMention, isUserStream)
 
         If _endingFlag Then Exit Sub
 
@@ -1608,8 +1632,8 @@ Public Class TweenMain
         If StatusText.Text.Trim.Length = 0 Then
             If Not ImageSelectionPanel.Enabled Then
                 DoRefresh()
+                Exit Sub
             End If
-            Exit Sub
         End If
 
         If _curPost IsNot Nothing AndAlso StatusText.Text.Trim() = String.Format("RT @{0}: {1}", _curPost.Name, _curPost.Data) Then
@@ -1627,7 +1651,7 @@ Public Class TweenMain
             End Select
         End If
 
-        _history(_history.Count - 1) = StatusText.Text.Trim
+        _history(_history.Count - 1) = New PostingStatus(StatusText.Text.Trim, _reply_to_id, _reply_to_name)
 
         If SettingDialog.UrlConvertAuto Then
             StatusText.SelectionStart = StatusText.Text.Length
@@ -1792,7 +1816,7 @@ Public Class TweenMain
         _reply_to_id = 0
         _reply_to_name = ""
         StatusText.Text = ""
-        _history.Add("")
+        _history.Add(New PostingStatus)
         _hisIdx = _history.Count - 1
         If Not ToolStripFocusLockMenuItem.Checked Then
             DirectCast(ListTab.SelectedTab.Tag, Control).Focus()
@@ -1978,6 +2002,7 @@ Public Class TweenMain
                     Next
                 Else
                     Dim picSvc As New PictureService(tw)
+                    If String.IsNullOrEmpty(args.status.status) Then args.status.status = ""
                     ret = picSvc.Upload(args.status.imagePath, args.status.status, args.status.imageService)
                 End If
                 bw.ReportProgress(300)
@@ -2279,7 +2304,7 @@ Public Class TweenMain
            rslt.type = WORKERTYPE.FavAdd OrElse _
            rslt.type = WORKERTYPE.FavRemove OrElse _
            rslt.type = WORKERTYPE.Related Then
-            RefreshTimeline() 'リスト反映
+            RefreshTimeline(False) 'リスト反映
         End If
 
         Select Case rslt.type
@@ -2907,15 +2932,15 @@ Public Class TweenMain
 
         If result = Windows.Forms.DialogResult.OK Then
             SyncLock _syncObject
-                Try
-                    If SettingDialog.TimelinePeriodInt > 0 Then
-                        _homeCounterAdjuster = 0
-                    End If
-                Catch ex As Exception
-                    ex.Data("Instance") = "Set Timers"
-                    ex.Data("IsTerminatePermission") = False
-                    Throw
-                End Try
+                'Try
+                '    If SettingDialog.TimelinePeriodInt > 0 Then
+                '        _homeCounterAdjuster = 0
+                '    End If
+                'Catch ex As Exception
+                '    ex.Data("Instance") = "Set Timers"
+                '    ex.Data("IsTerminatePermission") = False
+                '    Throw
+                'End Try
                 'tw.CountApi = SettingDialog.CountApi
                 'tw.CountApiReply = SettingDialog.CountApiReply
                 tw.TinyUrlResolve = SettingDialog.TinyUrlResolve
@@ -3687,7 +3712,11 @@ Public Class TweenMain
 
     Public Overloads Sub ShowSuplDialog(ByVal owner As TextBox, ByVal dialog As AtIdSupplement, ByVal offset As Integer, ByVal startswith As String)
         dialog.StartsWith = startswith
-        dialog.ShowDialog()
+        If dialog.Visible Then
+            dialog.Focus()
+        Else
+            dialog.ShowDialog()
+        End If
         Me.TopMost = SettingDialog.AlwaysTop
         Dim selStart As Integer = owner.SelectionStart
         Dim fHalf As String = ""
@@ -3759,7 +3788,8 @@ Public Class TweenMain
     Private Function GetRestStatusCount(ByVal isAuto As Boolean, ByVal isAddFooter As Boolean) As Integer
         '文字数カウント
         Dim pLen As Integer = 140 - StatusText.Text.Length
-        If (isAuto AndAlso Not My.Computer.Keyboard.ShiftKeyDown) OrElse _
+        If (isAuto AndAlso Not My.Computer.Keyboard.CtrlKeyDown AndAlso SettingDialog.PostShiftEnter) OrElse _
+           (isAuto AndAlso Not My.Computer.Keyboard.ShiftKeyDown AndAlso Not SettingDialog.PostShiftEnter) OrElse _
            (Not isAuto AndAlso isAddFooter) Then
             If SettingDialog.UseRecommendStatus Then
                 pLen -= SettingDialog.RecommendStatusText.Length
@@ -3801,7 +3831,7 @@ Public Class TweenMain
             Catch ex As Exception
                 '不正な要求に対する間に合わせの応答
                 Dim sitem() As String = {"", "", "", "", "", "", "", ""}
-                e.Item = New ListViewItem(sitem, "")
+                e.Item = New ImageListViewItem(sitem, "")
             End Try
         End If
     End Sub
@@ -3832,13 +3862,13 @@ Public Class TweenMain
         If Post.IsMark Then mk += "♪"
         If Post.IsProtect Then mk += "Ю"
         If Post.InReplyToId > 0 Then mk += "⇒"
-        Dim itm As ListViewItem
+        Dim itm As ImageListViewItem
         If Post.RetweetedId = 0 Then
             Dim sitem() As String = {"", Post.Nickname, Post.Data, Post.PDate.ToString(SettingDialog.DateTimeFormat), Post.Name, "", mk, Post.Source}
-            itm = New ListViewItem(sitem, Post.ImageUrl)
+            itm = New ImageListViewItem(sitem, DirectCast(Me.TIconDic, ImageDictionary), Post.ImageUrl)
         Else
             Dim sitem() As String = {"", Post.Nickname, Post.Data, Post.PDate.ToString(SettingDialog.DateTimeFormat), Post.Name + Environment.NewLine + "(RT:" + Post.RetweetedBy + ")", "", mk, Post.Source}
-            itm = New ListViewItem(sitem, Post.ImageUrl)
+            itm = New ImageListViewItem(sitem, DirectCast(Me.TIconDic, ImageDictionary), Post.ImageUrl)
         End If
 
         Dim read As Boolean = Post.IsRead
@@ -4105,9 +4135,10 @@ Public Class TweenMain
     End Sub
 
     Private Sub DrawListViewItemIcon(ByVal e As DrawListViewSubItemEventArgs)
-        If Not String.IsNullOrEmpty(e.Item.ImageKey) AndAlso Me.TIconDic.ContainsKey(e.Item.ImageKey) Then
+        Dim item As ImageListViewItem = DirectCast(e.Item, ImageListViewItem)
+        If item.Image IsNot Nothing Then
             'e.Bounds.Leftが常に0を指すから自前で計算
-            Dim itemRect As Rectangle = e.Item.Bounds
+            Dim itemRect As Rectangle = item.Bounds
             itemRect.Width = e.Item.ListView.Columns(0).Width
 
             For Each clm As ColumnHeader In e.Item.ListView.Columns
@@ -4122,7 +4153,7 @@ Public Class TweenMain
             If iconRect.Width > 0 Then
                 e.Graphics.FillRectangle(Brushes.White, iconRect)
                 e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.High
-                e.Graphics.DrawImage(Me.TIconDic(e.Item.ImageKey), iconRect)
+                e.Graphics.DrawImage(item.Image, iconRect)
             End If
         End If
     End Sub
@@ -4485,7 +4516,7 @@ RETRY:
         If _curPost.IsDm Then
             SourceLinkLabel.Tag = Nothing
             SourceLinkLabel.Text = ""
-            SourceLinkLabel.Visible = False
+            'SourceLinkLabel.Visible = False
         Else
             Dim mc As Match = Regex.Match(_curPost.SourceHtml, "<a href=""(?<sourceurl>.+?)""")
             If mc.Success Then
@@ -4499,9 +4530,15 @@ RETRY:
             Else
                 SourceLinkLabel.Tag = Nothing
             End If
-            SourceLinkLabel.Text = "via " + _curPost.Source
-            SourceLinkLabel.Visible = True
+            If String.IsNullOrEmpty(_curPost.Source) Then
+                SourceLinkLabel.Text = ""
+                'SourceLinkLabel.Visible = False
+            Else
+                SourceLinkLabel.Text = "via " + _curPost.Source
+                'SourceLinkLabel.Visible = True
+            End If
         End If
+        SourceLinkLabel.TabStop = False
 
         If _statuses.Tabs(_curTab.Text).TabType = TabUsageType.DirectMessage AndAlso Not _curPost.IsOwl Then
             NameLabel.Text = "DM TO -> "
@@ -4515,8 +4552,18 @@ RETRY:
         If Not String.IsNullOrEmpty(_curPost.RetweetedBy) Then
             NameLabel.Text += " (RT:" + _curPost.RetweetedBy + ")"
         End If
+        If UserPicture.Image IsNot Nothing Then UserPicture.Image.Dispose()
         If Not String.IsNullOrEmpty(_curPost.ImageUrl) AndAlso TIconDic.ContainsKey(_curPost.ImageUrl) Then
             UserPicture.Image = TIconDic(_curPost.ImageUrl)
+
+            'Dim dummy As Image = DirectCast(TIconDic, ImageDictionary)(_curPost.ImageUrl, Sub(getImg)
+            '                                                                                  If img IsNot Nothing Then img.Dispose()
+            '                                                                                  If getImg Is Nothing Then Exit Sub
+            '                                                                                  img = DirectCast(getImg.Clone(), Image)
+            '                                                                                  Me.Invoke(Sub()
+            '                                                                                                Me.UserPicture.Image = img
+            '                                                                                            End Sub)
+            '                                                                              End Sub)
         Else
             UserPicture.Image = Nothing
         End If
@@ -4685,6 +4732,11 @@ RETRY:
                 e.SuppressKeyPress = True
                 SendKeys.Send("{TAB}")
             End If
+            If e.KeyCode = Keys.G Then
+                e.Handled = True
+                e.SuppressKeyPress = True
+                ShowRelatedStatusesMenuItem_Click(Nothing, Nothing)
+            End If
             ' ] in_reply_to参照元へ戻る
             If e.KeyCode = Keys.Oem4 Then
                 e.Handled = True
@@ -5477,7 +5529,9 @@ RETRY:
             If e.KeyCode = Keys.A Then
                 StatusText.SelectAll()
             ElseIf e.KeyCode = Keys.Up OrElse e.KeyCode = Keys.Down Then
-                If StatusText.Text.Trim() <> "" Then _history(_hisIdx) = StatusText.Text
+                If StatusText.Text.Trim() <> "" Then
+                    _history(_hisIdx) = New PostingStatus(StatusText.Text, _reply_to_id, _reply_to_name)
+                End If
                 If e.KeyCode = Keys.Up Then
                     _hisIdx -= 1
                     If _hisIdx < 0 Then _hisIdx = 0
@@ -5485,7 +5539,9 @@ RETRY:
                     _hisIdx += 1
                     If _hisIdx > _history.Count - 1 Then _hisIdx = _history.Count - 1
                 End If
-                StatusText.Text = _history(_hisIdx)
+                StatusText.Text = _history(_hisIdx).status
+                _reply_to_id = _history(_hisIdx).inReplyToId
+                _reply_to_name = _history(_hisIdx).inReplyToName
                 StatusText.SelectionStart = StatusText.Text.Length
                 e.Handled = True
                 e.SuppressKeyPress = True
@@ -5763,6 +5819,8 @@ RETRY:
             _cfgCommon.IsOAuth = SettingDialog.IsOAuth
             _cfgCommon.Token = tw.AccessToken
             _cfgCommon.TokenSecret = tw.AccessTokenSecret
+            _cfgCommon.UserstreamStartup = SettingDialog.UserstreamStartup
+            _cfgCommon.UserstreamPeriod = SettingDialog.UserstreamPeriodInt
             _cfgCommon.TimelinePeriod = SettingDialog.TimelinePeriodInt
             _cfgCommon.ReplyPeriod = SettingDialog.ReplyPeriodInt
             _cfgCommon.DMPeriod = SettingDialog.DMPeriodInt
@@ -5861,6 +5919,8 @@ RETRY:
             _cfgCommon.FirstCountApi = SettingDialog.FirstCountApi
             _cfgCommon.SearchCountApi = SettingDialog.SearchCountApi
             _cfgCommon.FavoritesCountApi = SettingDialog.FavoritesCountApi
+            _cfgCommon.TrackWord = tw.TrackWord
+            _cfgCommon.AllAtReply = tw.AllAtReply
 
             _cfgCommon.Save()
         End SyncLock
@@ -5981,6 +6041,9 @@ RETRY:
                      Keys.ProcessKey
                     e.IsInputKey = True
                     JumpUnreadMenuItem_Click(Nothing, Nothing)
+                Case Keys.G
+                    e.IsInputKey = True
+                    ShowRelatedStatusesMenuItem_Click(Nothing, Nothing)
                 Case Keys.F1
                     e.IsInputKey = True
                     OpenUriAsync("http://sourceforge.jp/projects/tween/wiki/FrontPage")
@@ -6548,15 +6611,34 @@ RETRY:
     End Sub
 
     Private Sub TimerRefreshIcon_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerRefreshIcon.Tick
+        '200ms
         If _colorize Then Colorize()
         If Not TimerRefreshIcon.Enabled Then Exit Sub
         Static iconCnt As Integer = 0
         Static blinkCnt As Integer = 0
         Static blink As Boolean = False
         Static idle As Boolean = False
+        'Static usCheckCnt As Integer = 0
+
+        Static iconDlListTopItem As ListViewItem = Nothing
+        If DirectCast(ListTab.SelectedTab.Tag, ListView).TopItem Is iconDlListTopItem Then
+            DirectCast(Me.TIconDic, ImageDictionary).PauseGetImage = False
+        Else
+            DirectCast(Me.TIconDic, ImageDictionary).PauseGetImage = True
+        End If
+        iconDlListTopItem = DirectCast(ListTab.SelectedTab.Tag, ListView).TopItem
 
         iconCnt += 1
         blinkCnt += 1
+        'usCheckCnt += 1
+
+        'If usCheckCnt > 300 Then    '1min
+        '    usCheckCnt = 0
+        '    If Not Me.IsReceivedUserStream Then
+        '        TraceOut("ReconnectUserStream")
+        '        tw.ReconnectUserStream()
+        '    End If
+        'End If
 
         Dim busy As Boolean = False
         For Each bw As BackgroundWorker In Me._bw
@@ -7206,7 +7288,7 @@ RETRY:
                 ttl.Append("Ver:").Append(myVer)
             Case DispTitleEnum.Post
                 If _history IsNot Nothing AndAlso _history.Count > 1 Then
-                    ttl.Append(_history(_history.Count - 2).Replace(vbCrLf, ""))
+                    ttl.Append(_history(_history.Count - 2).status.Replace(vbCrLf, ""))
                 End If
             Case DispTitleEnum.UnreadRepCount
                 ttl.AppendFormat(My.Resources.SetMainWindowTitleText1, _statuses.GetTabByType(TabUsageType.Mentions).UnreadCount + _statuses.GetTabByType(TabUsageType.DirectMessage).UnreadCount)
@@ -7261,7 +7343,7 @@ RETRY:
         If SettingDialog.TimelinePeriodInt = 0 Then
             slbl.Append(My.Resources.SetStatusLabelText2)
         Else
-            slbl.Append((SettingDialog.TimelinePeriodInt - _homeCounterAdjuster).ToString() + My.Resources.SetStatusLabelText3)
+            slbl.Append(SettingDialog.TimelinePeriodInt.ToString() + My.Resources.SetStatusLabelText3)
         End If
         Return slbl.ToString()
     End Function
@@ -7490,14 +7572,14 @@ RETRY:
                 UnFollowToolStripMenuItem.Enabled = False
                 ShowFriendShipToolStripMenuItem.Enabled = False
                 ShowUserStatusToolStripMenuItem.Enabled = True
-                SearchPostsDetailNameToolStripMenuItem.Enabled = False
+                SearchAtPostsDetailNameToolStripMenuItem.Enabled = False
                 ListManageUserContextToolStripMenuItem3.Enabled = True
             Else
                 FollowToolStripMenuItem.Enabled = True
                 UnFollowToolStripMenuItem.Enabled = True
                 ShowFriendShipToolStripMenuItem.Enabled = True
                 ShowUserStatusToolStripMenuItem.Enabled = True
-                SearchPostsDetailNameToolStripMenuItem.Enabled = True
+                SearchAtPostsDetailNameToolStripMenuItem.Enabled = True
                 ListManageUserContextToolStripMenuItem3.Enabled = True
             End If
         Else
@@ -7505,7 +7587,7 @@ RETRY:
             UnFollowToolStripMenuItem.Enabled = False
             ShowFriendShipToolStripMenuItem.Enabled = False
             ShowUserStatusToolStripMenuItem.Enabled = False
-            SearchPostsDetailNameToolStripMenuItem.Enabled = False
+            SearchAtPostsDetailNameToolStripMenuItem.Enabled = False
             ListManageUserContextToolStripMenuItem3.Enabled = False
         End If
     End Sub
@@ -8247,6 +8329,19 @@ RETRY:
             End If
         End If
         _initial = False
+        AddHandler tw.NewPostFromStream, AddressOf tw_NewPostFromStream
+        AddHandler tw.UserStreamStarted, AddressOf tw_UserStreamStarted
+        AddHandler tw.UserStreamStopped, AddressOf tw_UserStreamStopped
+        AddHandler tw.UserStreamPaused, AddressOf tw_UserStreamPaused
+        AddHandler tw.PostDeleted, AddressOf tw_PostDeleted
+
+        MenuItemUserStream.Text = "&UserStream ■"
+        MenuItemUserStream.Enabled = True
+        PauseToolStripMenuItem.Text = "&Pause"
+        PauseToolStripMenuItem.Enabled = False
+        StopToolStripMenuItem.Text = "&Start"
+        StopToolStripMenuItem.Enabled = True
+        If SettingDialog.UserstreamStartup Then tw.StartUserStream()
         TimerTimeline.Enabled = True
     End Sub
 
@@ -8375,7 +8470,7 @@ RETRY:
         End If
 
         'その他のリンク(@IDなど)を置き換える
-        status = Regex.Replace(status, "@<a target=""_self"" href=""https?://twitter.com/(#!/)?(?<url>[^""]+)""[^>]*>(?<link>[^<]+)</a>", "${url}")
+        status = Regex.Replace(status, "@<a target=""_self"" href=""https?://twitter.com/(#!/)?(?<url>[^""]+)""[^>]*>(?<link>[^<]+)</a>", "@${url}")
         'ハッシュタグ
         status = Regex.Replace(status, "<a target=""_self"" href=""(?<url>[^""]+)""[^>]*>(?<link>[^<]+)</a>", "${link}")
         '<br>タグ除去
@@ -8663,7 +8758,7 @@ RETRY:
                 result += fInfo.id + My.Resources.GetFriendshipInfo5 + System.Environment.NewLine + ff
                 If fInfo.isFollowing Then
                     If MessageBox.Show( _
-                        "フォロー解除しますか?" + System.Environment.NewLine + result, "フォロー解除確認", _
+                        My.Resources.GetFriendshipInfo7 + System.Environment.NewLine + result, My.Resources.GetFriendshipInfo8, _
                         MessageBoxButtons.YesNo, _
                         MessageBoxIcon.Question, _
                         MessageBoxDefaultButton.Button2) = Windows.Forms.DialogResult.Yes Then
@@ -8699,6 +8794,10 @@ RETRY:
         If m.Success AndAlso IsTwitterId(m.Result("${name}")) Then
             Return m.Result("${name}")
         Else
+            Dim ma As Match = Regex.Match(Me._postBrowserStatusText, "^https?://twitter.com/(#!/)?(?<name>[a-zA-Z0-9_]+)(/(status|statuses)/[0-9]+)$")
+            If ma.Success AndAlso IsTwitterId(ma.Result("${name}")) Then
+                Return ma.Result("${name}")
+            End If
             Return Nothing
         End If
     End Function
@@ -9302,6 +9401,13 @@ RETRY:
         End If
     End Sub
 
+    Private Sub SearchAtPostsDetailNameToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SearchAtPostsDetailNameToolStripMenuItem.Click, SearchAtPostsDetailToolStripMenuItem.Click
+        If NameLabel.Tag IsNot Nothing Then
+            Dim id As String = DirectCast(NameLabel.Tag, String)
+            AddNewTabForSearch("@" + id)
+        End If
+    End Sub
+
     Private Sub ShowProfileMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ShowProfileMenuItem.Click, ShowProfMenuItem.Click
         If _curPost IsNot Nothing Then
             ShowUserStatus(_curPost.Name, False)
@@ -9646,10 +9752,6 @@ RETRY:
         SetStatusLabelUrl()
     End Sub
 
-    Private Sub SourceLinkLabel_Enter(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SourceLinkLabel.Enter
-        StatusText.Focus()
-    End Sub
-
     Private Sub MenuItemCommand_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles MenuItemCommand.DropDownOpening
         If _curPost IsNot Nothing AndAlso Not _curPost.IsDm Then
             RtCountMenuItem.Enabled = True
@@ -9691,7 +9793,7 @@ RETRY:
             End If
 
             Dim tb As TabClass = _statuses.GetTabByType(TabUsageType.Related)
-            tb.RelationTargetId = _curPost.Id
+            tb.RelationTargetPost = _curPost
             Me.ClearTab(tb.TabName, False)
             For i As Integer = 0 To ListTab.TabPages.Count - 1
                 If tb.TabName = ListTab.TabPages(i).Text Then
@@ -9704,4 +9806,172 @@ RETRY:
             GetTimeline(WORKERTYPE.Related, 1, 1, tb.TabName)
         End If
     End Sub
+
+    Private Sub CacheInfoMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CacheInfoMenuItem.Click
+        Dim buf As New StringBuilder
+        buf.AppendFormat("キャッシュメモリ容量         : {0}bytes({1}MB)" + vbCrLf, DirectCast(TIconDic, ImageDictionary).CacheMemoryLimit, DirectCast(TIconDic, ImageDictionary).CacheMemoryLimit / 1048576)
+        buf.AppendFormat("物理メモリ使用割合           : {0}%" + vbCrLf, DirectCast(TIconDic, ImageDictionary).PhysicalMemoryLimit)
+        buf.AppendFormat("キャッシュエントリ保持数     : {0}" + vbCrLf, DirectCast(TIconDic, ImageDictionary).CacheCount)
+        buf.AppendFormat("キャッシュエントリ破棄数     : {0}" + vbCrLf, DirectCast(TIconDic, ImageDictionary).CacheRemoveCount)
+        MessageBox.Show(buf.ToString, "アイコンキャッシュ使用状況")
+    End Sub
+
+    Private Sub tw_PostDeleted(ByVal id As Long)
+        _statuses.RemovePostReserve(id)
+    End Sub
+
+    Private Sub tw_NewPostFromStream()
+        If SettingDialog.ReadOldPosts Then
+            _statuses.SetRead() '新着時未読クリア
+        End If
+
+        Dim rsltAddCount As Integer = _statuses.DistributePosts()
+        SyncLock _syncObject
+            Dim tm As Date = Now
+            If _tlTimestamps.ContainsKey(tm) Then
+                _tlTimestamps(tm) += rsltAddCount
+            Else
+                _tlTimestamps.Add(Now, rsltAddCount)
+            End If
+            Dim oneHour As Date = Now.Subtract(New TimeSpan(1, 0, 0))
+            Dim keys As New List(Of Date)
+            _tlCount = 0
+            For Each key As Date In _tlTimestamps.Keys
+                If key.CompareTo(oneHour) < 0 Then
+                    keys.Add(key)
+                Else
+                    _tlCount += _tlTimestamps(key)
+                End If
+            Next
+            For Each key As Date In keys
+                _tlTimestamps.Remove(key)
+            Next
+            keys.Clear()
+
+            'Static before As DateTime = Now
+            'If before.Subtract(Now).Seconds > -5 Then Exit Sub
+            'before = Now
+        End SyncLock
+
+        If SettingDialog.UserstreamPeriodInt > 0 Then Exit Sub
+
+        Try
+            If InvokeRequired AndAlso Not IsDisposed Then
+                Invoke(New Action(Of Boolean)(AddressOf RefreshTimeline), True)
+                Exit Sub
+            End If
+        Catch ex As ObjectDisposedException
+            Exit Sub
+        End Try
+    End Sub
+    Private Sub tw_UserStreamStarted()
+        If InvokeRequired Then
+            Invoke(New MethodInvoker(AddressOf tw_UserStreamStarted))
+            Exit Sub
+        End If
+
+        MenuItemUserStream.Text = "&UserStream ▶"
+        MenuItemUserStream.Enabled = True
+        PauseToolStripMenuItem.Text = "&Pause"
+        PauseToolStripMenuItem.Enabled = True
+        StopToolStripMenuItem.Text = "&Stop"
+        StopToolStripMenuItem.Enabled = True
+
+        StatusLabel.Text = "UserStream Started."
+    End Sub
+
+    Private Sub tw_UserStreamStopped()
+        If InvokeRequired Then
+            Invoke(New MethodInvoker(AddressOf tw_UserStreamStopped))
+            Exit Sub
+        End If
+
+        MenuItemUserStream.Text = "&UserStream ■"
+        MenuItemUserStream.Enabled = True
+        PauseToolStripMenuItem.Text = "&Pause"
+        PauseToolStripMenuItem.Enabled = False
+        StopToolStripMenuItem.Text = "&Start"
+        StopToolStripMenuItem.Enabled = True
+
+        StatusLabel.Text = "UserStream Stopped."
+    End Sub
+
+    Private Sub tw_UserStreamPaused()
+        If InvokeRequired Then
+            Invoke(New MethodInvoker(AddressOf tw_UserStreamPaused))
+            Exit Sub
+        End If
+
+        MenuItemUserStream.Text = "&UserStream ||"
+        MenuItemUserStream.Enabled = True
+        PauseToolStripMenuItem.Text = "&Resume"
+        PauseToolStripMenuItem.Enabled = True
+        StopToolStripMenuItem.Text = "&Stop"
+        StopToolStripMenuItem.Enabled = True
+
+        StatusLabel.Text = "UserStream Paused."
+    End Sub
+
+    'Private ReadOnly Property IsReceivedUserStream As Boolean
+    '    Get
+    '        Static lastTime As DateTime
+    '        Dim changed As Boolean = (lastTime.CompareTo(tw.LastReceivedUserStream) < 0)
+    '        lastTime = tw.LastReceivedUserStream
+    '        Return changed
+    '    End Get
+    'End Property
+
+    Private Sub PauseToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PauseToolStripMenuItem.Click
+        MenuItemUserStream.Enabled = False
+        tw.PauseUserStream()
+    End Sub
+
+    Private Sub StopToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopToolStripMenuItem.Click
+        MenuItemUserStream.Enabled = False
+        tw.StartUserStream()
+    End Sub
+
+    Private Sub TrackToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackToolStripMenuItem.Click
+        Static inputTrack As String = ""
+        If TrackToolStripMenuItem.Checked Then
+            Using inputForm As New InputTabName
+                inputForm.TabName = inputTrack
+                inputForm.FormTitle = "Input track word"
+                inputForm.FormDescription = "Track word"
+                If inputForm.ShowDialog() <> Windows.Forms.DialogResult.OK Then
+                    TrackToolStripMenuItem.Checked = False
+                    Exit Sub
+                End If
+                inputTrack = inputForm.TabName.Trim()
+            End Using
+            If Not inputTrack.Equals(tw.TrackWord) Then
+                tw.TrackWord = inputTrack
+                Me._modifySettingCommon = True
+                TrackToolStripMenuItem.Checked = Not String.IsNullOrEmpty(inputTrack)
+                tw.ReconnectUserStream()
+            End If
+        Else
+            tw.TrackWord = ""
+            tw.ReconnectUserStream()
+        End If
+    End Sub
+
+    Private Sub AllrepliesToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AllrepliesToolStripMenuItem.Click
+        tw.AllAtReply = AllrepliesToolStripMenuItem.Checked
+        Me._modifySettingCommon = True
+        tw.ReconnectUserStream()
+    End Sub
+
+    Private Sub TweenRestartMenuItem_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TweenRestartMenuItem.Click
+        _endingFlag = True
+        Application.Restart()
+    End Sub
+
+    Private Sub OpenOwnFavedMenuItem_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OpenOwnFavedMenuItem.Click
+        If Not tw.Username = "" Then OpenUriAsync(My.Resources.FavstarUrl + "users/" + tw.Username + "/recent")
+    End Sub
+
+    Private Sub OpenOwnHomeMenuItem_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles OpenOwnHomeMenuItem.Click
+        OpenUriAsync("http://twitter.com/" + tw.Username)
+    End Sub
 End Class