OSDN Git Service

プロフィール画面へのD&Dによるアイコン変更時に確認ダイアログを表示するように変更
[opentween/open-tween.git] / OpenTween / TweetThumbnail.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2012 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
4 //
5 // This file is part of OpenTween.
6 //
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
10 // any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 // for more details.
16 //
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
21
22 using System;
23 using System.Collections.Generic;
24 using System.ComponentModel;
25 using System.Diagnostics.CodeAnalysis;
26 using System.Drawing;
27 using System.Data;
28 using System.Linq;
29 using System.Net;
30 using System.Net.Http;
31 using System.Text;
32 using System.Windows.Forms;
33 using System.Text.RegularExpressions;
34 using System.Threading.Tasks;
35 using OpenTween.Thumbnail;
36 using System.Threading;
37
38 namespace OpenTween
39 {
40     public partial class TweetThumbnail : UserControl
41     {
42         protected internal List<OTPictureBox> pictureBox = new List<OTPictureBox>();
43
44         public event EventHandler ThumbnailLoading;
45         public event EventHandler<ThumbnailDoubleClickEventArgs> ThumbnailDoubleClick;
46         public event EventHandler<ThumbnailImageSearchEventArgs> ThumbnailImageSearchClick;
47
48         public ThumbnailInfo Thumbnail
49         {
50             get { return this.pictureBox[this.scrollBar.Value].Tag as ThumbnailInfo; }
51         }
52
53         public TweetThumbnail()
54         {
55             InitializeComponent();
56         }
57
58         public Task ShowThumbnailAsync(PostClass post)
59         {
60             return this.ShowThumbnailAsync(post, CancellationToken.None);
61         }
62
63         public async Task ShowThumbnailAsync(PostClass post, CancellationToken cancelToken)
64         {
65             var loadTasks = new List<Task>();
66
67             this.scrollBar.Enabled = false;
68
69             if (post.Media.Count == 0 && post.PostGeo.Lat == 0 && post.PostGeo.Lng == 0)
70             {
71                 this.SetThumbnailCount(0);
72                 return;
73             }
74
75             var thumbnails = (await this.GetThumbailInfoAsync(post, cancelToken))
76                 .ToArray();
77
78             cancelToken.ThrowIfCancellationRequested();
79
80             this.SetThumbnailCount(thumbnails.Length);
81             if (thumbnails.Length == 0)
82                 return;
83
84             for (int i = 0; i < thumbnails.Length; i++)
85             {
86                 var thumb = thumbnails[i];
87                 var picbox = this.pictureBox[i];
88
89                 picbox.Tag = thumb;
90                 picbox.ContextMenuStrip = this.contextMenuStrip;
91
92                 var loadTask = picbox.SetImageFromTask(() => thumb.LoadThumbnailImageAsync(cancelToken));
93                 loadTasks.Add(loadTask);
94
95                 var tooltipText = thumb.TooltipText;
96                 if (!string.IsNullOrEmpty(tooltipText))
97                 {
98                     this.toolTip.SetToolTip(picbox, tooltipText);
99                 }
100
101                 cancelToken.ThrowIfCancellationRequested();
102             }
103
104             if (thumbnails.Length > 1)
105                 this.scrollBar.Enabled = true;
106
107             if (this.ThumbnailLoading != null)
108                 this.ThumbnailLoading(this, EventArgs.Empty);
109
110             await Task.WhenAll(loadTasks).ConfigureAwait(false);
111         }
112
113         private string GetImageSearchUri(string image_uri)
114         {
115             return @"https://www.google.com/searchbyimage?image_url=" + Uri.EscapeDataString(image_uri);
116         }
117
118         protected virtual Task<IEnumerable<ThumbnailInfo>> GetThumbailInfoAsync(PostClass post, CancellationToken token)
119         {
120             return ThumbnailGenerator.GetThumbnailsAsync(post, token);
121         }
122
123         /// <summary>
124         /// 表示するサムネイルの数を設定する
125         /// </summary>
126         /// <param name="count">表示するサムネイルの数</param>
127         protected void SetThumbnailCount(int count)
128         {
129             if (count == 0 && this.pictureBox.Count == 0)
130                 return;
131
132             using (ControlTransaction.Layout(this.panelPictureBox, false))
133             {
134                 this.panelPictureBox.Controls.Clear();
135                 foreach (var picbox in this.pictureBox)
136                 {
137                     var memoryImage = picbox.Image;
138                     picbox.Dispose();
139
140                     if (memoryImage != null)
141                         memoryImage.Dispose();
142
143                     // メモリリーク対策 (http://stackoverflow.com/questions/2792427#2793714)
144                     picbox.ContextMenuStrip = null;
145                 }
146                 this.pictureBox.Clear();
147
148                 this.scrollBar.Maximum = (count > 0) ? count - 1 : 0;
149                 this.scrollBar.Value = 0;
150
151                 for (int i = 0; i < count; i++)
152                 {
153                     var picbox = CreatePictureBox("pictureBox" + i);
154                     picbox.Visible = (i == 0);
155                     picbox.DoubleClick += this.pictureBox_DoubleClick;
156
157                     this.panelPictureBox.Controls.Add(picbox);
158                     this.pictureBox.Add(picbox);
159                 }
160             }
161         }
162
163         [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")]
164         protected virtual OTPictureBox CreatePictureBox(string name)
165         {
166             return new OTPictureBox()
167             {
168                 Name = name,
169                 SizeMode = PictureBoxSizeMode.Zoom,
170                 WaitOnLoad = false,
171                 Dock = DockStyle.Fill,
172             };
173         }
174
175         public void ScrollUp()
176         {
177             var newval = this.scrollBar.Value - this.scrollBar.SmallChange;
178
179             if (newval < this.scrollBar.Minimum)
180                 newval = this.scrollBar.Minimum;
181
182             this.scrollBar.Value = newval;
183         }
184
185         public void ScrollDown()
186         {
187             var newval = this.scrollBar.Value + this.scrollBar.SmallChange;
188
189             if (newval > this.scrollBar.Maximum)
190                 newval = this.scrollBar.Maximum;
191
192             this.scrollBar.Value = newval;
193         }
194
195         private void scrollBar_ValueChanged(object sender, EventArgs e)
196         {
197             using (ControlTransaction.Layout(this, false))
198             {
199                 var value = this.scrollBar.Value;
200                 for (var i = 0; i < this.pictureBox.Count; i++)
201                 {
202                     this.pictureBox[i].Visible = (i == value);
203                 }
204             }
205         }
206
207         private void pictureBox_DoubleClick(object sender, EventArgs e)
208         {
209             var thumb = ((PictureBox)sender).Tag as ThumbnailInfo;
210
211             if (thumb == null) return;
212
213             if (this.ThumbnailDoubleClick != null)
214             {
215                 this.ThumbnailDoubleClick(this, new ThumbnailDoubleClickEventArgs(thumb));
216             }
217         }
218
219         private void contextMenuStrip_Opening(object sender, CancelEventArgs e)
220         {
221             var picbox = (OTPictureBox)this.contextMenuStrip.SourceControl;
222             var thumb = (ThumbnailInfo)picbox.Tag;
223
224             var searchTargetUri = thumb.FullSizeImageUrl ?? thumb.ThumbnailUrl ?? null;
225             if (searchTargetUri != null)
226             {
227                 this.searchSimilarImageMenuItem.Enabled = true;
228                 this.searchSimilarImageMenuItem.Tag = searchTargetUri;
229             }
230             else
231             {
232                 this.searchSimilarImageMenuItem.Enabled = false;
233             }
234         }
235
236         private void searchSimilarImageMenuItem_Click(object sender, EventArgs e)
237         {
238             var searchTargetUri = (string)this.searchSimilarImageMenuItem.Tag;
239             var searchUri = this.GetImageSearchUri(searchTargetUri);
240
241             if (this.ThumbnailImageSearchClick != null)
242                 this.ThumbnailImageSearchClick(this, new ThumbnailImageSearchEventArgs(searchUri));
243         }
244     }
245
246     public class ThumbnailDoubleClickEventArgs : EventArgs
247     {
248         public ThumbnailInfo Thumbnail { get; private set; }
249
250         public ThumbnailDoubleClickEventArgs(ThumbnailInfo thumbnail)
251         {
252             this.Thumbnail = thumbnail;
253         }
254     }
255
256     public class ThumbnailImageSearchEventArgs : EventArgs
257     {
258         public string ImageUrl { get; private set; }
259
260         public ThumbnailImageSearchEventArgs(string url)
261         {
262             this.ImageUrl = url;
263         }
264     }
265 }