X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=OpenTween%2FTween.cs;h=860a2af863b65d9f97a7c0f805a80c1f8d44220f;hb=91c59331;hp=81a3951d7c1c335405632ebdcd83a24fd738bb8b;hpb=cbeb194f82a1af76c266f70f2f0db2c79463da1d;p=opentween%2Fopen-tween.git diff --git a/OpenTween/Tween.cs b/OpenTween/Tween.cs index 81a3951d..860a2af8 100644 --- a/OpenTween/Tween.cs +++ b/OpenTween/Tween.cs @@ -117,7 +117,7 @@ namespace OpenTween private Twitter tw; //Growl呼び出し部 - private GrowlHelper gh = new GrowlHelper(Application.ProductName); + private GrowlHelper gh = new GrowlHelper(ApplicationSettings.ApplicationName); //サブ画面インスタンス internal SearchWordDialog SearchDialog = new SearchWordDialog(); //検索画面インスタンス @@ -165,15 +165,15 @@ namespace OpenTween private PostClass _anchorPost; private bool _anchorFlag; //true:関連発言移動中(関連移動以外のオペレーションをするとfalseへ。trueだとリスト背景色をアンカー発言選択中として描画) - private List _history = new List(); //発言履歴 + private List _history = new List(); //発言履歴 private int _hisIdx; //発言履歴カレントインデックス //発言投稿時のAPI引数(発言編集時に設定。手書きreplyでは設定されない) - private Tuple inReplyTo = null; // リプライ先のステータスID・スクリーン名 + private (long StatusId, string ScreenName)? inReplyTo = null; // リプライ先のステータスID・スクリーン名 //時速表示用 - private List _postTimestamps = new List(); - private List _favTimestamps = new List(); + private List _postTimestamps = new List(); + private List _favTimestamps = new List(); // 以下DrawItem関連 private SolidBrush _brsHighLight = new SolidBrush(Color.FromKnownColor(KnownColor.Highlight)); @@ -250,10 +250,6 @@ namespace OpenTween } } - private TabPage _curTab; - private int _curItemIndex; - private DetailsListView _curList; - private PostClass _curPost; private bool _isColumnChanged = false; private const int MAX_WORKER_THREADS = 20; @@ -274,8 +270,11 @@ namespace OpenTween private bool _colorize = false; private System.Timers.Timer TimerTimeline = new System.Timers.Timer(); + private ThrottlingTimer RefreshThrottlingTimer; private string recommendedStatusFooter; + private bool urlMultibyteSplit = false; + private bool preventSmsCommand = true; //URL短縮のUndo用 private struct urlUndo @@ -290,9 +289,9 @@ namespace OpenTween { public long OriginalId; public long InReplyToId; - public TabPage OriginalTab; + public TabModel OriginalTab; - public ReplyChain(long originalId, long inReplyToId, TabPage originalTab) + public ReplyChain(long originalId, long inReplyToId, TabModel originalTab) { this.OriginalId = originalId; this.InReplyToId = inReplyToId; @@ -301,7 +300,22 @@ namespace OpenTween } private Stack replyChains; //[, ]でのリプライ移動の履歴 - private Stack> selectPostChains = new Stack>(); //ポスト選択履歴 + private Stack<(TabModel, PostClass)> selectPostChains = new Stack<(TabModel, PostClass)>(); //ポスト選択履歴 + + public TabModel CurrentTab + => this._statuses.SelectedTab; + + public string CurrentTabName + => this._statuses.SelectedTabName; + + public TabPage CurrentTabPage + => this.ListTab.TabPages.Cast().First(x => x.Text == this.CurrentTabName); + + public DetailsListView CurrentListView + => (DetailsListView)this.CurrentTabPage.Tag; + + public PostClass CurrentPost + => this.CurrentTab.SelectedPost; //検索処理タイプ internal enum SEARCHTYPE @@ -311,21 +325,19 @@ namespace OpenTween PrevSearch, } - private class PostingStatus + private class StatusTextHistory { public string status = ""; - public long? inReplyToId = null; - public string inReplyToName = null; + public (long StatusId, string ScreenName)? inReplyTo = null; public string imageService = ""; //画像投稿サービス名 public IMediaItem[] mediaItems = null; - public PostingStatus() + public StatusTextHistory() { } - public PostingStatus(string status, long? replyToId, string replyToName) + public StatusTextHistory(string status, (long StatusId, string ScreenName)? inReplyTo) { this.status = status; - this.inReplyToId = replyToId; - this.inReplyToName = replyToName; + this.inReplyTo = inReplyTo; } } @@ -400,6 +412,7 @@ namespace OpenTween // 終了時にRemoveHandlerしておかないとメモリリークする // http://msdn.microsoft.com/ja-jp/library/microsoft.win32.systemevents.powermodechanged.aspx Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; + Microsoft.Win32.SystemEvents.TimeChanged -= SystemEvents_TimeChanged; this.disposed = true; } @@ -523,7 +536,7 @@ namespace OpenTween else { var idx = 0; - foreach (var curListColumn in this._curList.Columns.Cast()) + foreach (var curListColumn in this.CurrentListView.Columns.Cast()) { columns[idx].Width = curListColumn.Width; columns[idx].DisplayIndex = curListColumn.DisplayIndex; @@ -576,7 +589,7 @@ namespace OpenTween else { var idx = 0; - foreach (var curListColumn in this._curList.Columns.Cast()) + foreach (var curListColumn in this.CurrentListView.Columns.Cast()) { columns[idx].Width = curListColumn.Width; columns[idx].DisplayIndex = curListColumn.DisplayIndex; @@ -692,7 +705,6 @@ namespace OpenTween //Win32Api.SetProxy(HttpConnection.ProxyType.Specified, "127.0.0.1", 8080, "user", "pass") - MyCommon.TwitterApiInfo.AccessLimitUpdated += TwitterApiStatus_AccessLimitUpdated; Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; Regex.CacheSize = 100; @@ -811,6 +823,7 @@ namespace OpenTween tw.AllAtReply = SettingManager.Common.AllAtReply; AllrepliesToolStripMenuItem.Checked = tw.AllAtReply; ShortUrl.Instance.DisableExpanding = !SettingManager.Common.TinyUrlResolve; + ShortUrl.Instance.BitlyAccessToken = SettingManager.Common.BitlyAccessToken; ShortUrl.Instance.BitlyId = SettingManager.Common.BilyUser; ShortUrl.Instance.BitlyKey = SettingManager.Common.BitlyPwd; @@ -823,7 +836,7 @@ namespace OpenTween catch (WebApiException ex) { MessageBox.Show(this, string.Format(Properties.Resources.StartupAuthError_Text, ex.Message), - Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Warning); } //サムネイル関連の初期化 @@ -898,7 +911,7 @@ namespace OpenTween //Regex statregex = new Regex("^0*"); this.recommendedStatusFooter = " [TWNv" + Regex.Replace(MyCommon.FileVersion.Replace(".", ""), "^0*", "") + "]"; - _history.Add(new PostingStatus()); + _history.Add(new StatusTextHistory()); _hisIdx = 0; this.inReplyTo = null; @@ -917,9 +930,6 @@ namespace OpenTween this.PlaySoundMenuItem.Checked = SettingManager.Common.PlaySound; this.PlaySoundFileMenuItem.Checked = SettingManager.Common.PlaySound; - this.IdeographicSpaceToSpaceToolStripMenuItem.Checked = SettingManager.Common.WideSpaceConvert; - this.ToolStripFocusLockMenuItem.Checked = SettingManager.Common.FocusLockToStatusText; - //ウィンドウ設定 this.ClientSize = ScaleBy(configScaleFactor, SettingManager.Local.FormSize); _mySize = this.ClientSize; // サイズ保持(最小化・最大化されたまま終了した場合の対応用) @@ -927,8 +937,7 @@ namespace OpenTween //タイトルバー領域 if (this.WindowState != FormWindowState.Minimized) { - this.DesktopLocation = SettingManager.Local.FormLocation; - Rectangle tbarRect = new Rectangle(this.Location, new Size(_mySize.Width, SystemInformation.CaptionHeight)); + Rectangle tbarRect = new Rectangle(this._myLoc, new Size(_mySize.Width, SystemInformation.CaptionHeight)); bool outOfScreen = true; if (Screen.AllScreens.Length == 1) //ハングするとの報告 { @@ -940,12 +949,11 @@ namespace OpenTween break; } } + if (outOfScreen) - { - this.DesktopLocation = new Point(0, 0); - _myLoc = this.DesktopLocation; - } + this._myLoc = new Point(0, 0); } + this.DesktopLocation = this._myLoc; } this.TopMost = SettingManager.Common.AlwaysTop; _mySpDis = ScaleBy(configScaleFactor.Height, SettingManager.Local.SplitterDistance); @@ -960,7 +968,6 @@ namespace OpenTween { _mySpDis3 = ScaleBy(configScaleFactor.Width, SettingManager.Local.PreviewDistance); } - MultiLineMenuItem.Checked = SettingManager.Local.StatusMultiline; //this.Tween_ClientSizeChanged(this, null); this.PlaySoundMenuItem.Checked = SettingManager.Common.PlaySound; this.PlaySoundFileMenuItem.Checked = SettingManager.Common.PlaySound; @@ -989,7 +996,7 @@ namespace OpenTween StatusLabel.AutoToolTip = false; StatusLabel.ToolTipText = ""; //文字カウンタ初期化 - lblLen.Text = this.GetRestStatusCount(this.FormatStatusText("")).ToString(); + lblLen.Text = this.GetRestStatusCount(this.FormatStatusTextExtended("")).ToString(); this.JumpReadOpMenuItem.ShortcutKeyDisplayString = "Space"; CopySTOTMenuItem.ShortcutKeyDisplayString = "Ctrl+C"; @@ -1050,22 +1057,19 @@ namespace OpenTween if (this._statuses.GetTabByType() == null) this._statuses.AddTab(new FavoritesTabModel()); - if (this._statuses.GetTabByType() == null) + if (this._statuses.MuteTab == null) this._statuses.AddTab(new MuteTabModel()); - foreach (var tab in _statuses.Tabs.Values) + foreach (var tab in _statuses.Tabs) { - // ミュートタブは表示しない - if (tab.TabType == MyCommon.TabUsageType.Mute) - continue; - if (!AddNewTab(tab, startup: true)) throw new TabException(Properties.Resources.TweenMain_LoadText1); } - _curTab = ListTab.SelectedTab; - _curItemIndex = -1; - _curList = (DetailsListView)_curTab.Tag; + this._statuses.SelectTab(this.ListTab.SelectedTab.Text); + + MyCommon.TwitterApiInfo.AccessLimitUpdated += TwitterApiStatus_AccessLimitUpdated; + Microsoft.Win32.SystemEvents.TimeChanged += SystemEvents_TimeChanged; if (SettingManager.Common.TabIconDisp) { @@ -1108,6 +1112,11 @@ namespace OpenTween } //タイマー設定 + + var streamingRefreshInterval = TimeSpan.FromSeconds(SettingManager.Common.UserstreamPeriod); + this.RefreshThrottlingTimer = new ThrottlingTimer(streamingRefreshInterval, + () => this.InvokeAsync(() => this.RefreshTimeline())); + TimerTimeline.AutoReset = true; TimerTimeline.SynchronizingObject = this; //Recent取得間隔 @@ -1123,7 +1132,7 @@ namespace OpenTween foreach (var ua in SettingManager.Common.UserAccounts) { - if (ua.UserId == 0 && ua.Username.ToLowerInvariant() == tw.Username.ToLowerInvariant()) + if (ua.UserId == 0 && ua.Username.Equals(tw.Username, StringComparison.InvariantCultureIgnoreCase)) { ua.UserId = tw.UserId; break; @@ -1269,6 +1278,15 @@ namespace OpenTween private void TimerInterval_Changed(object sender, IntervalChangedEventArgs e) //Handles SettingDialog.IntervalChanged { if (!TimerTimeline.Enabled) return; + + if (e.UserStream) + { + var interval = TimeSpan.FromSeconds(SettingManager.Common.UserstreamPeriod); + var newTimer = new ThrottlingTimer(interval, () => this.InvokeAsync(() => this.RefreshTimeline())); + var oldTimer = Interlocked.Exchange(ref this.RefreshThrottlingTimer, newTimer); + oldTimer.Dispose(); + } + ResetTimers = e; } @@ -1280,7 +1298,6 @@ namespace OpenTween private static int pubSearchCounter = 0; private static int userTimelineCounter = 0; private static int listsCounter = 0; - private static int usCounter = 0; private static int ResumeWait = 0; private static int refreshFollowers = 0; @@ -1292,7 +1309,6 @@ namespace OpenTween if (pubSearchCounter > 0) Interlocked.Decrement(ref pubSearchCounter); if (userTimelineCounter > 0) Interlocked.Decrement(ref userTimelineCounter); if (listsCounter > 0) Interlocked.Decrement(ref listsCounter); - if (usCounter > 0) Interlocked.Decrement(ref usCounter); Interlocked.Increment(ref refreshFollowers); var refreshTasks = new List(); @@ -1302,51 +1318,44 @@ namespace OpenTween { Interlocked.Exchange(ref homeCounter, SettingManager.Common.TimelinePeriod); if (!tw.IsUserstreamDataReceived && !ResetTimers.Timeline) - refreshTasks.Add(this.GetHomeTimelineAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.Timeline = false; } if (ResetTimers.Reply || mentionCounter <= 0 && SettingManager.Common.ReplyPeriod > 0) { Interlocked.Exchange(ref mentionCounter, SettingManager.Common.ReplyPeriod); if (!tw.IsUserstreamDataReceived && !ResetTimers.Reply) - refreshTasks.Add(this.GetReplyAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.Reply = false; } if (ResetTimers.DirectMessage || dmCounter <= 0 && SettingManager.Common.DMPeriod > 0) { Interlocked.Exchange(ref dmCounter, SettingManager.Common.DMPeriod); if (!tw.IsUserstreamDataReceived && !ResetTimers.DirectMessage) - refreshTasks.Add(this.GetDirectMessagesAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.DirectMessage = false; } if (ResetTimers.PublicSearch || pubSearchCounter <= 0 && SettingManager.Common.PubSearchPeriod > 0) { Interlocked.Exchange(ref pubSearchCounter, SettingManager.Common.PubSearchPeriod); if (!ResetTimers.PublicSearch) - refreshTasks.Add(this.GetPublicSearchAllAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.PublicSearch = false; } if (ResetTimers.UserTimeline || userTimelineCounter <= 0 && SettingManager.Common.UserTimelinePeriod > 0) { Interlocked.Exchange(ref userTimelineCounter, SettingManager.Common.UserTimelinePeriod); if (!ResetTimers.UserTimeline) - refreshTasks.Add(this.GetUserTimelineAllAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.UserTimeline = false; } if (ResetTimers.Lists || listsCounter <= 0 && SettingManager.Common.ListsPeriod > 0) { Interlocked.Exchange(ref listsCounter, SettingManager.Common.ListsPeriod); if (!ResetTimers.Lists) - refreshTasks.Add(this.GetListTimelineAllAsync()); + refreshTasks.Add(this.RefreshTabAsync()); ResetTimers.Lists = false; } - if (ResetTimers.UserStream || usCounter <= 0 && SettingManager.Common.UserstreamPeriod > 0) - { - Interlocked.Exchange(ref usCounter, SettingManager.Common.UserstreamPeriod); - if (this.tw.UserStreamActive) - this.RefreshTimeline(); - ResetTimers.UserStream = false; - } if (refreshFollowers > 6 * 3600) { Interlocked.Exchange(ref refreshFollowers, 0); @@ -1366,12 +1375,12 @@ namespace OpenTween Interlocked.Exchange(ref ResumeWait, 0); refreshTasks.AddRange(new[] { - this.GetHomeTimelineAsync(), - this.GetReplyAsync(), - this.GetDirectMessagesAsync(), - this.GetPublicSearchAllAsync(), - this.GetUserTimelineAllAsync(), - this.GetListTimelineAllAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), this.doGetFollowersMenu(), this.RefreshTwitterConfigurationAsync(), }); @@ -1383,10 +1392,11 @@ namespace OpenTween private void RefreshTimeline() { - var curTabModel = this._statuses.Tabs[this._curTab.Text]; + var curTabModel = this.CurrentTab; + var curListView = this.CurrentListView; // 現在表示中のタブのスクロール位置を退避 - var curListScroll = this.SaveListViewScroll(this._curList, curTabModel); + var curListScroll = this.SaveListViewScroll(curListView, curTabModel); // 各タブのリスト上の選択位置などを退避 var listSelections = this.SaveListViewSelection(); @@ -1408,7 +1418,7 @@ namespace OpenTween { using (ControlTransaction.Update(listView)) { - if (listView == this._curList) + if (listView == curListView) this.PurgeListViewItemCache(); try @@ -1447,7 +1457,7 @@ namespace OpenTween } // スクロール位置を復元 - this.RestoreListViewScroll(this._curList, curTabModel, curListScroll); + this.RestoreListViewScroll(curListView, curTabModel, curListScroll); //新着通知 NotifyNewPosts(notifyPosts, soundFile, addCount, newMentionOrDm); @@ -1573,7 +1583,7 @@ namespace OpenTween { return new ListViewSelection { - SelectedStatusIds = new long[0], + SelectedStatusIds = Array.Empty(), SelectionMarkStatusId = null, FocusedStatusId = null, }; @@ -1581,21 +1591,12 @@ namespace OpenTween return new ListViewSelection { - SelectedStatusIds = this.GetSelectedStatusIds(listView, tab), + SelectedStatusIds = tab.SelectedStatusIds, FocusedStatusId = this.GetFocusedStatusId(listView, tab), SelectionMarkStatusId = this.GetSelectionMarkStatusId(listView, tab), }; } - private long[] GetSelectedStatusIds(DetailsListView listView, TabModel tab) - { - var selectedIndices = listView.SelectedIndices; - if (selectedIndices.Count > 0 && selectedIndices.Count < 61) - return tab.GetStatusIdAt(selectedIndices.Cast()); - else - return null; - } - private long? GetFocusedStatusId(DetailsListView listView, TabModel tab) { var index = listView.FocusedItem?.Index ?? -1; @@ -1629,7 +1630,18 @@ namespace OpenTween case ScrollLockMode.FixedToItem: var topIndex = listScroll.TopItemStatusId != null ? tab.IndexOf(listScroll.TopItemStatusId.Value) : -1; if (topIndex != -1) - listView.TopItem = listView.Items[topIndex]; + { + var topItem = listView.Items[topIndex]; + try + { + listView.TopItem = topItem; + } + catch (NullReferenceException) + { + listView.EnsureVisible(listView.VirtualListSize - 1); + listView.EnsureVisible(topIndex); + } + } break; case ScrollLockMode.None: default: @@ -1660,8 +1672,10 @@ namespace OpenTween private bool BalloonRequired() { - Twitter.FormattedEvent ev = new Twitter.FormattedEvent(); - ev.Eventtype = MyCommon.EVENTTYPE.None; + var ev = new Twitter.FormattedEvent + { + Eventtype = MyCommon.EVENTTYPE.None, + }; return BalloonRequired(ev); } @@ -1772,7 +1786,7 @@ namespace OpenTween { //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [DM] " + Properties.Resources.RefreshDirectMessageText1 + " " + addCount.ToString() + Properties.Resources.RefreshDirectMessageText2; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" [DM] "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); nt = GrowlHelper.NotifyType.DirectMessage; @@ -1781,7 +1795,7 @@ namespace OpenTween { //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [Reply!] " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" [Reply!] "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); nt = GrowlHelper.NotifyType.Reply; @@ -1790,7 +1804,7 @@ namespace OpenTween { //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); nt = GrowlHelper.NotifyType.Notify; @@ -1799,7 +1813,7 @@ namespace OpenTween if (string.IsNullOrEmpty(bText)) return; var image = this.IconCache.TryGetFromCache(post.ImageUrl); - gh.Notify(nt, post.StatusId.ToString(), title.ToString(), bText, image == null ? null : image.Image, post.ImageUrl); + gh.Notify(nt, post.StatusId.ToString(), title.ToString(), bText, image?.Image, post.ImageUrl); } } else @@ -1841,7 +1855,7 @@ namespace OpenTween //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [DM] " + Properties.Resources.RefreshDirectMessageText1 + " " + addCount.ToString() + Properties.Resources.RefreshDirectMessageText2; ntIcon = ToolTipIcon.Warning; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" [DM] "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); } @@ -1850,7 +1864,7 @@ namespace OpenTween //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " [Reply!] " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2; ntIcon = ToolTipIcon.Warning; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" [Reply!] "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); } @@ -1859,7 +1873,7 @@ namespace OpenTween //NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info; //NotifyIcon1.BalloonTipTitle += Application.ProductName + " " + Properties.Resources.RefreshTimelineText1 + " " + addCount.ToString() + Properties.Resources.RefreshTimelineText2; ntIcon = ToolTipIcon.Info; - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" "); title.AppendFormat(Properties.Resources.RefreshTimeline_NotifyText, addCount); } @@ -1904,25 +1918,25 @@ namespace OpenTween private void MyList_SelectedIndexChanged(object sender, EventArgs e) { - if (_curList == null || !_curList.Equals(sender) || _curList.SelectedIndices.Count != 1) return; + var listView = this.CurrentListView; + if (listView != sender) + return; - _curItemIndex = _curList.SelectedIndices[0]; - if (_curItemIndex > _curList.VirtualListSize - 1) return; + var indices = listView.SelectedIndices.Cast().ToArray(); + this.CurrentTab.SelectPosts(indices); - try - { - this._curPost = GetCurTabPost(_curItemIndex); - } - catch (ArgumentException) - { + if (listView.SelectedIndices.Count != 1) return; - } + + var index = listView.SelectedIndices[0]; + if (index > listView.VirtualListSize - 1) return; this.PushSelectPostChain(); - this._statuses.SetReadAllTab(_curPost.StatusId, read: true); + var post = this.CurrentPost; + this._statuses.SetReadAllTab(post.StatusId, read: true); //キャッシュの書き換え - ChangeCacheStyleRead(true, _curItemIndex); //既読へ(フォント、文字色) + ChangeCacheStyleRead(true, index); // 既読へ(フォント、文字色) ColorizeList(); _colorize = true; @@ -1930,7 +1944,7 @@ namespace OpenTween private void ChangeCacheStyleRead(bool Read, int Index) { - var tabInfo = _statuses.Tabs[_curTab.Text]; + var tabInfo = this.CurrentTab; //Read:true=既読 false=未読 //未読管理していなかったら既読として扱う if (!tabInfo.UnreadManage || @@ -1944,7 +1958,7 @@ namespace OpenTween if (!listCache.TryGetValue(Index, out var itm, out var post)) return; - ChangeItemStyleRead(Read, itm, post, ((DetailsListView)_curTab.Tag)); + ChangeItemStyleRead(Read, itm, post, (DetailsListView)listCache.TargetList); } private void ChangeItemStyleRead(bool Read, ListViewItem Item, PostClass Post, DetailsListView DList) @@ -1999,7 +2013,7 @@ namespace OpenTween if (_anchorFlag) _post = _anchorPost; else - _post = _curPost; + _post = this.CurrentPost; if (_post == null) return; @@ -2008,10 +2022,11 @@ namespace OpenTween return; var index = listCache.StartIndex; + var listView = (DetailsListView)listCache.TargetList; foreach (var cachedPost in listCache.Post) { var backColor = this.JudgeColor(_post, cachedPost); - this._curList.ChangeItemBackColor(index++, backColor); + listView.ChangeItemBackColor(index++, backColor); } } @@ -2023,7 +2038,7 @@ namespace OpenTween if (_anchorFlag) _post = _anchorPost; else - _post = _curPost; + _post = this.CurrentPost; PostClass tPost = GetCurTabPost(Index); @@ -2032,7 +2047,7 @@ namespace OpenTween if (Item.Index == -1) Item.BackColor = JudgeColor(_post, tPost); else - _curList.ChangeItemBackColor(Item.Index, JudgeColor(_post, tPost)); + this.CurrentListView.ChangeItemBackColor(Item.Index, JudgeColor(_post, tPost)); } private Color JudgeColor(PostClass BasePost, PostClass TargetPost) @@ -2047,10 +2062,10 @@ namespace OpenTween else if (TargetPost.IsReply) //自分宛返信 cl = _clAtSelf; - else if (BasePost.ReplyToList.Contains(TargetPost.ScreenName.ToLowerInvariant())) + else if (BasePost.ReplyToList.Any(x => x.UserId == TargetPost.UserId)) //返信先 cl = _clAtFromTarget; - else if (TargetPost.ReplyToList.Contains(BasePost.ScreenName.ToLowerInvariant())) + else if (TargetPost.ReplyToList.Any(x => x.UserId == BasePost.UserId)) //その人への返信 cl = _clAtTarget; else if (TargetPost.ScreenName.Equals(BasePost.ScreenName, StringComparison.OrdinalIgnoreCase)) @@ -2063,6 +2078,36 @@ namespace OpenTween return cl; } + private void StatusTextHistoryBack() + { + if (!string.IsNullOrWhiteSpace(this.StatusText.Text)) + this._history[_hisIdx] = new StatusTextHistory(this.StatusText.Text, this.inReplyTo); + + this._hisIdx -= 1; + if (this._hisIdx < 0) + this._hisIdx = 0; + + var historyItem = this._history[this._hisIdx]; + this.inReplyTo = historyItem.inReplyTo; + this.StatusText.Text = historyItem.status; + this.StatusText.SelectionStart = this.StatusText.Text.Length; + } + + private void StatusTextHistoryForward() + { + if (!string.IsNullOrWhiteSpace(this.StatusText.Text)) + this._history[this._hisIdx] = new StatusTextHistory(this.StatusText.Text, this.inReplyTo); + + this._hisIdx += 1; + if (this._hisIdx > this._history.Count - 1) + this._hisIdx = this._history.Count - 1; + + var historyItem = this._history[this._hisIdx]; + this.inReplyTo = historyItem.inReplyTo; + this.StatusText.Text = historyItem.status; + this.StatusText.SelectionStart = this.StatusText.Text.Length; + } + private async void PostButton_Click(object sender, EventArgs e) { if (StatusText.Text.Trim().Length == 0) @@ -2074,7 +2119,8 @@ namespace OpenTween } } - if (this.ExistCurrentPost && StatusText.Text.Trim() == string.Format("RT @{0}: {1}", _curPost.ScreenName, _curPost.TextFromApi)) + var currentPost = this.CurrentPost; + if (this.ExistCurrentPost && StatusText.Text.Trim() == string.Format("RT @{0}: {1}", currentPost.ScreenName, currentPost.TextFromApi)) { DialogResult rtResult = MessageBox.Show(string.Format(Properties.Resources.PostButton_Click1, Environment.NewLine), "Retweet", @@ -2091,9 +2137,16 @@ namespace OpenTween } } - var inReplyToStatusId = this.inReplyTo?.Item1; - var inReplyToScreenName = this.inReplyTo?.Item2; - _history[_history.Count - 1] = new PostingStatus(StatusText.Text, inReplyToStatusId, inReplyToScreenName); + if (TextContainsOnlyMentions(this.StatusText.Text)) + { + var message = string.Format(Properties.Resources.PostConfirmText, this.StatusText.Text); + var ret = MessageBox.Show(message, ApplicationSettings.ApplicationName, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2); + + if (ret != DialogResult.OK) + return; + } + + _history[_history.Count - 1] = new StatusTextHistory(StatusText.Text, this.inReplyTo); if (SettingManager.Common.Nicoms) { @@ -2113,9 +2166,37 @@ namespace OpenTween StatusText.SelectionStart = StatusText.Text.Length; CheckReplyTo(StatusText.Text); - var statusText = this.FormatStatusText(this.StatusText.Text); + var status = new PostStatusParams(); - if (this.GetRestStatusCount(statusText) < 0) + var statusTextCompat = this.FormatStatusText(this.StatusText.Text); + if (this.GetRestStatusCount(statusTextCompat) >= 0) + { + // auto_populate_reply_metadata や attachment_url を使用しなくても 140 字以内に + // 収まる場合はこれらのオプションを使用せずに投稿する + status.Text = statusTextCompat; + status.InReplyToStatusId = this.inReplyTo?.StatusId; + } + else + { + status.Text = this.FormatStatusTextExtended(this.StatusText.Text, out var autoPopulatedUserIds, out var attachmentUrl); + status.InReplyToStatusId = this.inReplyTo?.StatusId; + + status.AttachmentUrl = attachmentUrl; + + // リプライ先がセットされていても autoPopulatedUserIds が空の場合は auto_populate_reply_metadata を有効にしない + // (非公式 RT の場合など) + var replyToPost = this.inReplyTo != null ? this._statuses[this.inReplyTo.Value.StatusId] : null; + if (replyToPost != null && autoPopulatedUserIds.Length != 0) + { + status.AutoPopulateReplyMetadata = true; + + // ReplyToList のうち autoPopulatedUserIds に含まれていないユーザー ID を抽出 + status.ExcludeReplyUserIds = replyToPost.ReplyToList.Select(x => x.UserId).Except(autoPopulatedUserIds) + .ToArray(); + } + } + + if (this.GetRestStatusCount(status.Text) < 0) { // 文字数制限を超えているが強制的に投稿するか var ret = MessageBox.Show(Properties.Resources.PostLengthOverMessage1, Properties.Resources.PostLengthOverMessage2, MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2); @@ -2123,24 +2204,23 @@ namespace OpenTween return; } - var status = new PostingStatus(); - status.status = statusText; - - status.inReplyToId = this.inReplyTo?.Item1; - status.inReplyToName = this.inReplyTo?.Item2; + IMediaUploadService uploadService = null; + IMediaItem[] uploadItems = null; if (ImageSelector.Visible) { //画像投稿 - if (!ImageSelector.TryGetSelectedMedia(out status.imageService, out status.mediaItems)) + if (!ImageSelector.TryGetSelectedMedia(out var serviceName, out uploadItems)) return; + + uploadService = this.ImageSelector.GetService(serviceName); } this.inReplyTo = null; StatusText.Text = ""; - _history.Add(new PostingStatus()); + _history.Add(new StatusTextHistory()); _hisIdx = _history.Count - 1; - if (!ToolStripFocusLockMenuItem.Checked) - ((Control)ListTab.SelectedTab.Tag).Focus(); + if (!SettingManager.Common.FocusLockToStatusText) + this.CurrentListView.Focus(); urlUndoBuffer = null; UrlUndoToolStripMenuItem.Enabled = false; //Undoをできないように設定 @@ -2151,7 +2231,7 @@ namespace OpenTween await this.OpenUriInBrowserAsync(tmp); } - await this.PostMessageAsync(status); + await this.PostMessageAsync(status, uploadService, uploadItems); } private void EndToolStripMenuItem_Click(object sender, EventArgs e) @@ -2208,260 +2288,71 @@ namespace OpenTween return true; } - private Task GetHomeTimelineAsync() - { - return this.GetHomeTimelineAsync(loadMore: false); - } - - private async Task GetHomeTimelineAsync(bool loadMore) - { - await this.workerSemaphore.WaitAsync(); - - try - { - var homeTab = this._statuses.GetTabByType(); - await homeTab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - - this.RefreshTimeline(); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetTimeline)"; - } - finally - { - this.workerSemaphore.Release(); - } - } - - private Task GetReplyAsync() - { - return this.GetReplyAsync(loadMore: false); - } - - private async Task GetReplyAsync(bool loadMore) - { - await this.workerSemaphore.WaitAsync(); - - try - { - var replyTab = this._statuses.GetTabByType(); - await replyTab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - - this.RefreshTimeline(); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetTimeline)"; - } - finally - { - this.workerSemaphore.Release(); - } - } - - private Task GetDirectMessagesAsync() - { - return this.GetDirectMessagesAsync(loadMore: false); - } + /// 指定された型 に合致する全てのタブを更新します + private Task RefreshTabAsync() where T : TabModel + => this.RefreshTabAsync(backward: false); - private async Task GetDirectMessagesAsync(bool loadMore) + /// 指定された型 に合致する全てのタブを更新します + private Task RefreshTabAsync(bool backward) where T : TabModel { - await this.workerSemaphore.WaitAsync(); + var loadTasks = + from tab in this._statuses.GetTabsByType() + select this.RefreshTabAsync(tab, backward); - try - { - var dmTab = this._statuses.GetTabByType(); - await dmTab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - - this.RefreshTimeline(); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetDirectMessage)"; - } - finally - { - this.workerSemaphore.Release(); - } + return Task.WhenAll(loadTasks); } - private Task GetFavoritesAsync() - { - return this.GetFavoritesAsync(loadMore: false); - } + /// 指定されたタブ を更新します + private Task RefreshTabAsync(TabModel tab) + => this.RefreshTabAsync(tab, backward: false); - private async Task GetFavoritesAsync(bool loadMore) + /// 指定されたタブ を更新します + private async Task RefreshTabAsync(TabModel tab, bool backward) { await this.workerSemaphore.WaitAsync(); try { - var favTab = this._statuses.GetTabByType(); - await favTab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - + await Task.Run(() => tab.RefreshAsync(this.tw, backward, this._initial, this.workerProgress)); this.RefreshTimeline(); } catch (WebApiException ex) { this._myStatusError = true; - this.StatusLabel.Text = ex.Message; - } - finally - { - this.workerSemaphore.Release(); - } - } - - private Task GetPublicSearchAllAsync() - { - var tabs = this._statuses.GetTabsByType(); - - return this.GetPublicSearchAsync(tabs, loadMore: false); - } - - private Task GetPublicSearchAsync(PublicSearchTabModel tab) - { - return this.GetPublicSearchAsync(tab, loadMore: false); - } - - private Task GetPublicSearchAsync(PublicSearchTabModel tab, bool loadMore) - { - return this.GetPublicSearchAsync(new[] { tab }, loadMore); - } - - private async Task GetPublicSearchAsync(IEnumerable tabs, bool loadMore) - { - await this.workerSemaphore.WaitAsync(); - - try - { - foreach (var tab in tabs) - { - try - { - await tab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetSearch)"; - } - } - - this.RefreshTimeline(); - } - finally - { - this.workerSemaphore.Release(); - } - } - - private Task GetUserTimelineAllAsync() - { - var tabs = this._statuses.GetTabsByType(); - - return this.GetUserTimelineAsync(tabs, loadMore: false); - } - - private Task GetUserTimelineAsync(UserTimelineTabModel tab) - { - return this.GetUserTimelineAsync(tab, loadMore: false); - } - - private Task GetUserTimelineAsync(UserTimelineTabModel tab, bool loadMore) - { - return this.GetUserTimelineAsync(new[] { tab }, loadMore); - } - - private async Task GetUserTimelineAsync(IEnumerable tabs, bool loadMore) - { - await this.workerSemaphore.WaitAsync(); - - try - { - foreach (var tab in tabs) - { - try - { - await tab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetUserTimeline)"; - } - } - - this.RefreshTimeline(); - } - finally - { - this.workerSemaphore.Release(); - } - } - - private Task GetListTimelineAllAsync() - { - var tabs = this._statuses.GetTabsByType(); - - return this.GetListTimelineAsync(tabs, loadMore: false); - } - - private Task GetListTimelineAsync(ListTimelineTabModel tab) - { - return this.GetListTimelineAsync(tab, loadMore: false); - } - - private Task GetListTimelineAsync(ListTimelineTabModel tab, bool loadMore) - { - return this.GetListTimelineAsync(new[] { tab }, loadMore); - } - private async Task GetListTimelineAsync(IEnumerable tabs, bool loadMore) - { - await this.workerSemaphore.WaitAsync(); - - try - { - foreach (var tab in tabs) + string tabType; + switch (tab) { - try - { - await tab.RefreshAsync(this.tw, loadMore, this._initial, this.workerProgress); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetListStatus)"; - } + case HomeTabModel _: + tabType = "GetTimeline"; + break; + case MentionsTabModel _: + tabType = "GetTimeline"; + break; + case DirectMessagesTabModel _: + tabType = "GetDirectMessage"; + break; + case FavoritesTabModel _: + tabType = "GetFavorites"; + break; + case PublicSearchTabModel _: + tabType = "GetSearch"; + break; + case UserTimelineTabModel _: + tabType = "GetUserTimeline"; + break; + case ListTimelineTabModel _: + tabType = "GetListStatus"; + break; + case RelatedPostsTabModel _: + tabType = "GetRelatedTweets"; + break; + default: + tabType = tab.GetType().Name.Replace("Model", ""); + break; } - this.RefreshTimeline(); - } - finally - { - this.workerSemaphore.Release(); - } - } - - private async Task GetRelatedTweetsAsync(RelatedPostsTabModel tab) - { - await this.workerSemaphore.WaitAsync(); - - try - { - await tab.RefreshAsync(this.tw, this._initial, this.workerProgress); - - this.RefreshTimeline(); - } - catch (WebApiException ex) - { - this._myStatusError = true; - this.StatusLabel.Text = $"Err:{ex.Message}(GetRelatedTweets)"; + this.StatusLabel.Text = $"Err:{ex.Message}({tabType})"; } finally { @@ -2510,9 +2401,17 @@ namespace OpenTween try { - await this.twitterApi.FavoritesCreate(post.RetweetedId ?? post.StatusId) - .IgnoreResponse() - .ConfigureAwait(false); + try + { + await this.twitterApi.FavoritesCreate(post.RetweetedId ?? post.StatusId) + .IgnoreResponse() + .ConfigureAwait(false); + } + catch (TwitterApiException ex) + when (ex.ErrorResponse.Errors.All(x => x.Code == TwitterErrorCode.AlreadyFavorited)) + { + // エラーコード 139 のみの場合は成功と見なす + } if (SettingManager.Common.RestrictFavCheck) { @@ -2523,7 +2422,7 @@ namespace OpenTween throw new WebApiException("NG(Restricted?)"); } - this._favTimestamps.Add(DateTime.Now); + this._favTimestamps.Add(DateTimeUtc.Now); // TLでも取得済みならfav反映 if (this._statuses.ContainsKey(statusId)) @@ -2551,7 +2450,7 @@ namespace OpenTween } // 時速表示用 - var oneHour = DateTime.Now - TimeSpan.FromHours(1); + var oneHour = DateTimeUtc.Now - TimeSpan.FromHours(1); foreach (var i in MyCommon.CountDown(this._favTimestamps.Count - 1, 0)) { if (this._favTimestamps[i] < oneHour) @@ -2566,16 +2465,17 @@ namespace OpenTween this.RefreshTimeline(); - if (this._curList != null && this._curTab != null && this._curTab.Text == tab.TabName) + if (this.CurrentTabName == tab.TabName) { - using (ControlTransaction.Update(this._curList)) + using (ControlTransaction.Update(this.CurrentListView)) { var idx = tab.IndexOf(statusId); if (idx != -1) this.ChangeCacheStyleRead(post.IsRead, idx); } - if (statusId == this._curPost.StatusId) + var currentPost = this.CurrentPost; + if (currentPost != null && statusId == currentPost.StatusId) await this.DispSelectedPost(true); // 選択アイテム再表示 } } @@ -2668,7 +2568,7 @@ namespace OpenTween this.RefreshTimeline(); - if (this._curList != null && this._curTab != null && this._curTab.Text == tab.TabName) + if (this.CurrentTabName == tab.TabName) { if (tab.TabType == MyCommon.TabUsageType.Favorites) { @@ -2676,7 +2576,7 @@ namespace OpenTween } else { - using (ControlTransaction.Update(this._curList)) + using (ControlTransaction.Update(this.CurrentListView)) { foreach (var statusId in successIds) { @@ -2689,13 +2589,14 @@ namespace OpenTween } } - if (successIds.Contains(this._curPost.StatusId)) + var currentPost = this.CurrentPost; + if (currentPost != null && successIds.Contains(currentPost.StatusId)) await this.DispSelectedPost(true); // 選択アイテム再表示 } } } - private async Task PostMessageAsync(PostingStatus status) + private async Task PostMessageAsync(PostStatusParams postParams, IMediaUploadService uploadService, IMediaItem[] uploadItems) { await this.workerSemaphore.WaitAsync(); @@ -2703,7 +2604,7 @@ namespace OpenTween { var progress = new Progress(x => this.StatusLabel.Text = x); - await this.PostMessageAsyncInternal(progress, this.workerCts.Token, status); + await this.PostMessageAsyncInternal(progress, this.workerCts.Token, postParams, uploadService, uploadItems); } catch (WebApiException ex) { @@ -2716,7 +2617,8 @@ namespace OpenTween } } - private async Task PostMessageAsyncInternal(IProgress p, CancellationToken ct, PostingStatus status) + private async Task PostMessageAsyncInternal(IProgress p, CancellationToken ct, PostStatusParams postParams, + IMediaUploadService uploadService, IMediaItem[] uploadItems) { if (ct.IsCancellationRequested) return; @@ -2726,23 +2628,23 @@ namespace OpenTween p.Report("Posting..."); + PostClass post = null; var errMsg = ""; try { await Task.Run(async () => { - if (status.mediaItems == null || status.mediaItems.Length == 0) - { - await this.tw.PostStatus(status.status, status.inReplyToId) - .ConfigureAwait(false); - } - else + var postParamsWithMedia = postParams; + + if (uploadService != null && uploadItems != null && uploadItems.Length > 0) { - var service = ImageSelector.GetService(status.imageService); - await service.PostStatusAsync(status.status, status.inReplyToId, status.mediaItems) + postParamsWithMedia = await uploadService.UploadAsync(uploadItems, postParamsWithMedia) .ConfigureAwait(false); } + + post = await this.tw.PostStatus(postParamsWithMedia) + .ConfigureAwait(false); }); p.Report(Properties.Resources.PostWorker_RunWorkerCompletedText4); @@ -2764,9 +2666,9 @@ namespace OpenTween finally { // 使い終わった MediaItem は破棄する - if (status.mediaItems != null) + if (uploadItems != null) { - foreach (var disposableItem in status.mediaItems.OfType()) + foreach (var disposableItem in uploadItems.OfType()) { disposableItem.Dispose(); } @@ -2780,7 +2682,7 @@ namespace OpenTween !errMsg.StartsWith("OK:", StringComparison.Ordinal) && !errMsg.StartsWith("Warn:", StringComparison.Ordinal)) { - var message = string.Format(Properties.Resources.StatusUpdateFailed, errMsg, status.status); + var message = string.Format(Properties.Resources.StatusUpdateFailed, errMsg, postParams.Text); var ret = MessageBox.Show( message, @@ -2790,20 +2692,23 @@ namespace OpenTween if (ret == DialogResult.Retry) { - await this.PostMessageAsync(status); + await this.PostMessageAsync(postParams, uploadService, uploadItems); } else { + this.StatusTextHistoryBack(); + this.StatusText.Focus(); + // 連投モードのときだけEnterイベントが起きないので強制的に背景色を戻す - if (this.ToolStripFocusLockMenuItem.Checked) + if (SettingManager.Common.FocusLockToStatusText) this.StatusText_Enter(this.StatusText, EventArgs.Empty); } return; } - this._postTimestamps.Add(DateTime.Now); + this._postTimestamps.Add(DateTimeUtc.Now); - var oneHour = DateTime.Now - TimeSpan.FromHours(1); + var oneHour = DateTimeUtc.Now - TimeSpan.FromHours(1); foreach (var i in MyCommon.CountDown(this._postTimestamps.Count - 1, 0)) { if (this._postTimestamps[i] < oneHour) @@ -2814,18 +2719,26 @@ namespace OpenTween { this.HashMgr.ClearHashtag(); this.HashStripSplitButton.Text = "#[-]"; + this.HashTogglePullDownMenuItem.Checked = false; this.HashToggleMenuItem.Checked = false; - this.HashToggleToolStripMenuItem.Checked = false; } this.SetMainWindowTitle(); - if (SettingManager.Common.PostAndGet) + // TLに反映 + if (!this.tw.UserStreamActive) { - if (this.tw.UserStreamActive) - this.RefreshTimeline(); + if (SettingManager.Common.PostAndGet) + await this.RefreshTabAsync(); else - await this.GetHomeTimelineAsync(); + { + if (post != null) + { + this._statuses.AddPost(post); + this._statuses.DistributePosts(); + this.RefreshTimeline(); + } + } } } @@ -2838,9 +2751,6 @@ namespace OpenTween var progress = new Progress(x => this.StatusLabel.Text = x); await this.RetweetAsyncInternal(progress, this.workerCts.Token, statusIds); - - if (SettingManager.Common.PostAndGet && !this.tw.UserStreamActive) - await this.GetHomeTimelineAsync(); } catch (WebApiException ex) { @@ -2869,24 +2779,46 @@ namespace OpenTween p.Report("Posting..."); - foreach (var statusId in statusIds) + var posts = new List(); + + await Task.Run(async () => { - await this.tw.PostRetweet(statusId, read).ConfigureAwait(false); - } + foreach (var statusId in statusIds) + { + var post = await this.tw.PostRetweet(statusId, read).ConfigureAwait(false); + if (post != null) posts.Add(post); + } + }); if (ct.IsCancellationRequested) return; p.Report(Properties.Resources.PostWorker_RunWorkerCompletedText4); - this._postTimestamps.Add(DateTime.Now); + this._postTimestamps.Add(DateTimeUtc.Now); - var oneHour = DateTime.Now - TimeSpan.FromHours(1); + var oneHour = DateTimeUtc.Now - TimeSpan.FromHours(1); foreach (var i in MyCommon.CountDown(this._postTimestamps.Count - 1, 0)) { if (this._postTimestamps[i] < oneHour) this._postTimestamps.RemoveAt(i); } + + // TLに反映 + if (!this.tw.UserStreamActive) + { + // 自分のRTはTLの更新では取得できない場合があるので、 + // 投稿時取得の有無に関わらず追加しておく + posts.ForEach(post => this._statuses.AddPost(post)); + + if (SettingManager.Common.PostAndGet) + await this.RefreshTabAsync(); + else + { + this._statuses.DistributePosts(); + this.RefreshTimeline(); + } + } } private async Task RefreshFollowerIdsAsync() @@ -2902,7 +2834,7 @@ namespace OpenTween this.RefreshTimeline(); this.PurgeListViewItemCache(); - this._curList?.Refresh(); + this.CurrentListView.Refresh(); } catch (WebApiException ex) { @@ -2970,8 +2902,7 @@ namespace OpenTween } this.PurgeListViewItemCache(); - - this._curList?.Refresh(); + this.CurrentListView.Refresh(); } catch (WebApiException ex) { @@ -3025,11 +2956,12 @@ namespace OpenTween await this.FavoriteChange(true); break; case 2: - if (_curPost != null) - await this.ShowUserStatus(_curPost.ScreenName, false); + var post = this.CurrentPost; + if (post != null) + await this.ShowUserStatus(post.ScreenName, false); break; case 3: - ShowUserTimeline(); + await ShowUserTimeline(); break; case 4: ShowRelatedStatusesMenuItem_Click(null, null); @@ -3047,36 +2979,28 @@ namespace OpenTween } private async void FavAddToolStripMenuItem_Click(object sender, EventArgs e) - { - await this.FavoriteChange(true); - } + => await this.FavoriteChange(true); private async void FavRemoveToolStripMenuItem_Click(object sender, EventArgs e) - { - await this.FavoriteChange(false); - } + => await this.FavoriteChange(false); private async void FavoriteRetweetMenuItem_Click(object sender, EventArgs e) - { - await this.FavoritesRetweetOfficial(); - } + => await this.FavoritesRetweetOfficial(); private async void FavoriteRetweetUnofficialMenuItem_Click(object sender, EventArgs e) - { - await this.FavoritesRetweetUnofficial(); - } + => await this.FavoritesRetweetUnofficial(); private async Task FavoriteChange(bool FavAdd, bool multiFavoriteChangeDialogEnable = true) { - if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out var tab)) - return; + var tab = this.CurrentTab; + var posts = tab.SelectedPosts; //trueでFavAdd,falseでFavRemove - if (tab.TabType == MyCommon.TabUsageType.DirectMessage || _curList.SelectedIndices.Count == 0 + if (tab.TabType == MyCommon.TabUsageType.DirectMessage || posts.Length == 0 || !this.ExistCurrentPost) return; - if (this._curList.SelectedIndices.Count > 1) + if (posts.Length > 1) { if (FavAdd) { @@ -3102,7 +3026,7 @@ namespace OpenTween if (FavAdd) { - var selectedPost = this.GetCurTabPost(_curList.SelectedIndices[0]); + var selectedPost = posts.Single(); if (selectedPost.IsFav) { this.StatusLabel.Text = Properties.Resources.FavAddToolStripMenuItem_ClickText4; @@ -3113,10 +3037,7 @@ namespace OpenTween } else { - var selectedPosts = this._curList.SelectedIndices.Cast() - .Select(x => this.GetCurTabPost(x)) - .Where(x => x.IsFav); - + var selectedPosts = posts.Where(x => x.IsFav); var statusIds = selectedPosts.Select(x => x.StatusId).ToArray(); if (statusIds.Length == 0) { @@ -3137,21 +3058,23 @@ namespace OpenTween return post; } - return _statuses.Tabs[_curTab.Text][Index]; + return this.CurrentTab[Index]; } private async void MoveToHomeToolStripMenuItem_Click(object sender, EventArgs e) { - if (_curList.SelectedIndices.Count > 0) - await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + GetCurTabPost(_curList.SelectedIndices[0]).ScreenName); - else if (_curList.SelectedIndices.Count == 0) + var post = this.CurrentPost; + if (post != null) + await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + post.ScreenName); + else await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl); } private async void MoveToFavToolStripMenuItem_Click(object sender, EventArgs e) { - if (_curList.SelectedIndices.Count > 0) - await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + "#!/" + GetCurTabPost(_curList.SelectedIndices[0]).ScreenName + "/favorites"); + var post = this.CurrentPost; + if (post != null) + await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + "#!/" + post.ScreenName + "/favorites"); } private void TweenMain_ClientSizeChanged(object sender, EventArgs e) @@ -3213,7 +3136,7 @@ namespace OpenTween private void SetSortColumnByDisplayIndex(int columnIndex) { // 表示上の列の位置から ColumnHeader を求める - var col = this._curList.Columns.Cast() + var col = this.CurrentListView.Columns.Cast() .FirstOrDefault(x => x.DisplayIndex == columnIndex); if (col == null) @@ -3232,7 +3155,7 @@ namespace OpenTween private void SetSortLastColumn() { // 表示上の最後列にある ColumnHeader を求める - var col = this._curList.Columns.Cast() + var col = this.CurrentListView.Columns.Cast() .OrderByDescending(x => x.DisplayIndex) .First(); @@ -3254,7 +3177,7 @@ namespace OpenTween this._statuses.ToggleSortOrder(sortColumn); this.InitColumnText(); - var list = this._curList; + var list = this.CurrentListView; if (_iconCol) { list.Columns[0].Text = this.ColumnText[0]; @@ -3270,10 +3193,11 @@ namespace OpenTween this.PurgeListViewItemCache(); - var tab = this._statuses.Tabs[this._curTab.Text]; - if (tab.AllCount > 0 && this._curPost != null) + var tab = this.CurrentTab; + var post = this.CurrentPost; + if (tab.AllCount > 0 && post != null) { - var idx = tab.IndexOf(this._curPost.StatusId); + var idx = tab.IndexOf(post.StatusId); if (idx > -1) { this.SelectListItem(list, idx); @@ -3296,8 +3220,6 @@ namespace OpenTween private void ContextMenuOperate_Opening(object sender, CancelEventArgs e) { - if (ListTab.SelectedTab == null) return; - if (_statuses == null || _statuses.Tabs == null || !_statuses.Tabs.ContainsKey(ListTab.SelectedTab.Text)) return; if (!this.ExistCurrentPost) { ReplyStripMenuItem.Enabled = false; @@ -3328,12 +3250,13 @@ namespace OpenTween ReadedStripMenuItem.Enabled = true; UnreadStripMenuItem.Enabled = true; } - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || _curPost.IsDm) + var tab = this.CurrentTab; + var post = this.CurrentPost; + if (tab.TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || post.IsDm) { FavAddToolStripMenuItem.Enabled = false; FavRemoveToolStripMenuItem.Enabled = false; StatusOpenMenuItem.Enabled = false; - FavorareMenuItem.Enabled = false; ShowRelatedStatusesMenuItem.Enabled = false; ReTweetStripMenuItem.Enabled = false; @@ -3347,10 +3270,9 @@ namespace OpenTween FavAddToolStripMenuItem.Enabled = true; FavRemoveToolStripMenuItem.Enabled = true; StatusOpenMenuItem.Enabled = true; - FavorareMenuItem.Enabled = true; ShowRelatedStatusesMenuItem.Enabled = true; //PublicSearchの時問題出るかも - if (!_curPost.CanRetweetBy(this.twitterApi.CurrentUserId)) + if (!post.CanRetweetBy(this.twitterApi.CurrentUserId)) { ReTweetStripMenuItem.Enabled = false; ReTweetUnofficialStripMenuItem.Enabled = false; @@ -3375,8 +3297,7 @@ namespace OpenTween //{ // RefreshMoreStripMenuItem.Enabled = false; //} - if (!this.ExistCurrentPost - || _curPost.InReplyToStatusId == null) + if (!this.ExistCurrentPost || post.InReplyToStatusId == null) { RepliedStatusOpenMenuItem.Enabled = false; } @@ -3384,7 +3305,7 @@ namespace OpenTween { RepliedStatusOpenMenuItem.Enabled = true; } - if (!this.ExistCurrentPost || string.IsNullOrEmpty(_curPost.RetweetedBy)) + if (!this.ExistCurrentPost || string.IsNullOrEmpty(post.RetweetedBy)) { MoveToRTHomeMenuItem.Enabled = false; } @@ -3395,8 +3316,8 @@ namespace OpenTween if (this.ExistCurrentPost) { - this.DeleteStripMenuItem.Enabled = this._curPost.CanDeleteBy(this.tw.UserId); - if (this._curPost.RetweetedByUserId == this.tw.UserId) + this.DeleteStripMenuItem.Enabled = post.CanDeleteBy(this.tw.UserId); + if (post.RetweetedByUserId == this.tw.UserId) this.DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText2; else this.DeleteStripMenuItem.Text = Properties.Resources.DeleteMenuText1; @@ -3404,27 +3325,17 @@ namespace OpenTween } private void ReplyStripMenuItem_Click(object sender, EventArgs e) - { - MakeReplyOrDirectStatus(false, true); - } + => this.MakeReplyOrDirectStatus(false, true); private void DMStripMenuItem_Click(object sender, EventArgs e) - { - MakeReplyOrDirectStatus(false, false); - } + => this.MakeReplyOrDirectStatus(false, false); private async Task doStatusDelete() { - if (this._curTab == null || this._curList == null) + var posts = this.CurrentTab.SelectedPosts; + if (posts.Length == 0) return; - if (this._curList.SelectedIndices.Count == 0) - return; - - var posts = this._curList.SelectedIndices.Cast() - .Select(x => this.GetCurTabPost(x)) - .ToArray(); - // 選択されたツイートの中に削除可能なものが一つでもあるか if (!posts.Any(x => x.CanDeleteBy(this.tw.UserId))) return; @@ -3437,7 +3348,8 @@ namespace OpenTween if (ret != DialogResult.OK) return; - var focusedIndex = this._curList.FocusedItem?.Index ?? this._curList.TopItem?.Index ?? 0; + var currentListView = this.CurrentListView; + var focusedIndex = currentListView.FocusedItem?.Index ?? currentListView.TopItem?.Index ?? 0; using (ControlTransaction.Cursor(this, Cursors.WaitCursor)) { @@ -3451,8 +3363,7 @@ namespace OpenTween { if (post.IsDm) { - await this.twitterApi.DirectMessagesDestroy(post.StatusId) - .IgnoreResponse(); + await this.twitterApi.DirectMessagesEventsDestroy(post.StatusId.ToString(CultureInfo.InvariantCulture)); } else { @@ -3496,8 +3407,6 @@ namespace OpenTween this.StatusLabel.Text = Properties.Resources.DeleteStripMenuItem_ClickText3; // 失敗 this.PurgeListViewItemCache(); - this._curPost = null; - this._curItemIndex = -1; foreach (var tabPage in this.ListTab.TabPages.Cast()) { @@ -3508,7 +3417,7 @@ namespace OpenTween { listView.VirtualListSize = tab.AllCount; - if (tabPage == this._curTab) + if (tab.TabName == this.CurrentTabName) { listView.SelectedIndices.Clear(); @@ -3540,18 +3449,17 @@ namespace OpenTween } private async void DeleteStripMenuItem_Click(object sender, EventArgs e) - { - await this.doStatusDelete(); - } + => await this.doStatusDelete(); private void ReadedStripMenuItem_Click(object sender, EventArgs e) { - using (ControlTransaction.Update(this._curList)) + using (ControlTransaction.Update(this.CurrentListView)) { - foreach (int idx in _curList.SelectedIndices) + var tab = this.CurrentTab; + foreach (var statusId in tab.SelectedStatusIds) { - var post = this._statuses.Tabs[this._curTab.Text][idx]; - this._statuses.SetReadAllTab(post.StatusId, read: true); + this._statuses.SetReadAllTab(statusId, read: true); + var idx = tab.IndexOf(statusId); ChangeCacheStyleRead(true, idx); } ColorizeList(); @@ -3571,12 +3479,13 @@ namespace OpenTween private void UnreadStripMenuItem_Click(object sender, EventArgs e) { - using (ControlTransaction.Update(this._curList)) + using (ControlTransaction.Update(this.CurrentListView)) { - foreach (int idx in _curList.SelectedIndices) + var tab = this.CurrentTab; + foreach (var statusId in tab.SelectedStatusIds) { - var post = this._statuses.Tabs[this._curTab.Text][idx]; - this._statuses.SetReadAllTab(post.StatusId, read: false); + this._statuses.SetReadAllTab(statusId, read: false); + var idx = tab.IndexOf(statusId); ChangeCacheStyleRead(false, idx); } ColorizeList(); @@ -3595,90 +3504,13 @@ namespace OpenTween } private async void RefreshStripMenuItem_Click(object sender, EventArgs e) - { - await this.DoRefresh(); - } + => await this.DoRefresh(); private async Task DoRefresh() - { - if (_curTab != null) - { - if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out var tab)) - return; - - switch (tab) - { - case MentionsTabModel replyTab: - await this.GetReplyAsync(); - break; - case DirectMessagesTabModel dmTab: - await this.GetDirectMessagesAsync(); - break; - case FavoritesTabModel favTab: - await this.GetFavoritesAsync(); - break; - case PublicSearchTabModel searchTab: - if (string.IsNullOrEmpty(searchTab.SearchWords)) return; - await this.GetPublicSearchAsync(searchTab); - break; - case UserTimelineTabModel userTab: - await this.GetUserTimelineAsync(userTab); - break; - case ListTimelineTabModel listTab: - if (listTab.ListInfo == null || listTab.ListInfo.Id == 0) return; - await this.GetListTimelineAsync(listTab); - break; - default: - await this.GetHomeTimelineAsync(); - break; - } - } - else - { - await this.GetHomeTimelineAsync(); - } - } + => await this.RefreshTabAsync(this.CurrentTab); private async Task DoRefreshMore() - { - //ページ指定をマイナス1に - if (_curTab != null) - { - if (!this._statuses.Tabs.TryGetValue(this._curTab.Text, out var tab)) - return; - - switch (tab) - { - case MentionsTabModel replyTab: - await this.GetReplyAsync(loadMore: true); - break; - case DirectMessagesTabModel dmTab: - await this.GetDirectMessagesAsync(loadMore: true); - break; - case FavoritesTabModel favTab: - await this.GetFavoritesAsync(loadMore: true); - break; - case PublicSearchTabModel searchTab: - if (string.IsNullOrEmpty(searchTab.SearchWords)) return; - await this.GetPublicSearchAsync(searchTab, loadMore: true); - break; - case UserTimelineTabModel userTab: - await this.GetUserTimelineAsync(userTab, loadMore: true); - break; - case ListTimelineTabModel listTab: - if (listTab.ListInfo == null || listTab.ListInfo.Id == 0) return; - await this.GetListTimelineAsync(listTab, loadMore: true); - break; - default: - await this.GetHomeTimelineAsync(loadMore: true); - break; - } - } - else - { - await this.GetHomeTimelineAsync(loadMore: true); - } - } + => await this.RefreshTabAsync(this.CurrentTab, backward: true); private DialogResult ShowSettingDialog(bool showTaskbarIcon = false) { @@ -3731,6 +3563,7 @@ namespace OpenTween tw.RestrictFavCheck = SettingManager.Common.RestrictFavCheck; tw.ReadOwnPost = SettingManager.Common.ReadOwnPost; ShortUrl.Instance.DisableExpanding = !SettingManager.Common.TinyUrlResolve; + ShortUrl.Instance.BitlyAccessToken = SettingManager.Common.BitlyAccessToken; ShortUrl.Instance.BitlyId = SettingManager.Common.BilyUser; ShortUrl.Instance.BitlyKey = SettingManager.Common.BitlyPwd; TwitterApiConnection.RestApiHost = SettingManager.Common.TwitterApiHost; @@ -3916,7 +3749,7 @@ namespace OpenTween SetNotifyIconText(); this.PurgeListViewItemCache(); - _curList?.Refresh(); + this.CurrentListView.Refresh(); ListTab.Refresh(); _hookGlobalHotkey.UnregisterAllOriginalHotkey(); @@ -3957,7 +3790,7 @@ namespace OpenTween this.TopMost = SettingManager.Common.AlwaysTop; SaveConfigsAll(false); - if (tw.Username != oldUser.Username) + if (tw.UserId != oldUser.UserId) await this.doGetFollowersMenu(); } @@ -4081,32 +3914,32 @@ namespace OpenTween //追加したタブをアクティブに ListTab.SelectedIndex = ListTab.TabPages.Count - 1; //検索条件の設定 - ComboBox cmb = (ComboBox)ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"]; + var tabPage = this.CurrentTabPage; + ComboBox cmb = (ComboBox)tabPage.Controls["panelSearch"].Controls["comboSearch"]; cmb.Items.Add(searchWord); cmb.Text = searchWord; SaveConfigsTabs(); //検索実行 - this.SearchButton_Click(ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"], null); + this.SearchButton_Click(tabPage.Controls["panelSearch"].Controls["comboSearch"], null); } - private void ShowUserTimeline() + private async Task ShowUserTimeline() { if (!this.ExistCurrentPost) return; - AddNewTabForUserTimeline(_curPost.ScreenName); + await this.AddNewTabForUserTimeline(this.CurrentPost.ScreenName); } private void SearchComboBox_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Escape) { - TabPage relTp = ListTab.SelectedTab; - RemoveSpecifiedTab(relTp.Text, false); + RemoveSpecifiedTab(this.CurrentTabName, false); SaveConfigsTabs(); e.SuppressKeyPress = true; } } - public void AddNewTabForUserTimeline(string user) + public async Task AddNewTabForUserTimeline(string user) { //同一検索条件のタブが既に存在すれば、そのタブアクティブにして終了 foreach (var tb in _statuses.GetTabsByType()) @@ -4137,7 +3970,7 @@ namespace OpenTween ListTab.SelectedIndex = ListTab.TabPages.Count - 1; SaveConfigsTabs(); //検索実行 - this.GetUserTimelineAsync(tab); + await this.RefreshTabAsync(tab); } public bool AddNewTab(TabModel tab, bool startup) @@ -4175,10 +4008,12 @@ namespace OpenTween if (userTab != null || listTab != null) { - var label = new Label(); - label.Dock = DockStyle.Top; - label.Name = "labelUser"; - label.TabIndex = 0; + var label = new Label + { + Dock = DockStyle.Top, + Name = "labelUser", + TabIndex = 0, + }; if (listTab != null) { @@ -4375,7 +4210,7 @@ namespace OpenTween using (ControlTransaction.Layout(this)) using (ControlTransaction.Layout(_tabPage, false)) { - if (this.ListTab.SelectedTab == _tabPage) + if (this.CurrentTabName == TabName) { this.ListTab.SelectTab((this._beforeSelectedTab != null && this.ListTab.TabPages.Contains(this._beforeSelectedTab)) ? this._beforeSelectedTab : this.ListTab.TabPages[0]); this._beforeSelectedTab = null; @@ -4444,14 +4279,7 @@ namespace OpenTween _listCustom.SmallImageList = null; _listCustom.ListViewItemSorter = null; - //キャッシュのクリア - if (_curTab.Equals(_tabPage)) - { - _curTab = null; - _curItemIndex = -1; - _curList = null; - _curPost = null; - } + // キャッシュのクリア this.PurgeListViewItemCache(); } @@ -4486,7 +4314,7 @@ namespace OpenTween if (!dragEnableRectangle.Contains(e.Location)) { //タブが多段の場合にはMouseDownの前の段階で選択されたタブの段が変わっているので、このタイミングでカーソルの位置からタブを判定出来ない。 - tn = ListTab.SelectedTab.Text; + tn = this.CurrentTabName; } if (string.IsNullOrEmpty(tn)) return; @@ -4524,24 +4352,25 @@ namespace OpenTween SetMainWindowTitle(); SetStatusLabelUrl(); SetApiStatusLabel(); - if (ListTab.Focused || ((Control)ListTab.SelectedTab.Tag).Focused) this.Tag = ListTab.Tag; - TabMenuControl(ListTab.SelectedTab.Text); + if (ListTab.Focused || ((Control)this.CurrentTabPage.Tag).Focused) + this.Tag = ListTab.Tag; + TabMenuControl(this.CurrentTabName); this.PushSelectPostChain(); await DispSelectedPost(); } private void SetListProperty() { - //削除などで見つからない場合は処理せず - if (_curList == null) return; if (!_isColumnChanged) return; - int[] dispOrder = new int[_curList.Columns.Count]; - for (int i = 0; i < _curList.Columns.Count; i++) + var currentListView = this.CurrentListView; + + int[] dispOrder = new int[currentListView.Columns.Count]; + for (int i = 0; i < currentListView.Columns.Count; i++) { - for (int j = 0; j < _curList.Columns.Count; j++) + for (int j = 0; j < currentListView.Columns.Count; j++) { - if (_curList.Columns[j].DisplayIndex == i) + if (currentListView.Columns[j].DisplayIndex == i) { dispOrder[i] = j; break; @@ -4552,16 +4381,16 @@ namespace OpenTween //列幅、列並びを他のタブに設定 foreach (TabPage tb in ListTab.TabPages) { - if (!tb.Equals(_curTab)) + if (tb.Text == this.CurrentTabName) + continue; + + if (tb.Tag != null && tb.Controls.Count > 0) { - if (tb.Tag != null && tb.Controls.Count > 0) + DetailsListView lst = (DetailsListView)tb.Tag; + for (int i = 0; i < lst.Columns.Count; i++) { - DetailsListView lst = (DetailsListView)tb.Tag; - for (int i = 0; i < lst.Columns.Count; i++) - { - lst.Columns[dispOrder[i]].DisplayIndex = i; - lst.Columns[i].Width = _curList.Columns[i].Width; - } + lst.Columns[dispOrder[i]].DisplayIndex = i; + lst.Columns[i].Width = currentListView.Columns[i].Width; } } } @@ -4589,14 +4418,10 @@ namespace OpenTween } public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog) - { - ShowSuplDialog(owner, dialog, 0, ""); - } + => this.ShowSuplDialog(owner, dialog, 0, ""); public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog, int offset) - { - ShowSuplDialog(owner, dialog, offset, ""); - } + => this.ShowSuplDialog(owner, dialog, offset, ""); public void ShowSuplDialog(TextBox owner, AtIdSupplement dialog, int offset, string startswith) { @@ -4670,35 +4495,131 @@ namespace OpenTween } if (isSpace) { - e.Handled = true; - StatusText.Text = ""; - JumpUnreadMenuItem_Click(null, null); + e.Handled = true; + StatusText.Text = ""; + JumpUnreadMenuItem_Click(null, null); + } + } + } + this.StatusText_TextChanged(null, null); + } + + private void StatusText_TextChanged(object sender, EventArgs e) + { + //文字数カウント + int pLen = this.GetRestStatusCount(this.FormatStatusTextExtended(this.StatusText.Text)); + lblLen.Text = pLen.ToString(); + if (pLen < 0) + { + StatusText.ForeColor = Color.Red; + } + else + { + StatusText.ForeColor = _clInputFont; + } + + this.StatusText.AccessibleDescription = string.Format(Properties.Resources.StatusText_AccessibleDescription, pLen); + + if (string.IsNullOrEmpty(StatusText.Text)) + { + this.inReplyTo = null; + } + } + + /// + /// メンション以外の文字列が含まれていないテキストであるか判定します + /// + internal static bool TextContainsOnlyMentions(string text) + { + var mentions = TweetExtractor.ExtractMentionEntities(text).OrderBy(x => x.Indices[0]); + var startIndex = 0; + + foreach (var mention in mentions) + { + var textPart = text.Substring(startIndex, mention.Indices[0] - startIndex); + + if (!string.IsNullOrWhiteSpace(textPart)) + return false; + + startIndex = mention.Indices[1]; + } + + var textPartLast = text.Substring(startIndex); + + if (!string.IsNullOrWhiteSpace(textPartLast)) + return false; + + return true; + } + + /// + /// 投稿時に auto_populate_reply_metadata オプションによって自動で追加されるメンションを除去します + /// + private string RemoveAutoPopuratedMentions(string statusText, out long[] autoPopulatedUserIds) + { + List _autoPopulatedUserIds = new List(); + + var replyToPost = this.inReplyTo != null ? this._statuses[this.inReplyTo.Value.StatusId] : null; + if (replyToPost != null) + { + if (statusText.StartsWith($"@{replyToPost.ScreenName} ", StringComparison.Ordinal)) + { + statusText = statusText.Substring(replyToPost.ScreenName.Length + 2); + _autoPopulatedUserIds.Add(replyToPost.UserId); + + foreach (var (userId, screenName) in replyToPost.ReplyToList) + { + if (statusText.StartsWith($"@{screenName} ", StringComparison.Ordinal)) + { + statusText = statusText.Substring(screenName.Length + 2); + _autoPopulatedUserIds.Add(userId); + } } } } - this.StatusText_TextChanged(null, null); + + autoPopulatedUserIds = _autoPopulatedUserIds.ToArray(); + + return statusText; } - private void StatusText_TextChanged(object sender, EventArgs e) + /// + /// attachment_url に指定可能な URL が含まれていれば除去 + /// + private string RemoveAttachmentUrl(string statusText, out string attachmentUrl) { - //文字数カウント - int pLen = this.GetRestStatusCount(this.FormatStatusText(this.StatusText.Text)); - lblLen.Text = pLen.ToString(); - if (pLen < 0) - { - StatusText.ForeColor = Color.Red; - } - else - { - StatusText.ForeColor = _clInputFont; - } + attachmentUrl = null; - this.StatusText.AccessibleDescription = string.Format(Properties.Resources.StatusText_AccessibleDescription, pLen); + // attachment_url は media_id と同時に使用できない + if (this.ImageSelector.Visible && this.ImageSelector.SelectedService is TwitterPhoto) + return statusText; - if (string.IsNullOrEmpty(StatusText.Text)) - { - this.inReplyTo = null; - } + var match = Twitter.AttachmentUrlRegex.Match(statusText); + if (!match.Success) + return statusText; + + attachmentUrl = match.Value; + + // マッチした URL を空白に置換 + statusText = statusText.Substring(0, match.Index); + + // テキストと URL の間にスペースが含まれていれば除去 + return statusText.TrimEnd(' '); + } + + private string FormatStatusTextExtended(string statusText) + => this.FormatStatusTextExtended(statusText, out var autoPopulatedUserIds, out var attachmentUrl); + + /// + /// に加えて、拡張モードで140字にカウントされない文字列の除去を行います + /// + private string FormatStatusTextExtended(string statusText, out long[] autoPopulatedUserIds, out string attachmentUrl) + { + statusText = this.RemoveAutoPopuratedMentions(statusText, out autoPopulatedUserIds); + + statusText = this.RemoveAttachmentUrl(statusText, out attachmentUrl); + + return this.FormatStatusText(statusText); } /// @@ -4708,13 +4629,13 @@ namespace OpenTween { statusText = statusText.Replace("\r\n", "\n"); - if (this.ToolStripMenuItemUrlMultibyteSplit.Checked) + if (this.urlMultibyteSplit) { // URLと全角文字の切り離し statusText = Regex.Replace(statusText, @"https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#^]+", "$& "); } - if (this.IdeographicSpaceToSpaceToolStripMenuItem.Checked) + if (SettingManager.Common.WideSpaceConvert) { // 文中の全角スペースを半角スペース1個にする statusText = statusText.Replace(" ", " "); @@ -4740,6 +4661,17 @@ namespace OpenTween if (statusText.Contains("RT @")) disableFooter = true; + // 自分宛のリプライの場合は先頭の「@screen_name 」の部分を除去する (in_reply_to_status_id は維持される) + if (this.inReplyTo != null && this.inReplyTo.Value.ScreenName == this.tw.Username) + { + var mentionSelf = $"@{this.tw.Username} "; + if (statusText.StartsWith(mentionSelf, StringComparison.OrdinalIgnoreCase)) + { + if (statusText.Length > mentionSelf.Length || this.GetSelectedImageService() != null) + statusText = statusText.Substring(mentionSelf.Length); + } + } + var header = ""; var footer = ""; @@ -4768,7 +4700,7 @@ namespace OpenTween statusText = header + statusText + footer; - if (this.ToolStripMenuItemPreventSmsCommand.Checked) + if (this.preventSmsCommand) { // ツイートが意図せず SMS コマンドとして解釈されることを回避 (D, DM, M のみ) // 参照: https://support.twitter.com/articles/14020 @@ -4789,11 +4721,10 @@ namespace OpenTween /// private int GetRestStatusCount(string statusText) { - //文字数カウント var remainCount = this.tw.GetTextLengthRemain(statusText); - var uploadService = this.ImageSelector.SelectedService; - if (this.ImageSelector.Visible && uploadService != null) + var uploadService = this.GetSelectedImageService(); + if (uploadService != null) { // TODO: ImageSelector で選択中の画像の枚数が mediaCount 引数に渡るようにする remainCount -= uploadService.GetReservedTextLength(1); @@ -4802,9 +4733,12 @@ namespace OpenTween return remainCount; } + private IMediaUploadService GetSelectedImageService() + => this.ImageSelector.Visible ? this.ImageSelector.SelectedService : null; + private void MyList_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e) { - if (sender != this._curList) + if (sender != this.CurrentListView) return; var listCache = this._listItemCache; @@ -4832,10 +4766,11 @@ namespace OpenTween } // A cache miss, so create a new ListViewItem and pass it back. - TabPage tb = (TabPage)((DetailsListView)sender).Parent; + var tabPage = (TabPage)((DetailsListView)sender).Parent; + var tab = this._statuses.Tabs[tabPage.Text]; try { - e.Item = this.CreateItem(tb, _statuses.Tabs[tb.Text][e.ItemIndex], e.ItemIndex); + e.Item = this.CreateItem(tab, tab[e.ItemIndex], e.ItemIndex); } catch (Exception) { @@ -4847,25 +4782,30 @@ namespace OpenTween private void CreateCache(int startIndex, int endIndex) { - var tabInfo = this._statuses.Tabs[this._curTab.Text]; + var tabInfo = this.CurrentTab; if (tabInfo.AllCount == 0) return; + // インデックスを 0...(tabInfo.AllCount - 1) の範囲内にする + int FilterRange(int index) + => Math.Max(Math.Min(index, tabInfo.AllCount - 1), 0); + // キャッシュ要求(要求範囲±30を作成) - startIndex = Math.Max(startIndex - 30, 0); - endIndex = Math.Min(endIndex + 30, tabInfo.AllCount - 1); + startIndex = FilterRange(startIndex - 30); + endIndex = FilterRange(endIndex + 30); var cacheLength = endIndex - startIndex + 1; + var tab = this.CurrentTab; var posts = tabInfo[startIndex, endIndex]; //配列で取得 var listItems = Enumerable.Range(0, cacheLength) - .Select(x => this.CreateItem(this._curTab, posts[x], startIndex + x)) + .Select(x => this.CreateItem(tab, posts[x], startIndex + x)) .ToArray(); var listCache = new ListViewItemCache { - TargetList = this._curList, + TargetList = this.CurrentListView, StartIndex = startIndex, EndIndex = endIndex, Post = posts, @@ -4879,11 +4819,9 @@ namespace OpenTween /// DetailsListView のための ListViewItem のキャッシュを消去する /// private void PurgeListViewItemCache() - { - Interlocked.Exchange(ref this._listItemCache, null); - } + => Interlocked.Exchange(ref this._listItemCache, null); - private ListViewItem CreateItem(TabPage Tab, PostClass Post, int Index) + private ListViewItem CreateItem(TabModel tab, PostClass Post, int Index) { StringBuilder mk = new StringBuilder(); //if (Post.IsDeleted) mk.Append("×"); @@ -4896,8 +4834,8 @@ namespace OpenTween { string[] sitem= {"", Post.Nickname, - Post.IsDeleted ? "(DELETED)" : Post.AccessibleText, - Post.CreatedAt.ToString(SettingManager.Common.DateTimeFormat), + Post.IsDeleted ? "(DELETED)" : Post.AccessibleText.Replace('\n', ' '), + Post.CreatedAt.ToLocalTimeString(SettingManager.Common.DateTimeFormat), Post.ScreenName, "", mk.ToString(), @@ -4908,8 +4846,8 @@ namespace OpenTween { string[] sitem = {"", Post.Nickname, - Post.IsDeleted ? "(DELETED)" : Post.AccessibleText, - Post.CreatedAt.ToString(SettingManager.Common.DateTimeFormat), + Post.IsDeleted ? "(DELETED)" : Post.AccessibleText.Replace('\n', ' '), + Post.CreatedAt.ToLocalTimeString(SettingManager.Common.DateTimeFormat), Post.ScreenName + Environment.NewLine + "(RT:" + Post.RetweetedBy + ")", "", mk.ToString(), @@ -4920,10 +4858,15 @@ namespace OpenTween itm.Tag = Post; bool read = Post.IsRead; - //未読管理していなかったら既読として扱う - if (!_statuses.Tabs[Tab.Text].UnreadManage || !SettingManager.Common.UnreadManage) read = true; + // 未読管理していなかったら既読として扱う + if (!tab.UnreadManage || !SettingManager.Common.UnreadManage) + read = true; + ChangeItemStyleRead(read, itm, Post, null); - if (Tab.Equals(_curTab)) ColorizeList(itm, Index); + + if (tab.TabName == this.CurrentTabName) + this.ColorizeList(itm, Index); + return itm; } @@ -4935,8 +4878,6 @@ namespace OpenTween using (ControlTransaction.Cursor(this, Cursors.WaitCursor)) { this.PurgeListViewItemCache(); - this._curPost = null; - this._curItemIndex = -1; this._statuses.FilterAll(); foreach (TabPage tabPage in this.ListTab.TabPages) @@ -4967,15 +4908,10 @@ namespace OpenTween } private void MyList_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) - { - e.DrawDefault = true; - } + => e.DrawDefault = true; private void MyList_HScrolled(object sender, EventArgs e) - { - DetailsListView listView = (DetailsListView)sender; - listView.Refresh(); - } + => ((DetailsListView)sender).Refresh(); private void MyList_DrawItem(object sender, DrawListViewItemEventArgs e) { @@ -5247,7 +5183,7 @@ namespace OpenTween internal void DoTabSearch(string searchWord, bool caseSensitive, bool useRegex, SEARCHTYPE searchType) { - var tab = this._statuses.Tabs[this._curTab.Text]; + var tab = this.CurrentTab; if (tab.AllCount == 0) { @@ -5255,7 +5191,7 @@ namespace OpenTween return; } - var selectedIndex = this._curList.SelectedIndices.Count != 0 ? this._curList.SelectedIndices[0] : -1; + var selectedIndex = tab.SelectedIndex; int startIndex; switch (searchType) @@ -5302,15 +5238,13 @@ namespace OpenTween return; } - this.SelectListItem(this._curList, foundIndex); - this._curList.EnsureVisible(foundIndex); + var listView = this.CurrentListView; + this.SelectListItem(listView, foundIndex); + listView.EnsureVisible(foundIndex); } private void MenuItemSubSearch_Click(object sender, EventArgs e) - { - // 検索メニュー - this.ShowSearchDialog(); - } + => this.ShowSearchDialog(); // 検索メニュー private void MenuItemSearchNext_Click(object sender, EventArgs e) { @@ -5373,14 +5307,14 @@ namespace OpenTween } catch (TabException ex) { - MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, ex.Message, ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error); } var resultTab = new LocalSearchTabModel(tabName); this.AddNewTab(resultTab, startup: false); this._statuses.AddTab(resultTab); - var targetTab = this._statuses.Tabs[this._curTab.Text]; + var targetTab = this.CurrentTab; Func stringComparer; try @@ -5461,7 +5395,7 @@ namespace OpenTween private void JumpUnreadMenuItem_Click(object sender, EventArgs e) { - int bgnIdx = ListTab.TabPages.IndexOf(_curTab); + int bgnIdx = ListTab.TabPages.IndexOf(this.CurrentTabPage); if (ImageSelector.Enabled) return; @@ -5550,34 +5484,25 @@ namespace OpenTween private async void StatusOpenMenuItem_Click(object sender, EventArgs e) { - if (_curList.SelectedIndices.Count > 0 && _statuses.Tabs[_curTab.Text].TabType != MyCommon.TabUsageType.DirectMessage) - { - var post = _statuses.Tabs[_curTab.Text][_curList.SelectedIndices[0]]; + var tab = this.CurrentTab; + var post = this.CurrentPost; + if (post != null && tab.TabType != MyCommon.TabUsageType.DirectMessage) await this.OpenUriInBrowserAsync(MyCommon.GetStatusUrl(post)); - } - } - - private async void FavorareMenuItem_Click(object sender, EventArgs e) - { - if (_curList.SelectedIndices.Count > 0) - { - PostClass post = _statuses.Tabs[_curTab.Text][_curList.SelectedIndices[0]]; - await this.OpenUriInBrowserAsync(Properties.Resources.FavstarUrl + "users/" + post.ScreenName + "/recent"); - } } private async void VerUpMenuItem_Click(object sender, EventArgs e) - { - await this.CheckNewVersion(false); - } + => await this.CheckNewVersion(false); private void RunTweenUp() { - ProcessStartInfo pinfo = new ProcessStartInfo(); - pinfo.UseShellExecute = true; - pinfo.WorkingDirectory = MyCommon.settingPath; - pinfo.FileName = Path.Combine(MyCommon.settingPath, "TweenUp3.exe"); - pinfo.Arguments = "\"" + Application.StartupPath + "\""; + var pinfo = new ProcessStartInfo + { + UseShellExecute = true, + WorkingDirectory = MyCommon.settingPath, + FileName = Path.Combine(MyCommon.settingPath, "TweenUp3.exe"), + Arguments = "\"" + Application.StartupPath + "\"", + }; + try { Process.Start(pinfo); @@ -5601,7 +5526,7 @@ namespace OpenTween public async Task GetVersionInfoAsync() { var versionInfoUrl = new Uri(ApplicationSettings.VersionInfoUrl + "?" + - DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount); + DateTimeUtc.Now.ToString("yyMMddHHmmss") + Environment.TickCount); var responseText = await Networking.Http.GetStringAsync(versionInfoUrl) .ConfigureAwait(false); @@ -5647,6 +5572,9 @@ namespace OpenTween return; } + if (startup && versionInfo.Version <= SettingManager.Common.SkipUpdateVersion) + return; + using (var dialog = new UpdateDialog()) { dialog.SummaryText = string.Format(Properties.Resources.CheckNewVersionText3, @@ -5657,6 +5585,11 @@ namespace OpenTween { await this.OpenUriInBrowserAsync(versionInfo.DownloadUri.OriginalString); } + else if (dialog.SkipButtonPressed) + { + SettingManager.Common.SkipUpdateVersion = versionInfo.Version; + this.ModifySettingCommon = true; + } } } catch (Exception) @@ -5699,17 +5632,10 @@ namespace OpenTween } public string createDetailHtml(string orgdata) - { - if (SettingManager.Local.UseTwemoji) - orgdata = EmojiFormatter.ReplaceEmojiToImg(orgdata); - - return detailHtmlFormatHeader + orgdata + detailHtmlFormatFooter; - } + => detailHtmlFormatHeader + orgdata + detailHtmlFormatFooter; private Task DispSelectedPost() - { - return this.DispSelectedPost(false); - } + => this.DispSelectedPost(false); private PostClass displayPost = new PostClass(); @@ -5720,18 +5646,19 @@ namespace OpenTween private async Task DispSelectedPost(bool forceupdate) { - if (_curList.SelectedIndices.Count == 0 || _curPost == null) + var currentPost = this.CurrentPost; + if (currentPost == null) return; var oldDisplayPost = this.displayPost; - this.displayPost = this._curPost; + this.displayPost = currentPost; - if (!forceupdate && this._curPost.Equals(oldDisplayPost)) + if (!forceupdate && currentPost.Equals(oldDisplayPost)) return; var loadTasks = new List { - this.tweetDetailsView.ShowPostDetails(this._curPost), + this.tweetDetailsView.ShowPostDetails(currentPost), }; this.SplitContainer3.Panel2Collapsed = true; @@ -5742,7 +5669,7 @@ namespace OpenTween oldTokenSource?.Cancel(); var token = this.thumbnailTokenSource.Token; - loadTasks.Add(this.tweetThumbnail1.ShowThumbnailAsync(_curPost, token)); + loadTasks.Add(this.tweetThumbnail1.ShowThumbnailAsync(currentPost, token)); } try @@ -5753,47 +5680,39 @@ namespace OpenTween } private async void MatomeMenuItem_Click(object sender, EventArgs e) - { - await this.OpenApplicationWebsite(); - } + => await this.OpenApplicationWebsite(); private async Task OpenApplicationWebsite() - { - await this.OpenUriInBrowserAsync(ApplicationSettings.WebsiteUrl); - } + => await this.OpenUriInBrowserAsync(ApplicationSettings.WebsiteUrl); private async void ShortcutKeyListMenuItem_Click(object sender, EventArgs e) - { - await this.OpenUriInBrowserAsync(ApplicationSettings.ShortcutKeyUrl); - } + => await this.OpenUriInBrowserAsync(ApplicationSettings.ShortcutKeyUrl); private async void ListTab_KeyDown(object sender, KeyEventArgs e) { - if (ListTab.SelectedTab != null) + var tab = this.CurrentTab; + if (tab.TabType == MyCommon.TabUsageType.PublicSearch) { - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch) - { - Control pnl = ListTab.SelectedTab.Controls["panelSearch"]; - if (pnl.Controls["comboSearch"].Focused || - pnl.Controls["comboLang"].Focused || - pnl.Controls["buttonSearch"].Focused) return; - } - - if (e.Control || e.Shift || e.Alt) - this._anchorFlag = false; + Control pnl = this.CurrentTabPage.Controls["panelSearch"]; + if (pnl.Controls["comboSearch"].Focused || + pnl.Controls["comboLang"].Focused || + pnl.Controls["buttonSearch"].Focused) return; + } - if (CommonKeyDown(e.KeyData, FocusedControl.ListTab, out var asyncTask)) - { - e.Handled = true; - e.SuppressKeyPress = true; - } + if (e.Control || e.Shift || e.Alt) + this._anchorFlag = false; - if (asyncTask != null) - await asyncTask; + if (CommonKeyDown(e.KeyData, FocusedControl.ListTab, out var asyncTask)) + { + e.Handled = true; + e.SuppressKeyPress = true; } + + if (asyncTask != null) + await asyncTask; } - private ShortcutCommand[] shortcutCommands = new ShortcutCommand[0]; + private ShortcutCommand[] shortcutCommands = Array.Empty(); private void InitializeShortcuts() { @@ -5826,10 +5745,10 @@ namespace OpenTween .Do(() => this.DoRefresh()), ShortcutCommand.Create(Keys.F6) - .Do(() => this.GetReplyAsync()), + .Do(() => this.RefreshTabAsync()), ShortcutCommand.Create(Keys.F7) - .Do(() => this.GetDirectMessagesAsync()), + .Do(() => this.RefreshTabAsync()), ShortcutCommand.Create(Keys.Space, Keys.ProcessKey) .NotFocusedOn(FocusedControl.StatusText) @@ -5898,15 +5817,12 @@ namespace OpenTween .FocusedOn(FocusedControl.ListTab) .Do(() => { this._anchorFlag = false; - if (ListTab.SelectedTab != null) + var tab = this.CurrentTab; + var tabtype = tab.TabType; + if (tabtype == MyCommon.TabUsageType.Related || tabtype == MyCommon.TabUsageType.UserTimeline || tabtype == MyCommon.TabUsageType.PublicSearch || tabtype == MyCommon.TabUsageType.SearchResults) { - var tabtype = _statuses.Tabs[ListTab.SelectedTab.Text].TabType; - if (tabtype == MyCommon.TabUsageType.Related || tabtype == MyCommon.TabUsageType.UserTimeline || tabtype == MyCommon.TabUsageType.PublicSearch || tabtype == MyCommon.TabUsageType.SearchResults) - { - var relTp = ListTab.SelectedTab; - RemoveSpecifiedTab(relTp.Text, false); - SaveConfigsTabs(); - } + RemoveSpecifiedTab(tab.TabName, false); + SaveConfigsTabs(); } }), @@ -6040,45 +5956,11 @@ namespace OpenTween ShortcutCommand.Create(Keys.Control | Keys.Up) .FocusedOn(FocusedControl.StatusText) - .Do(() => { - if (!string.IsNullOrWhiteSpace(StatusText.Text)) - { - var inReplyToStatusId = this.inReplyTo?.Item1; - var inReplyToScreenName = this.inReplyTo?.Item2; - _history[_hisIdx] = new PostingStatus(StatusText.Text, inReplyToStatusId, inReplyToScreenName); - } - _hisIdx -= 1; - if (_hisIdx < 0) _hisIdx = 0; - - var historyItem = this._history[this._hisIdx]; - StatusText.Text = historyItem.status; - StatusText.SelectionStart = StatusText.Text.Length; - if (historyItem.inReplyToId != null) - this.inReplyTo = Tuple.Create(historyItem.inReplyToId.Value, historyItem.inReplyToName); - else - this.inReplyTo = null; - }), + .Do(() => this.StatusTextHistoryBack()), ShortcutCommand.Create(Keys.Control | Keys.Down) .FocusedOn(FocusedControl.StatusText) - .Do(() => { - if (!string.IsNullOrWhiteSpace(StatusText.Text)) - { - var inReplyToStatusId = this.inReplyTo?.Item1; - var inReplyToScreenName = this.inReplyTo?.Item2; - _history[_hisIdx] = new PostingStatus(StatusText.Text, inReplyToStatusId, inReplyToScreenName); - } - _hisIdx += 1; - if (_hisIdx > _history.Count - 1) _hisIdx = _history.Count - 1; - - var historyItem = this._history[this._hisIdx]; - StatusText.Text = historyItem.status; - StatusText.SelectionStart = StatusText.Text.Length; - if (historyItem.inReplyToId != null) - this.inReplyTo = Tuple.Create(historyItem.inReplyToId.Value, historyItem.inReplyToName); - else - this.inReplyTo = null; - }), + .Do(() => this.StatusTextHistoryForward()), ShortcutCommand.Create(Keys.Control | Keys.PageUp, Keys.Control | Keys.P) .FocusedOn(FocusedControl.StatusText) @@ -6111,8 +5993,10 @@ namespace OpenTween ShortcutCommand.Create(Keys.Control | Keys.Y) .FocusedOn(FocusedControl.PostBrowser) .Do(() => { - MultiLineMenuItem.Checked = !MultiLineMenuItem.Checked; - MultiLineMenuItem_Click(null, null); + var multiline = !SettingManager.Local.StatusMultiline; + SettingManager.Local.StatusMultiline = multiline; + MultiLineMenuItem.Checked = multiline; + MultiLineMenuItem_Click(this.MultiLineMenuItem, EventArgs.Empty); }), ShortcutCommand.Create(Keys.Shift | Keys.F3) @@ -6122,10 +6006,10 @@ namespace OpenTween .Do(() => this.DoRefreshMore()), ShortcutCommand.Create(Keys.Shift | Keys.F6) - .Do(() => this.GetReplyAsync(loadMore: true)), + .Do(() => this.RefreshTabAsync(backward: true)), ShortcutCommand.Create(Keys.Shift | Keys.F7) - .Do(() => this.GetDirectMessagesAsync(loadMore: true)), + .Do(() => this.RefreshTabAsync(backward: true)), ShortcutCommand.Create(Keys.Shift | Keys.R) .NotFocusedOn(FocusedControl.StatusText) @@ -6177,8 +6061,8 @@ namespace OpenTween .Do(() => this.doReTweetOfficial(isConfirm: true)), ShortcutCommand.Create(Keys.Alt | Keys.P) - .OnlyWhen(() => this._curPost != null) - .Do(() => this.doShowUserStatus(_curPost.ScreenName, ShowInputDialog: false)), + .OnlyWhen(() => this.CurrentPost != null) + .Do(() => this.doShowUserStatus(this.CurrentPost.ScreenName, ShowInputDialog: false)), ShortcutCommand.Create(Keys.Alt | Keys.Up) .Do(() => this.tweetDetailsView.ScrollDownPostBrowser(forward: false)), @@ -6208,9 +6092,8 @@ namespace OpenTween .Do(() => this.CopyIdUri()), ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.F) - .OnlyWhen(() => this.ListTab.SelectedTab != null && - this._statuses.Tabs[this.ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch) - .Do(() => this.ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus()), + .OnlyWhen(() => this.CurrentTab.TabType == MyCommon.TabUsageType.PublicSearch) + .Do(() => this.CurrentTabPage.Controls["panelSearch"].Controls["comboSearch"].Focus()), ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.S) .Do(() => this.FavoriteChange(FavAdd: false)), @@ -6227,30 +6110,31 @@ namespace OpenTween ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.H) .Do(() => this.doMoveToRTHome()), - ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.O) - .Do(() => this.FavorareMenuItem_Click(null, null)), - ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.Up) .FocusedOn(FocusedControl.StatusText) .Do(() => { - if (_curList != null && _curList.VirtualListSize != 0 && - _curList.SelectedIndices.Count > 0 && _curList.SelectedIndices[0] > 0) + var tab = this.CurrentTab; + var selectedIndex = tab.SelectedIndex; + if (selectedIndex != -1 && selectedIndex > 0) { - var idx = _curList.SelectedIndices[0] - 1; - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + var idx = selectedIndex - 1; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); } }), ShortcutCommand.Create(Keys.Control | Keys.Shift | Keys.Down) .FocusedOn(FocusedControl.StatusText) .Do(() => { - if (_curList != null && _curList.VirtualListSize != 0 && _curList.SelectedIndices.Count > 0 - && _curList.SelectedIndices[0] < _curList.VirtualListSize - 1) + var tab = this.CurrentTab; + var selectedIndex = tab.SelectedIndex; + if (selectedIndex != -1 && selectedIndex < tab.AllCount - 1) { - var idx = _curList.SelectedIndices[0] + 1; - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + var idx = selectedIndex + 1; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); } }), @@ -6326,12 +6210,15 @@ namespace OpenTween .Do(() => this.SetSortLastColumn()), ShortcutCommand.Create(Keys.Control | Keys.Alt | Keys.S) + .FocusedOn(FocusedControl.ListTab) .Do(() => this.FavoritesRetweetOfficial()), ShortcutCommand.Create(Keys.Control | Keys.Alt | Keys.R) + .FocusedOn(FocusedControl.ListTab) .Do(() => this.FavoritesRetweetUnofficial()), ShortcutCommand.Create(Keys.Control | Keys.Alt | Keys.H) + .FocusedOn(FocusedControl.ListTab) .Do(() => this.OpenUserAppointUrl()), ShortcutCommand.Create(Keys.Alt | Keys.Shift | Keys.R) @@ -6399,12 +6286,11 @@ namespace OpenTween { string clstr = ""; StringBuilder sb = new StringBuilder(); + var tab = this.CurrentTab; bool IsProtected = false; - bool isDm = false; - if (this._curTab != null && this._statuses.GetTabByName(this._curTab.Text) != null) isDm = this._statuses.GetTabByName(this._curTab.Text).TabType == MyCommon.TabUsageType.DirectMessage; - foreach (int idx in _curList.SelectedIndices) + var isDm = tab.TabType == MyCommon.TabUsageType.DirectMessage; + foreach (var post in tab.SelectedPosts) { - PostClass post = _statuses.Tabs[_curTab.Text][idx]; if (post.IsDeleted) continue; if (!isDm) { @@ -6438,19 +6324,13 @@ namespace OpenTween private void CopyIdUri() { - if (this._curTab == null) - return; - - var tab = this._statuses.GetTabByName(this._curTab.Text); + var tab = this.CurrentTab; if (tab == null || tab is DirectMessagesTabModel) return; var copyUrls = new List(); - foreach (int idx in _curList.SelectedIndices) - { - var post = tab[idx]; + foreach (var post in tab.SelectedPosts) copyUrls.Add(MyCommon.GetStatusUrl(post)); - } if (copyUrls.Count == 0) return; @@ -6467,34 +6347,39 @@ namespace OpenTween private void GoFav(bool forward) { - if (_curList.VirtualListSize == 0) return; + var tab = this.CurrentTab; + if (tab.AllCount == 0) + return; + + var selectedIndex = tab.SelectedIndex; + int fIdx = 0; int toIdx = 0; int stp = 1; if (forward) { - if (_curList.SelectedIndices.Count == 0) + if (selectedIndex == -1) { fIdx = 0; } else { - fIdx = _curList.SelectedIndices[0] + 1; - if (fIdx > _curList.VirtualListSize - 1) return; + fIdx = selectedIndex + 1; + if (fIdx > tab.AllCount - 1) return; } - toIdx = _curList.VirtualListSize; + toIdx = tab.AllCount; stp = 1; } else { - if (_curList.SelectedIndices.Count == 0) + if (selectedIndex == -1) { - fIdx = _curList.VirtualListSize - 1; + fIdx = tab.AllCount - 1; } else { - fIdx = _curList.SelectedIndices[0] - 1; + fIdx = selectedIndex - 1; if (fIdx < 0) return; } toIdx = -1; @@ -6503,10 +6388,11 @@ namespace OpenTween for (int idx = fIdx; idx != toIdx; idx += stp) { - if (_statuses.Tabs[_curTab.Text][idx].IsFav) + if (tab[idx].IsFav) { - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); break; } } @@ -6514,17 +6400,15 @@ namespace OpenTween private void GoSamePostToAnotherTab(bool left) { - if (this._curList.SelectedIndices.Count == 0) - return; - - var tab = this._statuses.Tabs[this._curTab.Text]; + var tab = this.CurrentTab; // Directタブは対象外(見つかるはずがない) if (tab.TabType == MyCommon.TabUsageType.DirectMessage) return; - var selectedIndex = this._curList.SelectedIndices[0]; - var selectedStatusId = tab.GetStatusIdAt(selectedIndex); + var selectedStatusId = tab.SelectedStatusId; + if (selectedStatusId == -1) + return; int fIdx, toIdx, stp; @@ -6569,8 +6453,9 @@ namespace OpenTween if (foundIndex != -1) { ListTab.SelectedIndex = tabidx; - SelectListItem(_curList, foundIndex); - _curList.EnsureVisible(foundIndex); + var listView = this.CurrentListView; + SelectListItem(listView, foundIndex); + listView.EnsureVisible(foundIndex); return; } } @@ -6578,11 +6463,13 @@ namespace OpenTween private void GoPost(bool forward) { - if (_curList.SelectedIndices.Count == 0 || _curPost == null) + var tab = this.CurrentTab; + var currentPost = this.CurrentPost; + + if (currentPost == null) return; - var tab = this._statuses.Tabs[this._curTab.Text]; - var selectedIndex = this._curList.SelectedIndices[0]; + var selectedIndex = tab.SelectedIndex; int fIdx, toIdx, stp; @@ -6602,13 +6489,13 @@ namespace OpenTween } string name = ""; - if (_curPost.RetweetedId == null) + if (currentPost.RetweetedId == null) { - name = _curPost.ScreenName; + name = currentPost.ScreenName; } else { - name = _curPost.RetweetedBy; + name = currentPost.RetweetedBy; } for (int idx = fIdx; idx != toIdx; idx += stp) { @@ -6617,8 +6504,9 @@ namespace OpenTween { if (post.ScreenName == name) { - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); break; } } @@ -6626,8 +6514,9 @@ namespace OpenTween { if (post.RetweetedBy == name) { - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); break; } } @@ -6636,11 +6525,11 @@ namespace OpenTween private void GoRelPost(bool forward) { - if (this._curList.SelectedIndices.Count == 0) - return; + var tab = this.CurrentTab; + var selectedIndex = tab.SelectedIndex; - var tab = this._statuses.Tabs[this._curTab.Text]; - var selectedIndex = this._curList.SelectedIndices[0]; + if (selectedIndex == -1) + return; int fIdx, toIdx, stp; @@ -6661,8 +6550,9 @@ namespace OpenTween if (!_anchorFlag) { - if (_curPost == null) return; - _anchorPost = _curPost; + var currentPost = this.CurrentPost; + if (currentPost == null) return; + _anchorPost = currentPost; _anchorFlag = true; } else @@ -6677,13 +6567,14 @@ namespace OpenTween post.RetweetedBy == _anchorPost.ScreenName || post.ScreenName == _anchorPost.RetweetedBy || (!string.IsNullOrEmpty(post.RetweetedBy) && post.RetweetedBy == _anchorPost.RetweetedBy) || - _anchorPost.ReplyToList.Contains(post.ScreenName.ToLowerInvariant()) || - _anchorPost.ReplyToList.Contains(post.RetweetedBy.ToLowerInvariant()) || - post.ReplyToList.Contains(_anchorPost.ScreenName.ToLowerInvariant()) || - post.ReplyToList.Contains(_anchorPost.RetweetedBy.ToLowerInvariant())) - { - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + _anchorPost.ReplyToList.Any(x => x.UserId == post.UserId) || + _anchorPost.ReplyToList.Any(x => x.UserId == post.RetweetedByUserId) || + post.ReplyToList.Any(x => x.UserId == _anchorPost.UserId) || + post.ReplyToList.Any(x => x.UserId == _anchorPost.RetweetedByUserId)) + { + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); break; } } @@ -6692,16 +6583,18 @@ namespace OpenTween private void GoAnchor() { if (_anchorPost == null) return; - int idx = _statuses.Tabs[_curTab.Text].IndexOf(_anchorPost.StatusId); + int idx = this.CurrentTab.IndexOf(_anchorPost.StatusId); if (idx == -1) return; - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); } private void GoTopEnd(bool GoTop) { - if (_curList.VirtualListSize == 0) + var listView = this.CurrentListView; + if (listView.VirtualListSize == 0) return; ListViewItem _item; @@ -6709,7 +6602,7 @@ namespace OpenTween if (GoTop) { - _item = _curList.GetItemAt(0, 25); + _item = listView.GetItemAt(0, 25); if (_item == null) idx = 0; else @@ -6717,18 +6610,19 @@ namespace OpenTween } else { - _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1); + _item = listView.GetItemAt(0, listView.ClientSize.Height - 1); if (_item == null) - idx = _curList.VirtualListSize - 1; + idx = listView.VirtualListSize - 1; else idx = _item.Index; } - SelectListItem(_curList, idx); + SelectListItem(listView, idx); } private void GoMiddle() { - if (_curList.VirtualListSize == 0) + var listView = this.CurrentListView; + if (listView.VirtualListSize == 0) return; ListViewItem _item; @@ -6736,7 +6630,7 @@ namespace OpenTween int idx2; int idx3; - _item = _curList.GetItemAt(0, 0); + _item = listView.GetItemAt(0, 0); if (_item == null) { idx1 = 0; @@ -6746,10 +6640,10 @@ namespace OpenTween idx1 = _item.Index; } - _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1); + _item = listView.GetItemAt(0, listView.ClientSize.Height - 1); if (_item == null) { - idx2 = _curList.VirtualListSize - 1; + idx2 = listView.VirtualListSize - 1; } else { @@ -6757,57 +6651,63 @@ namespace OpenTween } idx3 = (idx1 + idx2) / 2; - SelectListItem(_curList, idx3); + SelectListItem(listView, idx3); } private void GoLast() { - if (_curList.VirtualListSize == 0) return; + var listView = this.CurrentListView; + if (listView.VirtualListSize == 0) return; if (_statuses.SortOrder == SortOrder.Ascending) { - SelectListItem(_curList, _curList.VirtualListSize - 1); - _curList.EnsureVisible(_curList.VirtualListSize - 1); + SelectListItem(listView, listView.VirtualListSize - 1); + listView.EnsureVisible(listView.VirtualListSize - 1); } else { - SelectListItem(_curList, 0); - _curList.EnsureVisible(0); + SelectListItem(listView, 0); + listView.EnsureVisible(0); } } private void MoveTop() { - if (_curList.SelectedIndices.Count == 0) return; - int idx = _curList.SelectedIndices[0]; + var listView = this.CurrentListView; + if (listView.SelectedIndices.Count == 0) return; + int idx = listView.SelectedIndices[0]; if (_statuses.SortOrder == SortOrder.Ascending) { - _curList.EnsureVisible(_curList.VirtualListSize - 1); + listView.EnsureVisible(listView.VirtualListSize - 1); } else { - _curList.EnsureVisible(0); + listView.EnsureVisible(0); } - _curList.EnsureVisible(idx); + listView.EnsureVisible(idx); } private async Task GoInReplyToPostTree() { - if (_curPost == null) return; + var curTabClass = this.CurrentTab; + var currentPost = this.CurrentPost; - TabModel curTabClass = _statuses.Tabs[_curTab.Text]; + if (currentPost == null) + return; - if (curTabClass.TabType == MyCommon.TabUsageType.PublicSearch && _curPost.InReplyToStatusId == null && _curPost.TextFromApi.Contains("@")) + if (curTabClass.TabType == MyCommon.TabUsageType.PublicSearch && currentPost.InReplyToStatusId == null && currentPost.TextFromApi.Contains("@")) { try { - var post = await tw.GetStatusApi(false, _curPost.StatusId); + var post = await tw.GetStatusApi(false, currentPost.StatusId); - _curPost.InReplyToStatusId = post.InReplyToStatusId; - _curPost.InReplyToUser = post.InReplyToUser; - _curPost.IsReply = post.IsReply; + currentPost.InReplyToStatusId = post.InReplyToStatusId; + currentPost.InReplyToUser = post.InReplyToUser; + currentPost.IsReply = post.IsReply; this.PurgeListViewItemCache(); - _curList.RedrawItems(_curItemIndex, _curItemIndex, false); + + var index = curTabClass.SelectedIndex; + this.CurrentListView.RedrawItems(index, index, false); } catch (WebApiException ex) { @@ -6815,21 +6715,20 @@ namespace OpenTween } } - if (!(this.ExistCurrentPost && _curPost.InReplyToUser != null && _curPost.InReplyToStatusId != null)) return; + if (!(this.ExistCurrentPost && currentPost.InReplyToUser != null && currentPost.InReplyToStatusId != null)) return; - if (replyChains == null || (replyChains.Count > 0 && replyChains.Peek().InReplyToId != _curPost.StatusId)) + if (replyChains == null || (replyChains.Count > 0 && replyChains.Peek().InReplyToId != currentPost.StatusId)) { replyChains = new Stack(); } - replyChains.Push(new ReplyChain(_curPost.StatusId, _curPost.InReplyToStatusId.Value, _curTab)); + replyChains.Push(new ReplyChain(currentPost.StatusId, currentPost.InReplyToStatusId.Value, curTabClass)); int inReplyToIndex; string inReplyToTabName; - long inReplyToId = _curPost.InReplyToStatusId.Value; - string inReplyToUser = _curPost.InReplyToUser; - //Dictionary curTabPosts = curTabClass.Posts; + var inReplyToId = currentPost.InReplyToStatusId.Value; + var inReplyToUser = currentPost.InReplyToUser; - var inReplyToPosts = from tab in _statuses.Tabs.Values + var inReplyToPosts = from tab in _statuses.Tabs orderby tab != curTabClass from post in tab.Posts.Values where post.StatusId == inReplyToId @@ -6844,7 +6743,7 @@ namespace OpenTween { await Task.Run(async () => { - var post = await tw.GetStatusApi(false, _curPost.InReplyToStatusId.Value) + var post = await tw.GetStatusApi(false, currentPost.InReplyToStatusId.Value) .ConfigureAwait(false); post.IsRead = true; @@ -6874,7 +6773,7 @@ namespace OpenTween TabPage tabPage = this.ListTab.TabPages.Cast().First((tp) => { return tp.Text == inReplyToTabName; }); DetailsListView listView = (DetailsListView)tabPage.Tag; - if (_curTab != tabPage) + if (this.CurrentTabName != inReplyToTabName) { this.ListTab.SelectTab(tabPage); } @@ -6885,23 +6784,24 @@ namespace OpenTween private void GoBackInReplyToPostTree(bool parallel = false, bool isForward = true) { - if (_curPost == null) return; + var curTabClass = this.CurrentTab; + var currentPost = this.CurrentPost; - TabModel curTabClass = _statuses.Tabs[_curTab.Text]; - //Dictionary curTabPosts = curTabClass.Posts; + if (currentPost == null) + return; if (parallel) { - if (_curPost.InReplyToStatusId != null) + if (currentPost.InReplyToStatusId != null) { var posts = from t in _statuses.Tabs - from p in t.Value.Posts - where p.Value.StatusId != _curPost.StatusId && p.Value.InReplyToStatusId == _curPost.InReplyToStatusId - let indexOf = t.Value.IndexOf(p.Value.StatusId) + from p in t.Posts + where p.Value.StatusId != currentPost.StatusId && p.Value.InReplyToStatusId == currentPost.InReplyToStatusId + let indexOf = t.IndexOf(p.Value.StatusId) where indexOf > -1 orderby isForward ? indexOf : indexOf * -1 - orderby t.Value != curTabClass - select new {Tab = t.Value, Post = p.Value, Index = indexOf}; + orderby t != curTabClass + select new {Tab = t, Post = p.Value, Index = indexOf}; try { var postList = posts.ToList(); @@ -6913,11 +6813,12 @@ namespace OpenTween postList.RemoveAt(index); } } - var post = postList.FirstOrDefault((pst) => { return pst.Tab == curTabClass && isForward ? pst.Index > _curItemIndex : pst.Index < _curItemIndex; }); + var currentIndex = this.CurrentTab.SelectedIndex; + var post = postList.FirstOrDefault((pst) => { return pst.Tab == curTabClass && isForward ? pst.Index > currentIndex : pst.Index < currentIndex; }); if (post == null) post = postList.FirstOrDefault((pst) => { return pst.Tab != curTabClass; }); if (post == null) post = postList.First(); this.ListTab.SelectTab(this.ListTab.TabPages.Cast().First((tp) => { return tp.Text == post.Tab.TabName; })); - DetailsListView listView = (DetailsListView)this.ListTab.SelectedTab.Tag; + var listView = this.CurrentListView; SelectListItem(listView, post.Index); listView.EnsureVisible(post.Index); } @@ -6932,18 +6833,18 @@ namespace OpenTween if (replyChains == null || replyChains.Count < 1) { var posts = from t in _statuses.Tabs - from p in t.Value.Posts - where p.Value.InReplyToStatusId == _curPost.StatusId - let indexOf = t.Value.IndexOf(p.Value.StatusId) + from p in t.Posts + where p.Value.InReplyToStatusId == currentPost.StatusId + let indexOf = t.IndexOf(p.Value.StatusId) where indexOf > -1 orderby indexOf - orderby t.Value != curTabClass - select new {Tab = t.Value, Index = indexOf}; + orderby t != curTabClass + select new {Tab = t, Index = indexOf}; try { var post = posts.First(); this.ListTab.SelectTab(this.ListTab.TabPages.Cast().First((tp) => { return tp.Text == post.Tab.TabName; })); - DetailsListView listView = (DetailsListView)this.ListTab.SelectedTab.Tag; + var listView = this.CurrentListView; SelectListItem(listView, post.Index); listView.EnsureVisible(post.Index); } @@ -6955,25 +6856,35 @@ namespace OpenTween else { ReplyChain chainHead = replyChains.Pop(); - if (chainHead.InReplyToId == _curPost.StatusId) + if (chainHead.InReplyToId == currentPost.StatusId) { - int idx = _statuses.Tabs[chainHead.OriginalTab.Text].IndexOf(chainHead.OriginalId); - if (idx == -1) + var tab = chainHead.OriginalTab; + if (!this._statuses.Tabs.Contains(tab)) { replyChains = null; } else { - try + var idx = tab.IndexOf(chainHead.OriginalId); + if (idx == -1) { - ListTab.SelectTab(chainHead.OriginalTab); + replyChains = null; } - catch (Exception) + else { - replyChains = null; + var tabPage = this.ListTab.TabPages.Cast().First(x => x.Text == tab.TabName); + try + { + ListTab.SelectTab(tabPage); + } + catch (Exception) + { + replyChains = null; + } + var listView = this.CurrentListView; + SelectListItem(listView, idx); + listView.EnsureVisible(idx); } - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); } } else @@ -6990,24 +6901,25 @@ namespace OpenTween if (this.selectPostChains.Count > 1) { var idx = -1; - TabPage tp = null; + TabModel foundTab = null; do { try { this.selectPostChains.Pop(); - var (tabPage, post) = this.selectPostChains.Peek(); + var (tab, post) = this.selectPostChains.Peek(); - if (!this.ListTab.TabPages.Contains(tabPage)) continue; //該当タブが存在しないので無視 + if (!this._statuses.Tabs.Contains(tab)) + continue; // 該当タブが存在しないので無視 if (post != null) { - idx = this._statuses.Tabs[tabPage.Text].IndexOf(post.StatusId); + idx = tab.IndexOf(post.StatusId); if (idx == -1) continue; //該当ポストが存在しないので無視 } - tp = tabPage; + foundTab = tab; this.selectPostChains.Pop(); } @@ -7019,7 +6931,7 @@ namespace OpenTween } while (this.selectPostChains.Count > 1); - if (tp == null) + if (foundTab == null) { //状態がおかしいので処理を中断 //履歴が残り1つであればクリアしておく @@ -7028,8 +6940,10 @@ namespace OpenTween return; } - DetailsListView lst = (DetailsListView)tp.Tag; - this.ListTab.SelectedTab = tp; + var tabPage = this.ListTab.TabPages.Cast().First(x => x.Text == foundTab.TabName); + var lst = (DetailsListView)tabPage.Tag; + this.ListTab.SelectedTab = tabPage; + if (idx > -1) { SelectListItem(lst, idx); @@ -7041,24 +6955,27 @@ namespace OpenTween private void PushSelectPostChain() { + var currentTab = this.CurrentTab; + var currentPost = this.CurrentPost; + int count = this.selectPostChains.Count; if (count > 0) { - var (tabPage, post) = this.selectPostChains.Peek(); - if (tabPage == this._curTab) + var (tab, post) = this.selectPostChains.Peek(); + if (tab == currentTab) { - if (post == this._curPost) return; //最新の履歴と同一 + if (post == currentPost) return; //最新の履歴と同一 if (post == null) this.selectPostChains.Pop(); //置き換えるため削除 } } if (count >= 2500) TrimPostChain(); - this.selectPostChains.Push((this._curTab, this._curPost)); + this.selectPostChains.Push((currentTab, currentPost)); } private void TrimPostChain() { if (this.selectPostChains.Count <= 2000) return; - var p = new Stack>(2000); + var p = new Stack<(TabModel, PostClass)>(2000); for (int i = 0; i < 2000; i++) { p.Push(this.selectPostChains.Pop()); @@ -7073,41 +6990,49 @@ namespace OpenTween private bool GoStatus(long statusId) { if (statusId == 0) return false; - for (int tabidx = 0; tabidx < ListTab.TabCount; tabidx++) - { - if (_statuses.Tabs[ListTab.TabPages[tabidx].Text].TabType != MyCommon.TabUsageType.DirectMessage && _statuses.Tabs[ListTab.TabPages[tabidx].Text].Contains(statusId)) - { - int idx = _statuses.Tabs[ListTab.TabPages[tabidx].Text].IndexOf(statusId); - ListTab.SelectedIndex = tabidx; - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); - return true; - } - } - return false; + + var tab = this._statuses.Tabs + .Where(x => x.TabType != MyCommon.TabUsageType.DirectMessage) + .Where(x => x.Contains(statusId)) + .FirstOrDefault(); + + if (tab == null) + return false; + + var index = tab.IndexOf(statusId); + + var tabPage = this.ListTab.TabPages.Cast().First(x => x.Text == tab.TabName); + this.ListTab.SelectedTab = tabPage; + + var listView = this.CurrentListView; + this.SelectListItem(listView, index); + listView.EnsureVisible(index); + + return true; } private bool GoDirectMessage(long statusId) { if (statusId == 0) return false; - for (int tabidx = 0; tabidx < ListTab.TabCount; tabidx++) - { - if (_statuses.Tabs[ListTab.TabPages[tabidx].Text].TabType == MyCommon.TabUsageType.DirectMessage && _statuses.Tabs[ListTab.TabPages[tabidx].Text].Contains(statusId)) - { - int idx = _statuses.Tabs[ListTab.TabPages[tabidx].Text].IndexOf(statusId); - ListTab.SelectedIndex = tabidx; - SelectListItem(_curList, idx); - _curList.EnsureVisible(idx); - return true; - } - } - return false; + + var tab = this._statuses.GetTabByType(); + var index = tab.IndexOf(statusId); + + if (index == -1) + return false; + + var tabPage = this.ListTab.TabPages.Cast().First(x => x.Text == tab.TabName); + this.ListTab.SelectedTab = tabPage; + + var listView = this.CurrentListView; + this.SelectListItem(listView, index); + listView.EnsureVisible(index); + + return true; } private void MyList_MouseClick(object sender, MouseEventArgs e) - { - _anchorFlag = false; - } + => this._anchorFlag = false; private void StatusText_Enter(object sender, EventArgs e) { @@ -7180,13 +7105,6 @@ namespace OpenTween SettingManager.Common.UserId = tw.UserId; SettingManager.Common.Token = tw.AccessToken; SettingManager.Common.TokenSecret = tw.AccessTokenSecret; - - if (IdeographicSpaceToSpaceToolStripMenuItem != null && - IdeographicSpaceToSpaceToolStripMenuItem.IsDisposed == false) - { - SettingManager.Common.WideSpaceConvert = this.IdeographicSpaceToSpaceToolStripMenuItem.Checked; - } - SettingManager.Common.SortOrder = (int)_statuses.SortOrder; switch (_statuses.SortMode) { @@ -7219,11 +7137,6 @@ namespace OpenTween SettingManager.Common.HashIsHead = HashMgr.IsHead; SettingManager.Common.HashIsPermanent = HashMgr.IsPermanent; SettingManager.Common.HashIsNotAddToAtReply = HashMgr.IsNotAddToAtReply; - if (ToolStripFocusLockMenuItem != null && - ToolStripFocusLockMenuItem.IsDisposed == false) - { - SettingManager.Common.FocusLockToStatusText = this.ToolStripFocusLockMenuItem.Checked; - } SettingManager.Common.TrackWord = tw.TrackWord; SettingManager.Common.AllAtReply = tw.AllAtReply; SettingManager.Common.UseImageService = ImageSelector.ServiceIndex; @@ -7280,7 +7193,7 @@ namespace OpenTween var tabs = this.ListTab.TabPages.Cast() .Select(x => this._statuses.Tabs[x.Text]) - .Concat(new[] { this._statuses.GetTabByType(MyCommon.TabUsageType.Mute) }); + .Append(this._statuses.MuteTab); foreach (var tab in tabs) { @@ -7342,18 +7255,20 @@ namespace OpenTween } catch (TabException ex) { - MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, ex.Message, ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } private void SaveLogMenuItem_Click(object sender, EventArgs e) { + var tab = this.CurrentTab; + DialogResult rslt = MessageBox.Show(string.Format(Properties.Resources.SaveLogMenuItem_ClickText1, Environment.NewLine), Properties.Resources.SaveLogMenuItem_ClickText2, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (rslt == DialogResult.Cancel) return; - SaveFileDialog1.FileName = MyCommon.GetAssemblyName() + "Posts" + DateTime.Now.ToString("yyMMdd-HHmmss") + ".tsv"; + SaveFileDialog1.FileName = $"{ApplicationSettings.AssemblyName}Posts{DateTimeUtc.Now.ToLocalTime():yyMMdd-HHmmss}.tsv"; SaveFileDialog1.InitialDirectory = Application.ExecutablePath; SaveFileDialog1.Filter = Properties.Resources.SaveLogMenuItem_ClickText3; SaveFileDialog1.FilterIndex = 0; @@ -7368,14 +7283,14 @@ namespace OpenTween if (rslt == DialogResult.Yes) { //All - for (int idx = 0; idx < _curList.VirtualListSize; idx++) + for (int idx = 0; idx < tab.AllCount; idx++) { - PostClass post = _statuses.Tabs[_curTab.Text][idx]; + var post = tab[idx]; string protect = ""; if (post.IsProtect) protect = "Protect"; sw.WriteLine(post.Nickname + "\t" + "\"" + post.TextFromApi.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" + - post.CreatedAt + "\t" + + post.CreatedAt.ToLocalTimeString() + "\t" + post.ScreenName + "\t" + post.StatusId + "\t" + post.ImageUrl + "\t" + @@ -7385,14 +7300,13 @@ namespace OpenTween } else { - foreach (int idx in _curList.SelectedIndices) + foreach (var post in this.CurrentTab.SelectedPosts) { - PostClass post = _statuses.Tabs[_curTab.Text][idx]; string protect = ""; if (post.IsProtect) protect = "Protect"; sw.WriteLine(post.Nickname + "\t" + "\"" + post.TextFromApi.Replace("\n", "").Replace("\"", "\"\"") + "\"" + "\t" + - post.CreatedAt + "\t" + + post.CreatedAt.ToLocalTimeString() + "\t" + post.ScreenName + "\t" + post.StatusId + "\t" + post.ImageUrl + "\t" + @@ -7467,9 +7381,7 @@ namespace OpenTween } private void ListTab_DoubleClick(object sender, MouseEventArgs e) - { - TabRename(this.ListTab.SelectedTab.Text, out var _); - } + => this.TabRename(this.CurrentTabName, out var _); private void ListTab_MouseDown(object sender, MouseEventArgs e) { @@ -7552,16 +7464,20 @@ namespace OpenTween using (ControlTransaction.Layout(this.ListTab)) { - var mTp = this.ListTab.TabPages[targetIndex]; - this.ListTab.TabPages.Remove(mTp); + var tab = this._statuses.Tabs[targetIndex]; + var tabPage = this.ListTab.TabPages[targetIndex]; + + this.ListTab.TabPages.Remove(tabPage); if (targetIndex < baseIndex) baseIndex--; - if (isBeforeBaseTab) - ListTab.TabPages.Insert(baseIndex, mTp); - else - ListTab.TabPages.Insert(baseIndex + 1, mTp); + if (!isBeforeBaseTab) + baseIndex++; + + this._statuses.MoveTab(baseIndex, tab); + + ListTab.TabPages.Insert(baseIndex, tabPage); } SaveConfigsTabs(); @@ -7572,40 +7488,42 @@ namespace OpenTween //isAuto:true=先頭に挿入、false=カーソル位置に挿入 //isReply:true=@,false=DM if (!StatusText.Enabled) return; - if (_curList == null) return; - if (_curTab == null) return; if (!this.ExistCurrentPost) return; + var tab = this.CurrentTab; + var selectedPosts = tab.SelectedPosts; + // 複数あてリプライはReplyではなく通常ポスト //↑仕様変更で全部リプライ扱いでOK(先頭ドット付加しない) //090403暫定でドットを付加しないようにだけ修正。単独と複数の処理は統合できると思われる。 //090513 all @ replies 廃止の仕様変更によりドット付加に戻し(syo68k) - if (_curList.SelectedIndices.Count > 0) + if (selectedPosts.Length > 0) { // アイテムが1件以上選択されている - if (_curList.SelectedIndices.Count == 1 && !isAll && this.ExistCurrentPost) + if (selectedPosts.Length == 1 && !isAll && this.ExistCurrentPost) { + var post = selectedPosts.Single(); + // 単独ユーザー宛リプライまたはDM - if ((_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage && isAuto) || (!isAuto && !isReply)) + if ((tab.TabType == MyCommon.TabUsageType.DirectMessage && isAuto) || (!isAuto && !isReply)) { // ダイレクトメッセージ - StatusText.Text = "D " + _curPost.ScreenName + " " + StatusText.Text; + this.inReplyTo = null; + StatusText.Text = "D " + post.ScreenName + " " + StatusText.Text; StatusText.SelectionStart = StatusText.Text.Length; StatusText.Focus(); - this.inReplyTo = null; return; } if (string.IsNullOrEmpty(StatusText.Text)) { //空の場合 + var inReplyToStatusId = post.RetweetedId ?? post.StatusId; + var inReplyToScreenName = post.ScreenName; + this.inReplyTo = (inReplyToStatusId, inReplyToScreenName); // ステータステキストが入力されていない場合先頭に@ユーザー名を追加する - StatusText.Text = "@" + _curPost.ScreenName + " "; - - var inReplyToStatusId = this._curPost.RetweetedId ?? this._curPost.StatusId; - var inReplyToScreenName = this._curPost.ScreenName; - this.inReplyTo = Tuple.Create(inReplyToStatusId, inReplyToScreenName); + StatusText.Text = "@" + post.ScreenName + " "; } else { @@ -7614,14 +7532,14 @@ namespace OpenTween if (isAuto) { //1件選んでEnter or DoubleClick - if (StatusText.Text.Contains("@" + _curPost.ScreenName + " ")) + if (StatusText.Text.Contains("@" + post.ScreenName + " ")) { - if (this.inReplyTo?.Item2 == _curPost.ScreenName) + if (this.inReplyTo?.ScreenName == post.ScreenName) { //返信先書き換え - var inReplyToStatusId = this._curPost.RetweetedId ?? this._curPost.StatusId; - var inReplyToScreenName = this._curPost.ScreenName; - this.inReplyTo = Tuple.Create(inReplyToStatusId, inReplyToScreenName); + var inReplyToStatusId = post.RetweetedId ?? post.StatusId; + var inReplyToScreenName = post.ScreenName; + this.inReplyTo = (inReplyToStatusId, inReplyToScreenName); } return; } @@ -7631,32 +7549,31 @@ namespace OpenTween if (StatusText.Text.StartsWith(". ", StringComparison.Ordinal)) { // 複数リプライ - StatusText.Text = StatusText.Text.Insert(2, "@" + _curPost.ScreenName + " "); this.inReplyTo = null; + StatusText.Text = StatusText.Text.Insert(2, "@" + post.ScreenName + " "); } else { // 単独リプライ - StatusText.Text = "@" + _curPost.ScreenName + " " + StatusText.Text; - var inReplyToStatusId = this._curPost.RetweetedId ?? this._curPost.StatusId; - var inReplyToScreenName = this._curPost.ScreenName; - this.inReplyTo = Tuple.Create(inReplyToStatusId, inReplyToScreenName); + var inReplyToStatusId = post.RetweetedId ?? post.StatusId; + var inReplyToScreenName = post.ScreenName; + this.inReplyTo = (inReplyToStatusId, inReplyToScreenName); + StatusText.Text = "@" + post.ScreenName + " " + StatusText.Text; } } else { //文頭@ // 複数リプライ - StatusText.Text = ". @" + _curPost.ScreenName + " " + StatusText.Text; - //StatusText.Text = "@" + _curPost.ScreenName + " " + StatusText.Text; this.inReplyTo = null; + StatusText.Text = ". @" + post.ScreenName + " " + StatusText.Text; } } else { //1件選んでCtrl-Rの場合(返信先操作せず) int sidx = StatusText.SelectionStart; - string id = "@" + _curPost.ScreenName + " "; + string id = "@" + post.ScreenName + " "; if (sidx > 0) { if (StatusText.Text.Substring(sidx - 1, 1) != " ") @@ -7703,9 +7620,8 @@ namespace OpenTween sTxt = ". " + sTxt; this.inReplyTo = null; } - for (int cnt = 0; cnt < _curList.SelectedIndices.Count; cnt++) + foreach (var post in selectedPosts) { - PostClass post = _statuses.Tabs[_curTab.Text][_curList.SelectedIndices[cnt]]; if (!sTxt.Contains("@" + post.ScreenName + " ")) { sTxt = sTxt.Insert(2, "@" + post.ScreenName + " "); @@ -7717,32 +7633,31 @@ namespace OpenTween else { //C-S-r or C-r - if (_curList.SelectedIndices.Count > 1) + + if (selectedPosts.Length > 1) { //複数ポスト選択 string ids = ""; int sidx = StatusText.SelectionStart; - for (int cnt = 0; cnt < _curList.SelectedIndices.Count; cnt++) + foreach (var post in selectedPosts) { - PostClass post = _statuses.Tabs[_curTab.Text][_curList.SelectedIndices[cnt]]; - if (!ids.Contains("@" + post.ScreenName + " ") && - !post.ScreenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) + if (!ids.Contains("@" + post.ScreenName + " ") && post.UserId != tw.UserId) { ids += "@" + post.ScreenName + " "; } if (isAll) { - foreach (string nm in post.ReplyToList) + foreach (var (_, screenName) in post.ReplyToList) { - if (!ids.Contains("@" + nm + " ") && - !nm.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) + if (!ids.Contains("@" + screenName + " ") && + !screenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) { - Match m = Regex.Match(post.TextFromApi, "[@@](?" + nm + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase); + Match m = Regex.Match(post.TextFromApi, "[@@](?" + screenName + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase); if (m.Success) ids += "@" + m.Result("${id}") + " "; else - ids += "@" + nm + " "; + ids += "@" + screenName + " "; } } } @@ -7750,9 +7665,9 @@ namespace OpenTween if (ids.Length == 0) return; if (!StatusText.Text.StartsWith(". ", StringComparison.Ordinal)) { + this.inReplyTo = null; StatusText.Text = ". " + StatusText.Text; sidx += 2; - this.inReplyTo = null; } if (sidx > 0) { @@ -7783,28 +7698,26 @@ namespace OpenTween string ids = ""; int sidx = StatusText.SelectionStart; - PostClass post = _curPost; - if (!ids.Contains("@" + post.ScreenName + " ") && - !post.ScreenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) + var post = selectedPosts.Single(); + if (!ids.Contains("@" + post.ScreenName + " ") && post.UserId != tw.UserId) { ids += "@" + post.ScreenName + " "; } - foreach (string nm in post.ReplyToList) + foreach (var (_, screenName) in post.ReplyToList) { - if (!ids.Contains("@" + nm + " ") && - !nm.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) + if (!ids.Contains("@" + screenName + " ") && + !screenName.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) { - Match m = Regex.Match(post.TextFromApi, "[@@](?" + nm + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase); + Match m = Regex.Match(post.TextFromApi, "[@@](?" + screenName + ")([^a-zA-Z0-9]|$)", RegexOptions.IgnoreCase); if (m.Success) ids += "@" + m.Result("${id}") + " "; else - ids += "@" + nm + " "; + ids += "@" + screenName + " "; } } if (!string.IsNullOrEmpty(post.RetweetedBy)) { - if (!ids.Contains("@" + post.RetweetedBy + " ") && - !post.RetweetedBy.Equals(tw.Username, StringComparison.CurrentCultureIgnoreCase)) + if (!ids.Contains("@" + post.RetweetedBy + " ") && post.RetweetedByUserId != tw.UserId) { ids += "@" + post.RetweetedBy + " "; } @@ -7813,13 +7726,13 @@ namespace OpenTween if (string.IsNullOrEmpty(StatusText.Text)) { //未入力の場合のみ返信先付加 + var inReplyToStatusId = post.RetweetedId ?? post.StatusId; + var inReplyToScreenName = post.ScreenName; + this.inReplyTo = (inReplyToStatusId, inReplyToScreenName); + StatusText.Text = ids; StatusText.SelectionStart = ids.Length; StatusText.Focus(); - - var inReplyToStatusId = this._curPost.RetweetedId ?? this._curPost.StatusId; - var inReplyToScreenName = this._curPost.ScreenName; - this.inReplyTo = Tuple.Create(inReplyToStatusId, inReplyToScreenName); return; } @@ -7844,9 +7757,7 @@ namespace OpenTween } private void ListTab_MouseUp(object sender, MouseEventArgs e) - { - _tabDrag = false; - } + => this._tabDrag = false; private static int iconCnt = 0; private static int blinkCnt = 0; @@ -7942,21 +7853,13 @@ namespace OpenTween } private async void TimerRefreshIcon_Tick(object sender, EventArgs e) - { - //200ms - await this.RefreshTasktrayIcon(); - } + => await this.RefreshTasktrayIcon(); // 200ms private void ContextMenuTabProperty_Opening(object sender, CancelEventArgs e) { //右クリックの場合はタブ名が設定済。アプリケーションキーの場合は現在のタブを対象とする if (string.IsNullOrEmpty(_rclickTabName) || sender != ContextMenuTabProperty) - { - if (ListTab != null && ListTab.SelectedTab != null) - _rclickTabName = ListTab.SelectedTab.Text; - else - return; - } + _rclickTabName = this.CurrentTabName; if (_statuses == null) return; if (_statuses.Tabs == null) return; @@ -8071,10 +7974,10 @@ namespace OpenTween ListTab.TabPages[idx].ImageIndex = -1; } - if (_curTab.Text == tabName) + if (this.CurrentTabName == tabName) { this.PurgeListViewItemCache(); - _curList.Refresh(); + this.CurrentListView.Refresh(); } SetMainWindowTitle(); @@ -8105,7 +8008,8 @@ namespace OpenTween private void DeleteTabMenuItem_Click(object sender, EventArgs e) { - if (string.IsNullOrEmpty(_rclickTabName) || sender == this.DeleteTbMenuItem) _rclickTabName = ListTab.SelectedTab.Text; + if (string.IsNullOrEmpty(_rclickTabName) || sender == this.DeleteTbMenuItem) + _rclickTabName = this.CurrentTabName; RemoveSpecifiedTab(_rclickTabName, true); SaveConfigsTabs(); @@ -8127,7 +8031,7 @@ namespace OpenTween SaveConfigsTabs(); } - private void AddTabMenuItem_Click(object sender, EventArgs e) + private async void AddTabMenuItem_Click(object sender, EventArgs e) { string tabName = null; MyCommon.TabUsageType tabUsage; @@ -8183,13 +8087,12 @@ namespace OpenTween if (tabUsage == MyCommon.TabUsageType.PublicSearch) { ListTab.SelectedIndex = ListTab.TabPages.Count - 1; - ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus(); + this.CurrentTabPage.Controls["panelSearch"].Controls["comboSearch"].Focus(); } if (tabUsage == MyCommon.TabUsageType.Lists) { ListTab.SelectedIndex = ListTab.TabPages.Count - 1; - var listTab = (ListTimelineTabModel)this._statuses.Tabs[this._curTab.Text]; - this.GetListTimelineAsync(listTab); + await this.RefreshTabAsync(this.CurrentTab); } } } @@ -8202,19 +8105,20 @@ namespace OpenTween fltDialog.Owner = this; //選択発言を元にフィルタ追加 - foreach (int idx in _curList.SelectedIndices) + foreach (var post in this.CurrentTab.SelectedPosts) { //タブ選択(or追加) if (!SelectTab(out var tabName)) return; fltDialog.SetCurrent(tabName); - if (_statuses.Tabs[_curTab.Text][idx].RetweetedId == null) + + if (post.RetweetedId == null) { - fltDialog.AddNewFilter(_statuses.Tabs[_curTab.Text][idx].ScreenName, _statuses.Tabs[_curTab.Text][idx].TextFromApi); + fltDialog.AddNewFilter(post.ScreenName, post.TextFromApi); } else { - fltDialog.AddNewFilter(_statuses.Tabs[_curTab.Text][idx].RetweetedBy, _statuses.Tabs[_curTab.Text][idx].TextFromApi); + fltDialog.AddNewFilter(post.RetweetedBy, post.TextFromApi); } fltDialog.ShowDialog(this); this.TopMost = SettingManager.Common.AlwaysTop; @@ -8223,11 +8127,6 @@ namespace OpenTween this.ApplyPostFilters(); SaveConfigsTabs(); - if (this.ListTab.SelectedTab != null && - ((DetailsListView)this.ListTab.SelectedTab.Tag).SelectedIndices.Count > 0) - { - _curPost = _statuses.Tabs[this.ListTab.SelectedTab.Text][((DetailsListView)this.ListTab.SelectedTab.Tag).SelectedIndices[0]]; - } } protected override bool ProcessDialogKey(Keys keyData) @@ -8302,12 +8201,19 @@ namespace OpenTween return true; } } - else if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch && - (ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focused || - ListTab.SelectedTab.Controls["panelSearch"].Controls["comboLang"].Focused)) + else { - this.SearchButton_Click(ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"], null); - return true; + var tab = this.CurrentTab; + if (tab.TabType == MyCommon.TabUsageType.PublicSearch) + { + var tabPage = this.CurrentTabPage; + if (tabPage.Controls["panelSearch"].Controls["comboSearch"].Focused || + tabPage.Controls["panelSearch"].Controls["comboLang"].Focused) + { + this.SearchButton_Click(tabPage.Controls["panelSearch"].Controls["comboSearch"], null); + return true; + } + } } } @@ -8315,18 +8221,18 @@ namespace OpenTween } private void ReplyAllStripMenuItem_Click(object sender, EventArgs e) - { - MakeReplyOrDirectStatus(false, true, true); - } + => this.MakeReplyOrDirectStatus(false, true, true); private void IDRuleMenuItem_Click(object sender, EventArgs e) { - //未選択なら処理終了 - if (_curList.SelectedIndices.Count == 0) return; + var tab = this.CurrentTab; + var selectedPosts = tab.SelectedPosts; + + // 未選択なら処理終了 + if (selectedPosts.Length == 0) + return; - var tab = this._statuses.Tabs[this._curTab.Text]; - var screenNameArray = this._curList.SelectedIndices.Cast() - .Select(x => tab[x]) + var screenNameArray = selectedPosts .Select(x => x.RetweetedId != null ? x.RetweetedBy : x.ScreenName) .ToArray(); @@ -8347,12 +8253,13 @@ namespace OpenTween private void SourceRuleMenuItem_Click(object sender, EventArgs e) { - if (this._curList.SelectedIndices.Count == 0) + var tab = this.CurrentTab; + var selectedPosts = tab.SelectedPosts; + + if (selectedPosts.Length == 0) return; - var tab = this._statuses.Tabs[this._curTab.Text]; - var sourceArray = this._curList.SelectedIndices.Cast() - .Select(x => tab[x].Source).ToArray(); + var sourceArray = selectedPosts.Select(x => x.Source).ToArray(); this.AddFilterRuleBySource(sourceArray); } @@ -8444,11 +8351,10 @@ namespace OpenTween { if (dialog.ShowDialog(this) == DialogResult.Cancel) return false; - var selectedTab = dialog.SelectedTab; - tabName = selectedTab == null ? null : selectedTab.TabName; + tabName = dialog.SelectedTab?.TabName; } - ListTab.SelectedTab.Focus(); + this.CurrentTabPage.Focus(); //新規タブを選択→タブ作成 if (tabName == null) { @@ -8508,15 +8414,12 @@ namespace OpenTween mark = false; } } + private void CopySTOTMenuItem_Click(object sender, EventArgs e) - { - this.CopyStot(); - } + => this.CopyStot(); private void CopyURLMenuItem_Click(object sender, EventArgs e) - { - this.CopyIdUri(); - } + => this.CopyIdUri(); private void SelectAllMenuItem_Click(object sender, EventArgs e) { @@ -8528,7 +8431,7 @@ namespace OpenTween else { // ListView上でのCtrl+A - NativeMethods.SelectAllItems(this._curList); + NativeMethods.SelectAllItems(this.CurrentListView); } } @@ -8538,27 +8441,28 @@ namespace OpenTween int idx1; int idx2; - if (_curList.SelectedIndices.Count == 0) return; + var listView = this.CurrentListView; + if (listView.SelectedIndices.Count == 0) return; - int idx = _curList.SelectedIndices[0]; + int idx = listView.SelectedIndices[0]; - _item = _curList.GetItemAt(0, 25); + _item = listView.GetItemAt(0, 25); if (_item == null) idx1 = 0; else idx1 = _item.Index; - _item = _curList.GetItemAt(0, _curList.ClientSize.Height - 1); + _item = listView.GetItemAt(0, listView.ClientSize.Height - 1); if (_item == null) - idx2 = _curList.VirtualListSize - 1; + idx2 = listView.VirtualListSize - 1; else idx2 = _item.Index; idx -= Math.Abs(idx1 - idx2) / 2; if (idx < 0) idx = 0; - _curList.EnsureVisible(_curList.VirtualListSize - 1); - _curList.EnsureVisible(idx); + listView.EnsureVisible(listView.VirtualListSize - 1); + listView.EnsureVisible(idx); } private async void OpenURLMenuItem_Click(object sender, EventArgs e) @@ -8662,13 +8566,11 @@ namespace OpenTween } _statuses.ClearTabIds(tabName); - if (ListTab.SelectedTab.Text == tabName) + if (this.CurrentTabName == tabName) { _anchorPost = null; _anchorFlag = false; this.PurgeListViewItemCache(); - _curItemIndex = -1; - _curPost = null; } foreach (TabPage tb in ListTab.TabPages) { @@ -8698,7 +8600,7 @@ namespace OpenTween SettingManager.Common.DispLatestPost != MyCommon.DispTitleEnum.Ver && SettingManager.Common.DispLatestPost != MyCommon.DispTitleEnum.OwnStatus) { - foreach (var tab in _statuses.Tabs.Values) + foreach (var tab in _statuses.Tabs) { ur += tab.UnreadCount; al += tab.AllCount; @@ -8706,7 +8608,7 @@ namespace OpenTween } if (SettingManager.Common.DispUsername) ttl.Append(tw.Username).Append(" - "); - ttl.Append(Application.ProductName); + ttl.Append(ApplicationSettings.ApplicationName); ttl.Append(" "); switch (SettingManager.Common.DispLatestPost) { @@ -8761,11 +8663,11 @@ namespace OpenTween StringBuilder slbl = new StringBuilder(256); try { - foreach (var tab in _statuses.Tabs.Values) + foreach (var tab in _statuses.Tabs) { ur += tab.UnreadCount; al += tab.AllCount; - if (_curTab != null && tab.TabName.Equals(_curTab.Text)) + if (tab.TabName == this.CurrentTabName) { tur = tab.UnreadCount; tal = tab.AllCount; @@ -8820,119 +8722,108 @@ namespace OpenTween private void SetApiStatusLabel(string endpointName = null) { - if (_curTab == null) - { - this.toolStripApiGauge.ApiEndpoint = null; - } - else - { - var tabType = _statuses.Tabs[_curTab.Text].TabType; + var tabType = this.CurrentTab.TabType; - if (endpointName == null) + if (endpointName == null) + { + // 表示中のタブに応じて更新 + switch (tabType) { - // 表示中のタブに応じて更新 - switch (tabType) - { - case MyCommon.TabUsageType.Home: - case MyCommon.TabUsageType.UserDefined: - endpointName = "/statuses/home_timeline"; - break; + case MyCommon.TabUsageType.Home: + case MyCommon.TabUsageType.UserDefined: + endpointName = "/statuses/home_timeline"; + break; + + case MyCommon.TabUsageType.Mentions: + endpointName = "/statuses/mentions_timeline"; + break; - case MyCommon.TabUsageType.Mentions: - endpointName = "/statuses/mentions_timeline"; - break; + case MyCommon.TabUsageType.Favorites: + endpointName = "/favorites/list"; + break; - case MyCommon.TabUsageType.Favorites: - endpointName = "/favorites/list"; - break; + case MyCommon.TabUsageType.DirectMessage: + endpointName = "/direct_messages/events/list"; + break; - case MyCommon.TabUsageType.DirectMessage: - endpointName = "/direct_messages"; - break; + case MyCommon.TabUsageType.UserTimeline: + endpointName = "/statuses/user_timeline"; + break; - case MyCommon.TabUsageType.UserTimeline: - endpointName = "/statuses/user_timeline"; - break; + case MyCommon.TabUsageType.Lists: + endpointName = "/lists/statuses"; + break; - case MyCommon.TabUsageType.Lists: - endpointName = "/lists/statuses"; - break; + case MyCommon.TabUsageType.PublicSearch: + endpointName = "/search/tweets"; + break; - case MyCommon.TabUsageType.PublicSearch: - endpointName = "/search/tweets"; - break; + case MyCommon.TabUsageType.Related: + endpointName = "/statuses/show/:id"; + break; - case MyCommon.TabUsageType.Related: - endpointName = "/statuses/show/:id"; - break; + default: + break; + } - default: - break; - } + this.toolStripApiGauge.ApiEndpoint = endpointName; + } + else + { + // 表示中のタブに関連する endpoint であれば更新 + var update = false; - this.toolStripApiGauge.ApiEndpoint = endpointName; - } - else + switch (endpointName) { - // 表示中のタブに関連する endpoint であれば更新 - var update = false; - - switch (endpointName) - { - case "/statuses/home_timeline": - update = tabType == MyCommon.TabUsageType.Home || - tabType == MyCommon.TabUsageType.UserDefined; - break; + case "/statuses/home_timeline": + update = tabType == MyCommon.TabUsageType.Home || + tabType == MyCommon.TabUsageType.UserDefined; + break; - case "/statuses/mentions_timeline": - update = tabType == MyCommon.TabUsageType.Mentions; - break; + case "/statuses/mentions_timeline": + update = tabType == MyCommon.TabUsageType.Mentions; + break; - case "/favorites/list": - update = tabType == MyCommon.TabUsageType.Favorites; - break; + case "/favorites/list": + update = tabType == MyCommon.TabUsageType.Favorites; + break; - case "/direct_messages:": - update = tabType == MyCommon.TabUsageType.DirectMessage; - break; + case "/direct_messages/events/list": + update = tabType == MyCommon.TabUsageType.DirectMessage; + break; - case "/statuses/user_timeline": - update = tabType == MyCommon.TabUsageType.UserTimeline; - break; + case "/statuses/user_timeline": + update = tabType == MyCommon.TabUsageType.UserTimeline; + break; - case "/lists/statuses": - update = tabType == MyCommon.TabUsageType.Lists; - break; + case "/lists/statuses": + update = tabType == MyCommon.TabUsageType.Lists; + break; - case "/search/tweets": - update = tabType == MyCommon.TabUsageType.PublicSearch; - break; + case "/search/tweets": + update = tabType == MyCommon.TabUsageType.PublicSearch; + break; - case "/statuses/show/:id": - update = tabType == MyCommon.TabUsageType.Related; - break; + case "/statuses/show/:id": + update = tabType == MyCommon.TabUsageType.Related; + break; - default: - break; - } + default: + break; + } - if (update) - { - this.toolStripApiGauge.ApiEndpoint = endpointName; - } + if (update) + { + this.toolStripApiGauge.ApiEndpoint = endpointName; } } } private void SetStatusLabelUrl() - { - StatusLabelUrl.Text = GetStatusLabelText(); - } + => this.StatusLabelUrl.Text = this.GetStatusLabelText(); public void SetStatusLabel(string text) - { - StatusLabel.Text = text; - } + => this.StatusLabel.Text = text; private void SetNotifyIconText() { @@ -8946,7 +8837,7 @@ namespace OpenTween ur.Append(tw.Username); ur.Append(" - "); } - ur.Append(Application.ProductName); + ur.Append(ApplicationSettings.ApplicationName); #if DEBUG ur.Append("(Debug Build)"); #endif @@ -9006,7 +8897,7 @@ namespace OpenTween if (m != null) { - var inReplyToScreenName = this.inReplyTo.Item2; + var inReplyToScreenName = this.inReplyTo.Value.ScreenName; if (StatusText.StartsWith("@", StringComparison.Ordinal)) { if (StatusText.StartsWith("@" + inReplyToScreenName, StringComparison.Ordinal)) return; @@ -9035,9 +8926,6 @@ namespace OpenTween var configScaleFactor = SettingManager.Local.GetConfigScaleFactor(this.CurrentAutoScaleDimensions); this.ClientSize = ScaleBy(configScaleFactor, SettingManager.Local.FormSize); - //_mySize = this.ClientSize; //サイズ保持(最小化・最大化されたまま終了した場合の対応用) - this.DesktopLocation = SettingManager.Local.FormLocation; - //_myLoc = this.DesktopLocation; //位置保持(最小化・最大化されたまま終了した場合の対応用) // Splitterの位置設定 var splitterDistance = ScaleBy(configScaleFactor.Height, SettingManager.Local.SplitterDistance); @@ -9126,44 +9014,46 @@ namespace OpenTween private async Task doRepliedStatusOpen() { - if (this.ExistCurrentPost && _curPost.InReplyToUser != null && _curPost.InReplyToStatusId != null) + var currentPost = this.CurrentPost; + if (this.ExistCurrentPost && currentPost.InReplyToUser != null && currentPost.InReplyToStatusId != null) { if (MyCommon.IsKeyDown(Keys.Shift)) { - await this.OpenUriInBrowserAsync(MyCommon.GetStatusUrl(_curPost.InReplyToUser, _curPost.InReplyToStatusId.Value)); + await this.OpenUriInBrowserAsync(MyCommon.GetStatusUrl(currentPost.InReplyToUser, currentPost.InReplyToStatusId.Value)); return; } - if (_statuses.ContainsKey(_curPost.InReplyToStatusId.Value)) + if (_statuses.ContainsKey(currentPost.InReplyToStatusId.Value)) { - PostClass repPost = _statuses[_curPost.InReplyToStatusId.Value]; - MessageBox.Show($"{repPost.ScreenName} / {repPost.Nickname} ({repPost.CreatedAt})" + Environment.NewLine + repPost.TextFromApi); + PostClass repPost = _statuses[currentPost.InReplyToStatusId.Value]; + MessageBox.Show($"{repPost.ScreenName} / {repPost.Nickname} ({repPost.CreatedAt.ToLocalTimeString()})" + Environment.NewLine + repPost.TextFromApi); } else { foreach (TabModel tb in _statuses.GetTabsByType(MyCommon.TabUsageType.Lists | MyCommon.TabUsageType.PublicSearch)) { - if (tb == null || !tb.Contains(_curPost.InReplyToStatusId.Value)) break; - PostClass repPost = _statuses[_curPost.InReplyToStatusId.Value]; - MessageBox.Show($"{repPost.ScreenName} / {repPost.Nickname} ({repPost.CreatedAt})" + Environment.NewLine + repPost.TextFromApi); + if (tb == null || !tb.Contains(currentPost.InReplyToStatusId.Value)) break; + PostClass repPost = _statuses[currentPost.InReplyToStatusId.Value]; + MessageBox.Show($"{repPost.ScreenName} / {repPost.Nickname} ({repPost.CreatedAt.ToLocalTimeString()})" + Environment.NewLine + repPost.TextFromApi); return; } - await this.OpenUriInBrowserAsync(MyCommon.GetStatusUrl(_curPost.InReplyToUser, _curPost.InReplyToStatusId.Value)); + await this.OpenUriInBrowserAsync(MyCommon.GetStatusUrl(currentPost.InReplyToUser, currentPost.InReplyToStatusId.Value)); } } } private async void RepliedStatusOpenMenuItem_Click(object sender, EventArgs e) - { - await this.doRepliedStatusOpen(); - } + => await this.doRepliedStatusOpen(); private void SplitContainer2_Panel2_Resize(object sender, EventArgs e) { + if (this._initialLayout) + return; // SettingLocal の反映が完了するまで multiline の判定を行わない + var multiline = this.SplitContainer2.Panel2.Height > this.SplitContainer2.Panel2MinSize + 2; if (multiline != this.StatusText.Multiline) { this.StatusText.Multiline = multiline; - MultiLineMenuItem.Checked = multiline; + SettingManager.Local.StatusMultiline = multiline; ModifySettingLocal = true; } } @@ -9181,9 +9071,10 @@ namespace OpenTween private void MultiLineMenuItem_Click(object sender, EventArgs e) { //発言欄複数行 - StatusText.Multiline = MultiLineMenuItem.Checked; - SettingManager.Local.StatusMultiline = MultiLineMenuItem.Checked; - if (MultiLineMenuItem.Checked) + var menuItemChecked = ((ToolStripMenuItem)sender).Checked; + StatusText.Multiline = menuItemChecked; + SettingManager.Local.StatusMultiline = menuItemChecked; + if (menuItemChecked) { if (SplitContainer2.Height - _mySpDis2 - SplitContainer2.SplitterWidth < 0) SplitContainer2.SplitterDistance = 0; @@ -9199,6 +9090,17 @@ namespace OpenTween private async Task UrlConvertAsync(MyCommon.UrlConverter Converter_Type) { + if (Converter_Type == MyCommon.UrlConverter.Bitly || Converter_Type == MyCommon.UrlConverter.Jmp) + { + // OAuth2 アクセストークンまたは API キー (旧方式) のいずれも設定されていなければ短縮しない + if (string.IsNullOrEmpty(SettingManager.Common.BitlyAccessToken) && + (string.IsNullOrEmpty(SettingManager.Common.BilyUser) || string.IsNullOrEmpty(SettingManager.Common.BitlyPwd))) + { + MessageBox.Show(this, Properties.Resources.UrlConvert_BitlyAuthRequired, ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; + } + } + //t.coで投稿時自動短縮する場合は、外部サービスでの短縮禁止 //if (SettingDialog.UrlConvertAuto && SettingDialog.ShortenTco) return; @@ -9377,24 +9279,13 @@ namespace OpenTween } private async void TinyURLToolStripMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.TinyUrl); - } + => await this.UrlConvertAsync(MyCommon.UrlConverter.TinyUrl); private async void IsgdToolStripMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.Isgd); - } - - private async void TwurlnlToolStripMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.Twurl); - } + => await this.UrlConvertAsync(MyCommon.UrlConverter.Isgd); private async void UxnuMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.Uxnu); - } + => await this.UrlConvertAsync(MyCommon.UrlConverter.Uxnu); private async void UrlConvertAutoToolStripMenuItem_Click(object sender, EventArgs e) { @@ -9413,9 +9304,7 @@ namespace OpenTween } private void UrlUndoToolStripMenuItem_Click(object sender, EventArgs e) - { - doUrlUndo(); - } + => this.doUrlUndo(); private void NewPostPopMenuItem_CheckStateChanged(object sender, EventArgs e) { @@ -9442,20 +9331,18 @@ namespace OpenTween private void MenuStrip1_MenuDeactivate(object sender, EventArgs e) { + var currentTabPage = this.CurrentTabPage; if (this.Tag != null) // 設定された戻り先へ遷移 { - if (this.Tag == this.ListTab.SelectedTab) - ((Control)this.ListTab.SelectedTab.Tag).Select(); + if (this.Tag == currentTabPage) + ((Control)currentTabPage.Tag).Select(); else ((Control)this.Tag).Select(); } else // 戻り先が指定されていない (初期状態) 場合はタブに遷移 { - if (ListTab.SelectedIndex > -1 && ListTab.SelectedTab.HasChildren) - { - this.Tag = ListTab.SelectedTab.Tag; - ((Control)this.Tag).Select(); - } + this.Tag = currentTabPage.Tag; + ((Control)this.Tag).Select(); } // フォーカスがメニューに遷移したかどうかを表すフラグを降ろす MenuStrip1.Tag = null; @@ -9773,7 +9660,7 @@ namespace OpenTween var screenName = userUriMatch.Groups["ScreenName"].Value; if (this.IsTwitterId(screenName)) { - this.AddNewTabForUserTimeline(screenName); + await this.AddNewTabForUserTimeline(screenName); return; } } @@ -9844,40 +9731,28 @@ namespace OpenTween this.PurgeListViewItemCache(); - _curTab = _tab; - _curList = (DetailsListView)_tab.Tag; + this._statuses.SelectTab(_tab.Text); - if (_curList.SelectedIndices.Count > 0) - { - _curItemIndex = _curList.SelectedIndices[0]; - _curPost = GetCurTabPost(_curItemIndex); - } - else - { - _curItemIndex = -1; - _curPost = null; - } + var listView = this.CurrentListView; _anchorPost = null; _anchorFlag = false; if (_iconCol) { - ((DetailsListView)_tab.Tag).Columns[1].Text = ColumnText[2]; + listView.Columns[1].Text = ColumnText[2]; } else { - for (int i = 0; i < _curList.Columns.Count; i++) + for (int i = 0; i < listView.Columns.Count; i++) { - ((DetailsListView)_tab.Tag).Columns[i].Text = ColumnText[i]; + listView.Columns[i].Text = ColumnText[i]; } } } private void ListTab_Selecting(object sender, TabControlCancelEventArgs e) - { - ListTabSelect(e.TabPage); - } + => this.ListTabSelect(e.TabPage); private void SelectListItem(DetailsListView LView, int Index) { @@ -9968,19 +9843,19 @@ namespace OpenTween this.RefreshBlockIdsAsync(), this.RefreshNoRetweetIdsAsync(), this.RefreshTwitterConfigurationAsync(), - this.GetHomeTimelineAsync(), - this.GetReplyAsync(), - this.GetDirectMessagesAsync(), - this.GetPublicSearchAllAsync(), - this.GetUserTimelineAllAsync(), - this.GetListTimelineAllAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), + this.RefreshTabAsync(), }; if (SettingManager.Common.StartupFollowers) loadTasks.Add(this.RefreshFollowerIdsAsync()); if (SettingManager.Common.GetFav) - loadTasks.Add(this.GetFavoritesAsync()); + loadTasks.Add(this.RefreshTabAsync()); var allTasks = Task.WhenAll(loadTasks); @@ -10048,35 +9923,34 @@ namespace OpenTween } private async void GetFollowersAllToolStripMenuItem_Click(object sender, EventArgs e) - { - await this.doGetFollowersMenu(); - } + => await this.doGetFollowersMenu(); private void ReTweetUnofficialStripMenuItem_Click(object sender, EventArgs e) - { - doReTweetUnofficial(); - } + => this.doReTweetUnofficial(); private async Task doReTweetOfficial(bool isConfirm) { //公式RT if (this.ExistCurrentPost) { - if (!_curPost.CanRetweetBy(this.twitterApi.CurrentUserId)) + var selectedPosts = this.CurrentTab.SelectedPosts; + + if (selectedPosts.Any(x => !x.CanRetweetBy(this.twitterApi.CurrentUserId))) { - if (this._curPost.IsProtect) + if (selectedPosts.Any(x => x.IsProtect)) MessageBox.Show("Protected."); _DoFavRetweetFlags = false; return; } - if (_curList.SelectedIndices.Count > 15) + + if (selectedPosts.Length > 15) { MessageBox.Show(Properties.Resources.RetweetLimitText); _DoFavRetweetFlags = false; return; } - else if (_curList.SelectedIndices.Count > 1) + else if (selectedPosts.Length > 1) { string QuestionText = Properties.Resources.RetweetQuestion2; if (_DoFavRetweetFlags) QuestionText = Properties.Resources.FavoriteRetweetQuestionText1; @@ -10102,22 +9976,14 @@ namespace OpenTween } } - var statusIds = new List(); - foreach (int idx in _curList.SelectedIndices) - { - PostClass post = GetCurTabPost(idx); - if (post.CanRetweetBy(this.twitterApi.CurrentUserId)) - statusIds.Add(post.StatusId); - } + var statusIds = selectedPosts.Select(x => x.StatusId).ToList(); await this.RetweetAsync(statusIds); } } private async void ReTweetStripMenuItem_Click(object sender, EventArgs e) - { - await this.doReTweetOfficial(true); - } + => await this.doReTweetOfficial(true); private async Task FavoritesRetweetOfficial() { @@ -10139,11 +10005,12 @@ namespace OpenTween private async Task FavoritesRetweetUnofficial() { - if (this.ExistCurrentPost && !_curPost.IsDm) + var post = this.CurrentPost; + if (this.ExistCurrentPost && !post.IsDm) { _DoFavRetweetFlags = true; var favoriteTask = this.FavoriteChange(true); - if (!_curPost.IsProtect && _DoFavRetweetFlags) + if (!post.IsProtect && _DoFavRetweetFlags) { _DoFavRetweetFlags = false; doReTweetUnofficial(); @@ -10169,6 +10036,8 @@ namespace OpenTween statusHtml = Regex.Replace(statusHtml, ".+?)\">(?.+?)", "${text}"); // ハッシュタグ statusHtml = Regex.Replace(statusHtml, ".+?)\">(?.+?)", "${text}"); + // 絵文字 + statusHtml = Regex.Replace(statusHtml, "\"(?<text.+?)\" />", "${text}"); //
除去 if (multiline) @@ -10187,7 +10056,7 @@ namespace OpenTween { this.tweetDetailsView.DumpPostClass = this.DumpPostClassToolStripMenuItem.Checked; - if (_curPost != null) + if (this.CurrentPost != null) await this.DispSelectedPost(true); } @@ -10199,14 +10068,45 @@ namespace OpenTween DebugModeToolStripMenuItem.Visible = false; } - private void ToolStripMenuItemUrlAutoShorten_CheckedChanged(object sender, EventArgs e) + private void UrlMultibyteSplitMenuItem_CheckedChanged(object sender, EventArgs e) + => this.urlMultibyteSplit = ((ToolStripMenuItem)sender).Checked; + + private void PreventSmsCommandMenuItem_CheckedChanged(object sender, EventArgs e) + => this.preventSmsCommand = ((ToolStripMenuItem)sender).Checked; + + private void UrlAutoShortenMenuItem_CheckedChanged(object sender, EventArgs e) + => SettingManager.Common.UrlConvertAuto = ((ToolStripMenuItem)sender).Checked; + + private void IdeographicSpaceToSpaceMenuItem_Click(object sender, EventArgs e) + { + SettingManager.Common.WideSpaceConvert = ((ToolStripMenuItem)sender).Checked; + ModifySettingCommon = true; + } + + private void FocusLockMenuItem_CheckedChanged(object sender, EventArgs e) + { + SettingManager.Common.FocusLockToStatusText = ((ToolStripMenuItem)sender).Checked; + ModifySettingCommon = true; + } + + private void PostModeMenuItem_DropDownOpening(object sender, EventArgs e) { - SettingManager.Common.UrlConvertAuto = ToolStripMenuItemUrlAutoShorten.Checked; + UrlMultibyteSplitMenuItem.Checked = this.urlMultibyteSplit; + PreventSmsCommandMenuItem.Checked = this.preventSmsCommand; + UrlAutoShortenMenuItem.Checked = SettingManager.Common.UrlConvertAuto; + IdeographicSpaceToSpaceMenuItem.Checked = SettingManager.Common.WideSpaceConvert; + MultiLineMenuItem.Checked = SettingManager.Local.StatusMultiline; + FocusLockMenuItem.Checked = SettingManager.Common.FocusLockToStatusText; } private void ContextMenuPostMode_Opening(object sender, CancelEventArgs e) { - ToolStripMenuItemUrlAutoShorten.Checked = SettingManager.Common.UrlConvertAuto; + UrlMultibyteSplitPullDownMenuItem.Checked = this.urlMultibyteSplit; + PreventSmsCommandPullDownMenuItem.Checked = this.preventSmsCommand; + UrlAutoShortenPullDownMenuItem.Checked = SettingManager.Common.UrlConvertAuto; + IdeographicSpaceToSpacePullDownMenuItem.Checked = SettingManager.Common.WideSpaceConvert; + MultiLinePullDownMenuItem.Checked = SettingManager.Local.StatusMultiline; + FocusLockPullDownMenuItem.Checked = SettingManager.Common.FocusLockToStatusText; } private void TraceOutToolStripMenuItem_Click(object sender, EventArgs e) @@ -10218,10 +10118,7 @@ namespace OpenTween } private void TweenMain_Deactivate(object sender, EventArgs e) - { - //画面が非アクティブになったら、発言欄の背景色をデフォルトへ - this.StatusText_Leave(StatusText, System.EventArgs.Empty); - } + => this.StatusText_Leave(StatusText, EventArgs.Empty); // 画面が非アクティブになったら、発言欄の背景色をデフォルトへ private void TabRenameMenuItem_Click(object sender, EventArgs e) { @@ -10231,14 +10128,10 @@ namespace OpenTween } private async void BitlyToolStripMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.Bitly); - } + => await this.UrlConvertAsync(MyCommon.UrlConverter.Bitly); private async void JmpToolStripMenuItem_Click(object sender, EventArgs e) - { - await UrlConvertAsync(MyCommon.UrlConverter.Jmp); - } + => await this.UrlConvertAsync(MyCommon.UrlConverter.Jmp); private async void ApiUsageInfoMenuItem_Click(object sender, EventArgs e) { @@ -10276,7 +10169,7 @@ namespace OpenTween private async void FollowCommandMenuItem_Click(object sender, EventArgs e) { - var id = _curPost?.ScreenName ?? ""; + var id = this.CurrentPost?.ScreenName ?? ""; await this.FollowCommand(id); } @@ -10316,7 +10209,7 @@ namespace OpenTween private async void RemoveCommandMenuItem_Click(object sender, EventArgs e) { - var id = _curPost?.ScreenName ?? ""; + var id = this.CurrentPost?.ScreenName ?? ""; await this.RemoveCommand(id, false); } @@ -10359,7 +10252,7 @@ namespace OpenTween private async void FriendshipMenuItem_Click(object sender, EventArgs e) { - var id = _curPost?.ScreenName ?? ""; + var id = this.CurrentPost?.ScreenName ?? ""; await this.ShowFriendship(id); } @@ -10497,18 +10390,7 @@ namespace OpenTween } private async void OwnStatusMenuItem_Click(object sender, EventArgs e) - { - await this.doShowUserStatus(tw.Username, false); - //if (!string.IsNullOrEmpty(tw.UserInfoXml)) - //{ - // doShowUserStatus(tw.Username, false); - //} - //else - //{ - // MessageBox.Show(Properties.Resources.ShowYourProfileText1, "Your status", MessageBoxButtons.OK, MessageBoxIcon.Information); - // return; - //} - } + => await this.doShowUserStatus(tw.Username, false); // TwitterIDでない固定文字列を調べる(文字列検証のみ 実際に取得はしない) // URLから切り出した文字列を渡す @@ -10518,37 +10400,30 @@ namespace OpenTween if (this.tw.Configuration.NonUsernamePaths == null || this.tw.Configuration.NonUsernamePaths.Length == 0) return !Regex.Match(name, @"^(about|jobs|tos|privacy|who_to_follow|download|messages)$", RegexOptions.IgnoreCase).Success; else - return !this.tw.Configuration.NonUsernamePaths.Contains(name.ToLowerInvariant()); - } - - private void IdeographicSpaceToSpaceToolStripMenuItem_Click(object sender, EventArgs e) - { - ModifySettingCommon = true; - } - - private void ToolStripFocusLockMenuItem_CheckedChanged(object sender, EventArgs e) - { - ModifySettingCommon = true; + return !this.tw.Configuration.NonUsernamePaths.Contains(name, StringComparer.InvariantCultureIgnoreCase); } private void doQuoteOfficial() { if (this.ExistCurrentPost) { - if (_curPost.IsDm || - !StatusText.Enabled) return; + var post = this.CurrentPost; + if (post.IsDm || !StatusText.Enabled) + return; - if (_curPost.IsProtect) + if (post.IsProtect) { MessageBox.Show("Protected."); return; } - StatusText.Text = " " + MyCommon.GetStatusUrl(_curPost); + var selection = (this.StatusText.SelectionStart, this.StatusText.SelectionLength); this.inReplyTo = null; - StatusText.SelectionStart = 0; + StatusText.Text += " " + MyCommon.GetStatusUrl(post); + + (this.StatusText.SelectionStart, this.StatusText.SelectionLength) = selection; StatusText.Focus(); } } @@ -10558,35 +10433,36 @@ namespace OpenTween //RT @id:内容 if (this.ExistCurrentPost) { - if (_curPost.IsDm || !StatusText.Enabled) + var post = this.CurrentPost; + if (post.IsDm || !StatusText.Enabled) return; - if (_curPost.IsProtect) + if (post.IsProtect) { MessageBox.Show("Protected."); return; } - string rtdata = _curPost.Text; + string rtdata = post.Text; rtdata = CreateRetweetUnofficial(rtdata, this.StatusText.Multiline); - StatusText.Text = " RT @" + _curPost.ScreenName + ": " + rtdata; + var selection = (this.StatusText.SelectionStart, this.StatusText.SelectionLength); // 投稿時に in_reply_to_status_id を付加する - var inReplyToStatusId = this._curPost.RetweetedId ?? this._curPost.StatusId; - var inReplyToScreenName = this._curPost.ScreenName; - this.inReplyTo = Tuple.Create(inReplyToStatusId, inReplyToScreenName); + var inReplyToStatusId = post.RetweetedId ?? post.StatusId; + var inReplyToScreenName = post.ScreenName; + this.inReplyTo = (inReplyToStatusId, inReplyToScreenName); - StatusText.SelectionStart = 0; + StatusText.Text += " RT @" + post.ScreenName + ": " + rtdata; + + (this.StatusText.SelectionStart, this.StatusText.SelectionLength) = selection; StatusText.Focus(); } } - private void QuoteStripMenuItem_Click(object sender, EventArgs e) // Handles QuoteStripMenuItem.Click, QtOpMenuItem.Click - { - doQuoteOfficial(); - } + private void QuoteStripMenuItem_Click(object sender, EventArgs e) + => this.doQuoteOfficial(); - private void SearchButton_Click(object sender, EventArgs e) + private async void SearchButton_Click(object sender, EventArgs e) { //公式検索 Control pnl = ((Control)sender).Parent; @@ -10649,15 +10525,12 @@ namespace OpenTween SaveConfigsTabs(); //検索条件の保存 } - this.GetPublicSearchAsync(tb); listView.Focus(); + await this.RefreshTabAsync(tb); } private async void RefreshMoreStripMenuItem_Click(object sender, EventArgs e) - { - //もっと前を取得 - await this.DoRefreshMore(); - } + => await this.DoRefreshMore(); // もっと前を取得 /// /// 指定されたタブのListTabにおける位置を返します @@ -10699,7 +10572,8 @@ namespace OpenTween // 関連発言なら既存のタブを置き換える tb.TabName = relatedTab.TabName; this.ClearTab(tb.TabName, false); - _statuses.Tabs[tb.TabName] = tb; + + this._statuses.ReplaceTab(tb); for (int i = 0; i < ListTab.TabPages.Count; i++) { @@ -10762,24 +10636,17 @@ namespace OpenTween private async Task doMoveToRTHome() { - if (_curList.SelectedIndices.Count > 0) - { - PostClass post = GetCurTabPost(_curList.SelectedIndices[0]); - if (post.RetweetedId != null) - { - await this.OpenUriInBrowserAsync("https://twitter.com/" + GetCurTabPost(_curList.SelectedIndices[0]).RetweetedBy); - } - } + var post = this.CurrentPost; + if (post != null && post.RetweetedId != null) + await this.OpenUriInBrowserAsync("https://twitter.com/" + post.RetweetedBy); } private async void MoveToRTHomeMenuItem_Click(object sender, EventArgs e) - { - await this.doMoveToRTHome(); - } + => await this.doMoveToRTHome(); private void ListManageUserContextToolStripMenuItem_Click(object sender, EventArgs e) { - var screenName = this._curPost?.ScreenName; + var screenName = this.CurrentPost?.ScreenName; if (screenName != null) this.ListManageUserContext(screenName); } @@ -10812,17 +10679,13 @@ namespace OpenTween private void PublicSearchQueryMenuItem_Click(object sender, EventArgs e) { - if (ListTab.SelectedTab != null) - { - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType != MyCommon.TabUsageType.PublicSearch) return; - ListTab.SelectedTab.Controls["panelSearch"].Controls["comboSearch"].Focus(); - } + var tab = this.CurrentTab; + if (tab.TabType != MyCommon.TabUsageType.PublicSearch) return; + this.CurrentTabPage.Controls["panelSearch"].Controls["comboSearch"].Focus(); } private void StatusLabel_DoubleClick(object sender, EventArgs e) - { - MessageBox.Show(StatusLabel.TextHistory, "Logs", MessageBoxButtons.OK, MessageBoxIcon.None); - } + => MessageBox.Show(StatusLabel.TextHistory, "Logs", MessageBoxButtons.OK, MessageBoxIcon.None); private void HashManageMenuItem_Click(object sender, EventArgs e) { @@ -10840,14 +10703,14 @@ namespace OpenTween if (!string.IsNullOrEmpty(HashMgr.UseHash)) { HashStripSplitButton.Text = HashMgr.UseHash; + HashTogglePullDownMenuItem.Checked = true; HashToggleMenuItem.Checked = true; - HashToggleToolStripMenuItem.Checked = true; } else { HashStripSplitButton.Text = "#[-]"; + HashTogglePullDownMenuItem.Checked = false; HashToggleMenuItem.Checked = false; - HashToggleToolStripMenuItem.Checked = false; } //if (HashMgr.IsInsert && HashMgr.UseHash != "") //{ @@ -10874,37 +10737,33 @@ namespace OpenTween { HashStripSplitButton.Text = HashMgr.UseHash; HashToggleMenuItem.Checked = true; - HashToggleToolStripMenuItem.Checked = true; + HashTogglePullDownMenuItem.Checked = true; } else { HashStripSplitButton.Text = "#[-]"; HashToggleMenuItem.Checked = false; - HashToggleToolStripMenuItem.Checked = false; + HashTogglePullDownMenuItem.Checked = false; } ModifySettingCommon = true; this.StatusText_TextChanged(null, null); } private void HashStripSplitButton_ButtonClick(object sender, EventArgs e) - { - HashToggleMenuItem_Click(null, null); - } + => this.HashToggleMenuItem_Click(null, null); public void SetPermanentHashtag(string hashtag) { HashMgr.SetPermanentHash("#" + hashtag); HashStripSplitButton.Text = HashMgr.UseHash; + HashTogglePullDownMenuItem.Checked = true; HashToggleMenuItem.Checked = true; - HashToggleToolStripMenuItem.Checked = true; //使用ハッシュタグとして設定 ModifySettingCommon = true; } private void MenuItemOperate_DropDownOpening(object sender, EventArgs e) { - if (ListTab.SelectedTab == null) return; - if (_statuses == null || _statuses.Tabs == null || !_statuses.Tabs.ContainsKey(ListTab.SelectedTab.Text)) return; if (!this.ExistCurrentPost) { this.ReplyOpMenuItem.Enabled = false; @@ -10936,12 +10795,13 @@ namespace OpenTween this.UnreadOpMenuItem.Enabled = true; } - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || _curPost.IsDm) + var tab = this.CurrentTab; + var post = this.CurrentPost; + if (tab.TabType == MyCommon.TabUsageType.DirectMessage || !this.ExistCurrentPost || post.IsDm) { this.FavOpMenuItem.Enabled = false; this.UnFavOpMenuItem.Enabled = false; this.OpenStatusOpMenuItem.Enabled = false; - this.OpenFavotterOpMenuItem.Enabled = false; this.ShowRelatedStatusesMenuItem2.Enabled = false; this.RtOpMenuItem.Enabled = false; this.RtUnOpMenuItem.Enabled = false; @@ -10954,10 +10814,9 @@ namespace OpenTween this.FavOpMenuItem.Enabled = true; this.UnFavOpMenuItem.Enabled = true; this.OpenStatusOpMenuItem.Enabled = true; - this.OpenFavotterOpMenuItem.Enabled = true; this.ShowRelatedStatusesMenuItem2.Enabled = true; //PublicSearchの時問題出るかも - if (!_curPost.CanRetweetBy(this.twitterApi.CurrentUserId)) + if (!post.CanRetweetBy(this.twitterApi.CurrentUserId)) { this.RtOpMenuItem.Enabled = false; this.RtUnOpMenuItem.Enabled = false; @@ -10975,7 +10834,7 @@ namespace OpenTween } } - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType != MyCommon.TabUsageType.Favorites) + if (tab.TabType != MyCommon.TabUsageType.Favorites) { this.RefreshPrevOpMenuItem.Enabled = true; } @@ -10983,8 +10842,7 @@ namespace OpenTween { this.RefreshPrevOpMenuItem.Enabled = false; } - if (!this.ExistCurrentPost - || _curPost.InReplyToStatusId == null) + if (!this.ExistCurrentPost || post.InReplyToStatusId == null) { OpenRepSourceOpMenuItem.Enabled = false; } @@ -10992,7 +10850,7 @@ namespace OpenTween { OpenRepSourceOpMenuItem.Enabled = true; } - if (!this.ExistCurrentPost || string.IsNullOrEmpty(_curPost.RetweetedBy)) + if (!this.ExistCurrentPost || string.IsNullOrEmpty(post.RetweetedBy)) { OpenRterHomeMenuItem.Enabled = false; } @@ -11003,19 +10861,15 @@ namespace OpenTween if (this.ExistCurrentPost) { - this.DelOpMenuItem.Enabled = this._curPost.CanDeleteBy(this.tw.UserId); + this.DelOpMenuItem.Enabled = post.CanDeleteBy(this.tw.UserId); } } private void MenuItemTab_DropDownOpening(object sender, EventArgs e) - { - ContextMenuTabProperty_Opening(sender, null); - } + => this.ContextMenuTabProperty_Opening(sender, null); public Twitter TwitterInstance - { - get { return tw; } - } + => this.tw; private void SplitContainer3_SplitterMoved(object sender, SplitterEventArgs e) { @@ -11052,17 +10906,12 @@ namespace OpenTween { UndoRemoveTabMenuItem.Enabled = true; } - if (ListTab.SelectedTab != null) - { - if (_statuses.Tabs[ListTab.SelectedTab.Text].TabType == MyCommon.TabUsageType.PublicSearch) - PublicSearchQueryMenuItem.Enabled = true; - else - PublicSearchQueryMenuItem.Enabled = false; - } + + if (this.CurrentTab.TabType == MyCommon.TabUsageType.PublicSearch) + PublicSearchQueryMenuItem.Enabled = true; else - { PublicSearchQueryMenuItem.Enabled = false; - } + if (!this.ExistCurrentPost) { this.CopySTOTMenuItem.Enabled = false; @@ -11074,22 +10923,18 @@ namespace OpenTween this.CopySTOTMenuItem.Enabled = true; this.CopyURLMenuItem.Enabled = true; this.CopyUserIdStripMenuItem.Enabled = true; - if (_curPost.IsDm) this.CopyURLMenuItem.Enabled = false; - if (_curPost.IsProtect) this.CopySTOTMenuItem.Enabled = false; + + var post = this.CurrentPost; + if (post.IsDm) this.CopyURLMenuItem.Enabled = false; + if (post.IsProtect) this.CopySTOTMenuItem.Enabled = false; } } private void NotifyIcon1_MouseMove(object sender, MouseEventArgs e) - { - SetNotifyIconText(); - } + => this.SetNotifyIconText(); private async void UserStatusToolStripMenuItem_Click(object sender, EventArgs e) - { - var id = _curPost?.ScreenName ?? ""; - - await this.ShowUserStatus(id); - } + => await this.ShowUserStatus(this.CurrentPost?.ScreenName ?? ""); private async Task doShowUserStatus(string id, bool ShowInputDialog) { @@ -11151,20 +10996,17 @@ namespace OpenTween } internal Task ShowUserStatus(string id, bool ShowInputDialog) - { - return this.doShowUserStatus(id, ShowInputDialog); - } + => this.doShowUserStatus(id, ShowInputDialog); internal Task ShowUserStatus(string id) - { - return this.doShowUserStatus(id, true); - } + => this.doShowUserStatus(id, true); private async void ShowProfileMenuItem_Click(object sender, EventArgs e) { - if (_curPost != null) + var post = this.CurrentPost; + if (post != null) { - await this.ShowUserStatus(_curPost.ScreenName, false); + await this.ShowUserStatus(post.ScreenName, false); } } @@ -11173,7 +11015,8 @@ namespace OpenTween if (!this.ExistCurrentPost) return; - var statusId = this._curPost.RetweetedId ?? this._curPost.StatusId; + var post = this.CurrentPost; + var statusId = post.RetweetedId ?? post.StatusId; TwitterStatus status; using (var dialog = new WaitingDialog(Properties.Resources.RtCountMenuItem_ClickText1)) @@ -11252,14 +11095,7 @@ namespace OpenTween } private void SplitContainer2_MouseDoubleClick(object sender, MouseEventArgs e) - { - this.MultiLineMenuItem.PerformClick(); - } - - public PostClass CurPost - { - get { return _curPost; } - } + => this.MultiLinePullDownMenuItem.PerformClick(); #region "画像投稿" private void ImageSelectMenuItem_Click(object sender, EventArgs e) @@ -11298,18 +11134,14 @@ namespace OpenTween { TimelinePanel.Visible = true; TimelinePanel.Enabled = true; - ((DetailsListView)ListTab.SelectedTab.Tag).Focus(); + this.CurrentListView.Focus(); } private void ImageSelector_FilePickDialogOpening(object sender, EventArgs e) - { - this.AllowDrop = false; - } + => this.AllowDrop = false; private void ImageSelector_FilePickDialogClosed(object sender, EventArgs e) - { - this.AllowDrop = true; - } + => this.AllowDrop = true; private void ImageSelector_SelectedServiceChanged(object sender, EventArgs e) { @@ -11323,9 +11155,7 @@ namespace OpenTween } private void ImageSelector_VisibleChanged(object sender, EventArgs e) - { - this.StatusText_TextChanged(null, null); - } + => this.StatusText_TextChanged(null, null); /// /// StatusTextでCtrl+Vが押下された時の処理 @@ -11378,7 +11208,8 @@ namespace OpenTween private void MenuItemCommand_DropDownOpening(object sender, EventArgs e) { - if (this.ExistCurrentPost && !_curPost.IsDm) + var post = this.CurrentPost; + if (this.ExistCurrentPost && !post.IsDm) RtCountMenuItem.Enabled = true; else RtCountMenuItem.Enabled = false; @@ -11390,14 +11221,13 @@ namespace OpenTween } private void CopyUserIdStripMenuItem_Click(object sender, EventArgs e) - { - CopyUserId(); - } + => this.CopyUserId(); private void CopyUserId() { - if (_curPost == null) return; - string clstr = _curPost.ScreenName; + var post = this.CurrentPost; + if (post == null) return; + var clstr = post.ScreenName; try { Clipboard.SetDataObject(clstr, false, 5, 100); @@ -11410,15 +11240,16 @@ namespace OpenTween private async void ShowRelatedStatusesMenuItem_Click(object sender, EventArgs e) { - if (this.ExistCurrentPost && !_curPost.IsDm) + var post = this.CurrentPost; + if (this.ExistCurrentPost && !post.IsDm) { try { - await this.OpenRelatedTab(this._curPost); + await this.OpenRelatedTab(post); } catch (TabException ex) { - MessageBox.Show(this, ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, ex.Message, ApplicationSettings.ApplicationName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } } @@ -11428,7 +11259,7 @@ namespace OpenTween /// /// 表示するツイートのID /// 名前の重複が多すぎてタブを作成できない場合 - private async Task OpenRelatedTab(long statusId) + public async Task OpenRelatedTab(long statusId) { var post = this._statuses[statusId]; if (post == null) @@ -11462,9 +11293,11 @@ namespace OpenTween var tabName = this._statuses.MakeTabName("Related Tweets"); - tabRelated = new RelatedPostsTabModel(tabName, post); - tabRelated.UnreadManage = false; - tabRelated.Notify = false; + tabRelated = new RelatedPostsTabModel(tabName, post) + { + UnreadManage = false, + Notify = false, + }; this._statuses.AddTab(tabRelated); this.AddNewTab(tabRelated, startup: false); @@ -11480,7 +11313,7 @@ namespace OpenTween } } - await this.GetRelatedTweetsAsync(tabRelated); + await this.RefreshTabAsync(tabRelated); tabPage = this.ListTab.TabPages.Cast() .FirstOrDefault(x => x.Text == tabRelated.TabName); @@ -11512,9 +11345,7 @@ namespace OpenTween } private void tw_UserIdChanged() - { - this.ModifySettingCommon = true; - } + => this.ModifySettingCommon = true; #region "Userstream" private async void tw_PostDeleted(object sender, PostDeletedEventArgs e) @@ -11526,11 +11357,12 @@ namespace OpenTween await this.InvokeAsync(async () => { this._statuses.RemovePostFromAllTabs(e.StatusId, setIsDeleted: true); - if (_curTab != null && _statuses.Tabs[_curTab.Text].Contains(e.StatusId)) + if (this.CurrentTab.Contains(e.StatusId)) { this.PurgeListViewItemCache(); - ((DetailsListView)_curTab.Tag).Update(); - if (_curPost != null && _curPost.StatusId == e.StatusId) + this.CurrentListView.Update(); + var post = this.CurrentPost; + if (post != null && post.StatusId == e.StatusId) await this.DispSelectedPost(true); } }); @@ -11547,9 +11379,7 @@ namespace OpenTween } } - private int userStreamsRefreshing = 0; - - private async void tw_NewPostFromStream(object sender, EventArgs e) + private void tw_NewPostFromStream(object sender, EventArgs e) { if (SettingManager.Common.ReadOldPosts) { @@ -11558,21 +11388,7 @@ namespace OpenTween this._statuses.DistributePosts(); - if (SettingManager.Common.UserstreamPeriod > 0) return; - - // userStreamsRefreshing が 0 (インクリメント後は1) であれば RefreshTimeline を実行 - if (Interlocked.Increment(ref this.userStreamsRefreshing) == 1) - { - try - { - await this.InvokeAsync(() => this.RefreshTimeline()) - .ConfigureAwait(false); - } - finally - { - Interlocked.Exchange(ref this.userStreamsRefreshing, 0); - } - } + this.RefreshThrottlingTimer.Invoke(); } private async void tw_UserStreamStarted(object sender, EventArgs e) @@ -11666,12 +11482,12 @@ namespace OpenTween NotifyEvent(ev); if (ev.Event == "favorite" || ev.Event == "unfavorite") { - if (_curTab != null && _statuses.Tabs[_curTab.Text].Contains(ev.Id)) + if (this.CurrentTab.Contains(ev.Id)) { this.PurgeListViewItemCache(); - ((DetailsListView)_curTab.Tag).Update(); + this.CurrentListView.Update(); } - if (ev.Event == "unfavorite" && ev.Username.ToLowerInvariant().Equals(tw.Username.ToLowerInvariant())) + if (ev.Event == "unfavorite" && ev.Username.Equals(tw.Username, StringComparison.InvariantCultureIgnoreCase)) { var favTab = this._statuses.GetTabByType(MyCommon.TabUsageType.Favorites); favTab.EnqueueRemovePost(ev.Id, setIsDeleted: false); @@ -11697,7 +11513,7 @@ namespace OpenTween { //title.Clear(); } - title.Append(Application.ProductName); + title.Append(ApplicationSettings.ApplicationName); title.Append(" ["); title.Append(ev.Event.ToUpper(CultureInfo.CurrentCulture)); title.Append("] by "); @@ -11823,14 +11639,17 @@ namespace OpenTween { if (evtDialog == null || evtDialog.IsDisposed) { - evtDialog = null; - evtDialog = new EventViewerDialog(); - evtDialog.Owner = this; + this.evtDialog = new EventViewerDialog + { + Owner = this, + }; + //親の中央に表示 - Point pos = evtDialog.Location; - pos.X = Convert.ToInt32(this.Location.X + this.Size.Width / 2 - evtDialog.Size.Width / 2); - pos.Y = Convert.ToInt32(this.Location.Y + this.Size.Height / 2 - evtDialog.Size.Height / 2); - evtDialog.Location = pos; + this.evtDialog.Location = new Point + { + X = Convert.ToInt32(this.Location.X + this.Size.Width / 2 - evtDialog.Size.Width / 2), + Y = Convert.ToInt32(this.Location.Y + this.Size.Height / 2 - evtDialog.Size.Height / 2), + }; } evtDialog.EventSource = tw.StoredEvent; if (!evtDialog.Visible) @@ -11855,39 +11674,28 @@ namespace OpenTween } catch (Exception) { - MessageBox.Show("Failed to restart. Please run " + Application.ProductName + " manually."); + MessageBox.Show("Failed to restart. Please run " + ApplicationSettings.ApplicationName + " manually."); } } - private async void OpenOwnFavedMenuItem_Click(object sender, EventArgs e) - { - if (!string.IsNullOrEmpty(tw.Username)) - await this.OpenUriInBrowserAsync(Properties.Resources.FavstarUrl + "users/" + tw.Username + "/recent"); - } - private async void OpenOwnHomeMenuItem_Click(object sender, EventArgs e) - { - await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + tw.Username); - } + => await this.OpenUriInBrowserAsync(MyCommon.TwitterUrl + tw.Username); private bool ExistCurrentPost { get { - if (_curPost == null) return false; - if (_curPost.IsDeleted) return false; - return true; + var post = this.CurrentPost; + return post != null && !post.IsDeleted; } } - private void ShowUserTimelineToolStripMenuItem_Click(object sender, EventArgs e) - { - ShowUserTimeline(); - } + private async void ShowUserTimelineToolStripMenuItem_Click(object sender, EventArgs e) + => await this.ShowUserTimeline(); private string GetUserIdFromCurPostOrInput(string caption) { - var id = _curPost?.ScreenName ?? ""; + var id = this.CurrentPost?.ScreenName ?? ""; using (InputTabName inputName = new InputTabName()) { @@ -11907,27 +11715,36 @@ namespace OpenTween return id; } - private void UserTimelineToolStripMenuItem_Click(object sender, EventArgs e) + private async void UserTimelineToolStripMenuItem_Click(object sender, EventArgs e) { string id = GetUserIdFromCurPostOrInput("Show UserTimeline"); if (!string.IsNullOrEmpty(id)) { - AddNewTabForUserTimeline(id); + await this.AddNewTabForUserTimeline(id); } } - private async void UserFavorareToolStripMenuItem_Click(object sender, EventArgs e) + private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e) { - string id = GetUserIdFromCurPostOrInput("Show Favstar"); - if (!string.IsNullOrEmpty(id)) - { - await this.OpenUriInBrowserAsync(Properties.Resources.FavstarUrl + "users/" + id + "/recent"); - } + if (e.Mode == Microsoft.Win32.PowerModes.Resume) osResumed = true; } - private void SystemEvents_PowerModeChanged(object sender, Microsoft.Win32.PowerModeChangedEventArgs e) + private async void SystemEvents_TimeChanged(object sender, EventArgs e) { - if (e.Mode == Microsoft.Win32.PowerModes.Resume) osResumed = true; + var prevTimeOffset = TimeZoneInfo.Local.BaseUtcOffset; + + TimeZoneInfo.ClearCachedData(); + + var curTimeOffset = TimeZoneInfo.Local.BaseUtcOffset; + + if (curTimeOffset != prevTimeOffset) + { + // タイムゾーンの変更を反映 + this.PurgeListViewItemCache(); + this.CurrentListView.Refresh(); + + await this.DispSelectedPost(forceupdate: true); + } } private void TimelineRefreshEnableChange(bool isEnable) @@ -11944,9 +11761,7 @@ namespace OpenTween } private void StopRefreshAllMenuItem_CheckedChanged(object sender, EventArgs e) - { - TimelineRefreshEnableChange(!StopRefreshAllMenuItem.Checked); - } + => this.TimelineRefreshEnableChange(!StopRefreshAllMenuItem.Checked); private async Task OpenUserAppointUrl() { @@ -11954,12 +11769,13 @@ namespace OpenTween { if (SettingManager.Common.UserAppointUrl.Contains("{ID}") || SettingManager.Common.UserAppointUrl.Contains("{STATUS}")) { - if (_curPost != null) + var post = this.CurrentPost; + if (post != null) { string xUrl = SettingManager.Common.UserAppointUrl; - xUrl = xUrl.Replace("{ID}", _curPost.ScreenName); + xUrl = xUrl.Replace("{ID}", post.ScreenName); - var statusId = _curPost.RetweetedId ?? _curPost.StatusId; + var statusId = post.RetweetedId ?? post.StatusId; xUrl = xUrl.Replace("{STATUS}", statusId.ToString()); await this.OpenUriInBrowserAsync(xUrl); @@ -11973,9 +11789,7 @@ namespace OpenTween } private async void OpenUserSpecifiedUrlMenuItem_Click(object sender, EventArgs e) - { - await this.OpenUserAppointUrl(); - } + => await this.OpenUserAppointUrl(); private async void GrowlHelper_Callback(object sender, GrowlHelper.NotifyCallbackEventArgs e) { @@ -12006,19 +11820,13 @@ namespace OpenTween } private void tweetThumbnail1_ThumbnailLoading(object sender, EventArgs e) - { - this.SplitContainer3.Panel2Collapsed = false; - } + => this.SplitContainer3.Panel2Collapsed = false; private async void tweetThumbnail1_ThumbnailDoubleClick(object sender, ThumbnailDoubleClickEventArgs e) - { - await this.OpenThumbnailPicture(e.Thumbnail); - } + => await this.OpenThumbnailPicture(e.Thumbnail); private async void tweetThumbnail1_ThumbnailImageSearchClick(object sender, ThumbnailImageSearchEventArgs e) - { - await this.OpenUriInBrowserAsync(e.ImageUrl); - } + => await this.OpenUriInBrowserAsync(e.ImageUrl); private async Task OpenThumbnailPicture(ThumbnailInfo thumbnail) { @@ -12028,9 +11836,7 @@ namespace OpenTween } private async void TwitterApiStatusToolStripMenuItem_Click(object sender, EventArgs e) - { - await this.OpenUriInBrowserAsync(Twitter.ServiceAvailabilityStatusUrl); - } + => await this.OpenUriInBrowserAsync(Twitter.ServiceAvailabilityStatusUrl); private void PostButton_KeyDown(object sender, KeyEventArgs e) { @@ -12054,29 +11860,19 @@ namespace OpenTween } private void IconSizeNoneToolStripMenuItem_Click(object sender, EventArgs e) - { - ChangeListViewIconSize(MyCommon.IconSizes.IconNone); - } + => this.ChangeListViewIconSize(MyCommon.IconSizes.IconNone); private void IconSize16ToolStripMenuItem_Click(object sender, EventArgs e) - { - ChangeListViewIconSize(MyCommon.IconSizes.Icon16); - } + => this.ChangeListViewIconSize(MyCommon.IconSizes.Icon16); private void IconSize24ToolStripMenuItem_Click(object sender, EventArgs e) - { - ChangeListViewIconSize(MyCommon.IconSizes.Icon24); - } + => this.ChangeListViewIconSize(MyCommon.IconSizes.Icon24); private void IconSize48ToolStripMenuItem_Click(object sender, EventArgs e) - { - ChangeListViewIconSize(MyCommon.IconSizes.Icon48); - } + => this.ChangeListViewIconSize(MyCommon.IconSizes.Icon48); private void IconSize48_2ToolStripMenuItem_Click(object sender, EventArgs e) - { - ChangeListViewIconSize(MyCommon.IconSizes.Icon48_2); - } + => this.ChangeListViewIconSize(MyCommon.IconSizes.Icon48_2); private void ChangeListViewIconSize(MyCommon.IconSizes iconSize) { @@ -12095,7 +11891,7 @@ namespace OpenTween } } - _curList?.Refresh(); + this.CurrentListView.Refresh(); ModifySettingCommon = true; }