OSDN Git Service

PostClass.Mediaを1つのURLごとに複数の画像URLを保持できるように修正
[opentween/open-tween.git] / OpenTween / DetailsListView.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 //           (c) 2008-2011 Moz (@syo68k)
4 //           (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 //           (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 //           (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 //           (c) 2011      kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
8 // All rights reserved.
9 // 
10 // This file is part of OpenTween.
11 // 
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
15 // any later version.
16 // 
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 // for more details. 
21 // 
22 // You should have received a copy of the GNU General Public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
26
27 using System;
28 using System.Collections.Generic;
29 using System.Linq;
30 using System.Text;
31 using System.Windows.Forms;
32 using System.Drawing;
33 using System.ComponentModel;
34 using System.Runtime.InteropServices;
35 using System.Diagnostics;
36
37 namespace OpenTween.OpenTweenCustomControl
38 {
39     public sealed class DetailsListView : ListView
40     {
41         private Rectangle changeBounds;
42         private EventHandlerList _handlers = new EventHandlerList();
43
44         public ContextMenuStrip ColumnHeaderContextMenuStrip { get; set; }
45
46         public event EventHandler VScrolled;
47         public event EventHandler HScrolled;
48
49         public DetailsListView()
50         {
51             View = View.Details;
52             FullRowSelect = true;
53             HideSelection = false;
54             DoubleBuffered = true;
55         }
56
57         //[System.ComponentModel.DefaultValue(0),
58         // System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
59         //public new int VirtualListSize
60         //{
61         //    get { return base.VirtualListSize; }
62         //    set
63         //    {
64         //        if (value == base.VirtualListSize) return;
65         //        if (base.VirtualListSize > 0 && value > 0)
66         //        {
67         //            int topIndex = 0;
68         //            if (!this.IsDisposed)
69         //            {
70         //                if (base.VirtualListSize < value)
71         //                {
72         //                    if (this.TopItem == null)
73         //                    {
74         //                        topIndex = 0;
75         //                    }
76         //                    else
77         //                    {
78         //                        topIndex = this.TopItem.Index;
79         //                    }
80         //                    topIndex = Math.Min(topIndex, Math.Abs(value - 1));
81         //                    this.TopItem = this.Items[topIndex];
82         //                }
83         //                else
84         //                {
85         //                    if (this.TopItem == null)
86         //                    {
87         //                        topIndex = 0;
88         //                    }
89         //                    else
90         //                    {
91         //
92         //                    }
93         //                    this.TopItem = this.Items[0];
94         //                }
95         //            }
96         //        }
97         //        base.VirtualListSize = value;
98         //    }
99         //}
100
101         /// <summary>
102         /// 複数選択時の起点になるアイテム (selection mark) の位置を取得・設定する
103         /// </summary>
104         /// <remarks>
105         /// Items[idx].Selected の設定では mark が設定されるが、SelectedIndices.Add(idx) では設定されないため、
106         /// 主に後者と合わせて使用する
107         /// </remarks>
108         public int SelectionMark
109         {
110             get { return Win32Api.ListView_GetSelectionMark(this.Handle); }
111             set { Win32Api.ListView_SetSelectionMark(this.Handle, value); }
112         }
113
114         public void ChangeItemBackColor(int index, Color backColor)
115         {
116             ChangeSubItemBackColor(index, 0, backColor);
117         }
118
119         public void ChangeItemForeColor(int index, Color foreColor)
120         {
121             ChangeSubItemForeColor(index, 0, foreColor);
122         }
123
124         public void ChangeItemFont(int index, Font fnt)
125         {
126             ChangeSubItemFont(index, 0, fnt);
127         }
128
129         public void ChangeItemFontAndColor(int index, Color foreColor, Font fnt)
130         {
131             ChangeSubItemStyles(index, 0, BackColor, foreColor, fnt);
132         }
133
134         public void ChangeItemStyles(int index, Color backColor, Color foreColor, Font fnt)
135         {
136             ChangeSubItemStyles(index, 0, backColor, foreColor, fnt);
137         }
138
139         public void ChangeSubItemBackColor(int itemIndex, int subitemIndex, Color backColor)
140         {
141             var item = this.Items[itemIndex];
142             item.SubItems[subitemIndex].BackColor = backColor;
143             SetUpdateBounds(item, subitemIndex);
144             this.Update();
145             this.changeBounds = Rectangle.Empty;
146         }
147
148         public void ChangeSubItemForeColor(int itemIndex, int subitemIndex, Color foreColor)
149         {
150             var item = this.Items[itemIndex];
151             item.SubItems[subitemIndex].ForeColor = foreColor;
152             SetUpdateBounds(item, subitemIndex);
153             this.Update();
154             this.changeBounds = Rectangle.Empty;
155         }
156
157         public void ChangeSubItemFont(int itemIndex, int subitemIndex, Font fnt)
158         {
159             var item = this.Items[itemIndex];
160             item.SubItems[subitemIndex].Font = fnt;
161             SetUpdateBounds(item, subitemIndex);
162             this.Update();
163             this.changeBounds = Rectangle.Empty;
164         }
165
166         public void ChangeSubItemFontAndColor(int itemIndex, int subitemIndex, Color foreColor, Font fnt)
167         {
168             var item = this.Items[itemIndex];
169             var subItem = item.SubItems[subitemIndex];
170             subItem.ForeColor = foreColor;
171             subItem.Font = fnt;
172             SetUpdateBounds(item, subitemIndex);
173             this.Update();
174             this.changeBounds = Rectangle.Empty;
175         }
176
177         public void ChangeSubItemStyles(int itemIndex, int subitemIndex, Color backColor, Color foreColor, Font fnt)
178         {
179             var item = this.Items[itemIndex];
180             var subItem = item.SubItems[subitemIndex];
181             subItem.BackColor = backColor;
182             subItem.ForeColor = foreColor;
183             subItem.Font = fnt;
184             SetUpdateBounds(item, subitemIndex);
185             this.Update();
186             this.changeBounds = Rectangle.Empty;
187         }
188
189         private void SetUpdateBounds(ListViewItem item, int subItemIndex)
190         {
191             try
192             {
193                 if (subItemIndex > this.Columns.Count)
194                 {
195                     throw new ArgumentOutOfRangeException("subItemIndex");
196                 }
197                 if (item.UseItemStyleForSubItems)
198                 {
199                     this.changeBounds = item.Bounds;
200                 }
201                 else
202                 {
203                     this.changeBounds = this.GetSubItemBounds(item, subItemIndex);
204                 }
205             }
206             catch (ArgumentException)
207             {
208                 //タイミングによりBoundsプロパティが取れない?
209                 this.changeBounds = Rectangle.Empty;
210             }
211         }
212
213         private Rectangle GetSubItemBounds(ListViewItem item, int subitemIndex)
214         {
215             if (subitemIndex == 0 && this.Columns.Count > 0)
216             {
217                 Rectangle col0 = item.Bounds;
218                 return new Rectangle(col0.Left, col0.Top, item.SubItems[1].Bounds.X + 1, col0.Height);
219             }
220             else
221             {
222                 return item.SubItems[subitemIndex].Bounds;
223             }
224         }
225
226         [StructLayout(LayoutKind.Sequential)]
227         private struct SCROLLINFO
228         {
229             public int cbSize;
230             public int fMask;
231             public int nMin;
232             public int nMax;
233             public int nPage;
234             public int nPos;
235             public int nTrackPos;
236         }
237
238         private enum ScrollBarDirection
239         {
240             SB_HORZ = 0,
241             SB_VERT = 1,
242             SB_CTL = 2,
243             SB_BOTH = 3,
244         }
245
246         private enum ScrollInfoMask
247         {
248             SIF_RANGE = 0x1,
249             SIF_PAGE = 0x2,
250             SIF_POS = 0x4,
251             SIF_DISABLENOSCROLL = 0x8,
252             SIF_TRACKPOS = 0x10,
253             SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS),
254         }
255
256         [DllImport("user32.dll")]
257         private static extern int GetScrollInfo(IntPtr hWnd, ScrollBarDirection fnBar, ref SCROLLINFO lpsi);
258
259         private SCROLLINFO si = new SCROLLINFO {
260             cbSize = Marshal.SizeOf(new SCROLLINFO()),
261             fMask = (int)ScrollInfoMask.SIF_POS
262         };
263
264         [DebuggerStepThrough()]
265         protected override void WndProc(ref Message m)
266         {
267             const int WM_ERASEBKGND = 0x14;
268             const int WM_PAINT = 0xF;
269             const int WM_MOUSEWHEEL = 0x20A;
270             const int WM_MOUSEHWHEEL = 0x20E;
271             const int WM_HSCROLL = 0x114;
272             const int WM_VSCROLL = 0x115;
273             const int WM_KEYDOWN = 0x100;
274             const int WM_CONTEXTMENU = 0x7B;
275             const int LVM_SETITEMCOUNT = 0x102F;
276             const long LVSICF_NOSCROLL = 0x2;
277             const long LVSICF_NOINVALIDATEALL = 0x1;
278
279             int hPos = -1;
280             int vPos = -1;
281
282             switch (m.Msg)
283             {
284                 case WM_ERASEBKGND:
285                     if (this.changeBounds != Rectangle.Empty)
286                         m.Msg = 0;
287                     break;
288                 case WM_PAINT:
289                     if (this.changeBounds != Rectangle.Empty)
290                     {
291                         Win32Api.ValidateRect(this.Handle, IntPtr.Zero);
292                         this.Invalidate(this.changeBounds);
293                         this.changeBounds = Rectangle.Empty;
294                     }
295                     break;
296                 case WM_HSCROLL:
297                     if (HScrolled != null)
298                         HScrolled(this, EventArgs.Empty);
299                     break;
300                 case WM_VSCROLL:
301                     if (VScrolled != null)
302                         VScrolled(this, EventArgs.Empty);
303                     break;
304                 case WM_MOUSEWHEEL:
305                 case WM_MOUSEHWHEEL:
306                 case WM_KEYDOWN:
307                     if (GetScrollInfo(this.Handle, ScrollBarDirection.SB_VERT, ref si) != 0)
308                         vPos = si.nPos;
309                     if (GetScrollInfo(this.Handle, ScrollBarDirection.SB_HORZ, ref si) != 0)
310                         hPos = si.nPos;
311                     break;
312                 case WM_CONTEXTMENU:
313                     if (m.WParam != this.Handle)
314                     {
315                         //カラムヘッダメニューを表示
316                         if (this.ColumnHeaderContextMenuStrip != null)
317                             this.ColumnHeaderContextMenuStrip.Show(new Point(m.LParam.ToInt32()));
318                         return;
319                     }
320                     break;
321                 case LVM_SETITEMCOUNT:
322                     m.LParam = new IntPtr(LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL);
323                     break;
324             }
325
326             try
327             {
328                 base.WndProc(ref m);
329             }
330             catch (ArgumentOutOfRangeException)
331             {
332                 //Substringでlengthが0以下。アイコンサイズが影響?
333             }
334             catch (AccessViolationException)
335             {
336                 //WndProcのさらに先で発生する。
337             }
338             if (this.IsDisposed) return;
339
340             if (vPos != -1)
341                 if (GetScrollInfo(this.Handle, ScrollBarDirection.SB_VERT, ref si) != 0 && vPos != si.nPos)
342                     if (VScrolled != null)
343                         VScrolled(this, EventArgs.Empty);
344             if (hPos != -1)
345                 if (GetScrollInfo(this.Handle, ScrollBarDirection.SB_HORZ, ref si) != 0 && hPos != si.nPos)
346                     if (HScrolled != null)
347                         HScrolled(this, EventArgs.Empty);
348         }
349    }
350 }