1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 // (c) 2008-2011 Moz (@syo68k)
4 // (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 // (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 // (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 // (c) 2011 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
10 // This file is part of OpenTween.
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General public License
22 // You should have received a copy of the GNU General public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
30 using System.Collections.Generic;
33 using System.Windows.Forms;
34 using OpenTween.Models;
35 using OpenTween.OpenTweenCustomControl;
39 public class TimelineListViewState
41 private readonly DetailsListView listView;
42 private readonly TabModel tab;
44 private ListViewScroll savedScrollState;
45 private ListViewSelection savedSelectionState;
47 internal readonly record struct ListViewScroll(
48 ScrollLockMode ScrollLockMode,
52 internal enum ScrollLockMode
54 /// <summary>固定しない</summary>
57 /// <summary>最上部に固定する</summary>
60 /// <summary>最下部に固定する</summary>
63 /// <summary><see cref="ListViewScroll.TopItemStatusId"/> の位置に固定する</summary>
67 internal readonly record struct ListViewSelection(
68 long[] SelectedStatusIds,
69 long? SelectionMarkStatusId,
73 public TimelineListViewState(DetailsListView listView, TabModel tab)
75 this.listView = listView;
79 public void Save(bool lockScroll)
81 this.savedScrollState = this.SaveListViewScroll(lockScroll);
82 this.savedSelectionState = this.SaveListViewSelection();
88 this.RestoreSelection();
91 public void RestoreScroll()
92 => this.RestoreListViewScroll(this.savedScrollState);
94 public void RestoreSelection()
95 => this.RestoreListViewSelection(this.savedSelectionState);
98 /// <see cref="ListView"/> のスクロール位置に関する情報を <see cref="ListViewScroll"/> として返します
100 private ListViewScroll SaveListViewScroll(bool lockScroll)
102 var lockMode = this.GetScrollLockMode(lockScroll);
103 long? topItemStatusId = null;
105 if (lockMode == ScrollLockMode.FixedToItem)
107 var topItemIndex = this.listView.TopItem?.Index ?? -1;
108 if (topItemIndex != -1 && topItemIndex < this.tab.AllCount)
109 topItemStatusId = this.tab.GetStatusIdAt(topItemIndex);
112 return new ListViewScroll
114 ScrollLockMode = lockMode,
115 TopItemStatusId = topItemStatusId,
119 private ScrollLockMode GetScrollLockMode(bool lockScroll)
121 if (this.tab.SortMode == ComparerMode.Id)
123 if (this.tab.SortOrder == SortOrder.Ascending)
127 return ScrollLockMode.None;
129 // 最下行が表示されていたら、最下行へ強制スクロール。最下行が表示されていなかったら制御しない
132 var bottomItem = this.listView.GetItemAt(0, this.listView.ClientSize.Height - 1);
133 if (bottomItem == null || bottomItem.Index == this.listView.VirtualListSize - 1)
134 return ScrollLockMode.FixedToBottom;
136 return ScrollLockMode.None;
142 return ScrollLockMode.FixedToItem;
144 // 最上行が表示されていたら、制御しない。最上行が表示されていなかったら、現在表示位置へ強制スクロール
145 var topItem = this.listView.TopItem;
146 if (topItem == null || topItem.Index == 0)
147 return ScrollLockMode.FixedToTop;
149 return ScrollLockMode.FixedToItem;
154 return ScrollLockMode.FixedToItem;
159 /// <see cref="ListView"/> の選択状態を <see cref="ListViewSelection"/> として返します
161 private ListViewSelection SaveListViewSelection()
163 if (this.listView.VirtualListSize == 0)
165 return new ListViewSelection
167 SelectedStatusIds = Array.Empty<long>(),
168 SelectionMarkStatusId = null,
169 FocusedStatusId = null,
173 return new ListViewSelection
175 SelectedStatusIds = this.tab.SelectedStatusIds,
176 FocusedStatusId = this.GetFocusedStatusId(),
177 SelectionMarkStatusId = this.GetSelectionMarkStatusId(),
181 private long? GetFocusedStatusId()
183 var index = this.listView.FocusedItem?.Index ?? -1;
185 return index != -1 && index < this.tab.AllCount ? this.tab.GetStatusIdAt(index) : (long?)null;
188 private long? GetSelectionMarkStatusId()
190 var index = this.listView.SelectionMark;
192 return index != -1 && index < this.tab.AllCount ? this.tab.GetStatusIdAt(index) : (long?)null;
196 /// <see cref="SaveListViewScroll"/> によって保存されたスクロール位置を復元します
198 private void RestoreListViewScroll(ListViewScroll listScroll)
200 if (this.listView.VirtualListSize == 0)
203 switch (listScroll.ScrollLockMode)
205 case ScrollLockMode.FixedToTop:
206 this.listView.EnsureVisible(0);
208 case ScrollLockMode.FixedToBottom:
209 this.listView.EnsureVisible(this.listView.VirtualListSize - 1);
211 case ScrollLockMode.FixedToItem:
212 var topIndex = listScroll.TopItemStatusId != null ? this.tab.IndexOf(listScroll.TopItemStatusId.Value) : -1;
215 var topItem = this.listView.Items[topIndex];
218 this.listView.TopItem = topItem;
220 catch (NullReferenceException)
222 this.listView.EnsureVisible(this.listView.VirtualListSize - 1);
223 this.listView.EnsureVisible(topIndex);
227 case ScrollLockMode.None:
234 /// <see cref="SaveListViewSelection"/> によって保存された選択状態を復元します
236 private void RestoreListViewSelection(ListViewSelection listSelection)
238 var invalidateBounds = (Rectangle?)null;
240 var previousFocusedItem = this.listView.FocusedItem;
241 if (previousFocusedItem != null)
242 invalidateBounds = previousFocusedItem.Bounds;
244 // status_id から ListView 上のインデックスに変換
245 if (listSelection.SelectedStatusIds != null)
247 var selectedIndices = this.tab.IndexOf(listSelection.SelectedStatusIds).Where(x => x != -1).ToArray();
248 this.listView.SelectItems(selectedIndices);
251 if (listSelection.FocusedStatusId != null)
253 var focusedIndex = this.tab.IndexOf(listSelection.FocusedStatusId.Value);
254 if (focusedIndex != -1)
255 this.listView.Items[focusedIndex].Focused = true;
258 if (listSelection.SelectionMarkStatusId != null)
260 var selectionMarkIndex = this.tab.IndexOf(listSelection.SelectionMarkStatusId.Value);
261 if (selectionMarkIndex != -1)
262 this.listView.SelectionMark = selectionMarkIndex;
265 if (invalidateBounds != null)
266 this.listView.Invalidate(invalidateBounds.Value);