OSDN Git Service

フォーマット修正
[opentween/open-tween.git] / OpenTween / UserInfoDialog.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.ComponentModel;
30 using System.Data;
31 using System.Drawing;
32 using System.Linq;
33 using System.Text;
34 using System.Threading;
35 using System.Threading.Tasks;
36 using System.Windows.Forms;
37 using System.Text.RegularExpressions;
38 using System.Web;
39 using System.IO;
40 using System.Net;
41 using OpenTween.Api;
42 using OpenTween.Connection;
43
44 namespace OpenTween
45 {
46     public partial class UserInfoDialog : OTBaseForm
47     {
48         private TwitterUser _displayUser;
49         private CancellationTokenSource cancellationTokenSource = null;
50
51         private readonly TweenMain mainForm;
52         private readonly Twitter twitter;
53
54         public UserInfoDialog(TweenMain mainForm, Twitter twitter)
55         {
56             this.mainForm = mainForm;
57             this.twitter = twitter;
58
59             InitializeComponent();
60
61             // LabelScreenName のフォントを OTBaseForm.GlobalFont に変更
62             this.LabelScreenName.Font = this.ReplaceToGlobalFont(this.LabelScreenName.Font);
63         }
64
65         private void CancelLoading()
66         {
67             var newTokenSource = new CancellationTokenSource();
68             var oldTokenSource = Interlocked.Exchange(ref this.cancellationTokenSource, newTokenSource);
69
70             if (oldTokenSource != null)
71             {
72                 oldTokenSource.Cancel();
73                 oldTokenSource.Dispose();
74             }
75         }
76
77         public async Task ShowUserAsync(TwitterUser user)
78         {
79             if (this.IsDisposed)
80                 return;
81
82             if (user == null || user == this._displayUser)
83                 return;
84
85             this.CancelLoading();
86
87             var cancellationToken = this.cancellationTokenSource.Token;
88
89             this._displayUser = user;
90
91             this.LabelId.Text = user.IdStr;
92             this.LabelScreenName.Text = user.ScreenName;
93             this.LabelName.Text = user.Name;
94             this.LabelLocation.Text = user.Location ?? "";
95             this.LabelCreatedAt.Text = MyCommon.DateTimeParse(user.CreatedAt).ToString();
96
97             if (user.Protected)
98                 this.LabelIsProtected.Text = Properties.Resources.Yes;
99             else
100                 this.LabelIsProtected.Text = Properties.Resources.No;
101
102             if (user.Verified)
103                 this.LabelIsVerified.Text = Properties.Resources.Yes;
104             else
105                 this.LabelIsVerified.Text = Properties.Resources.No;
106
107             var followingUrl = "https://twitter.com/" + user.ScreenName + "/following";
108             this.LinkLabelFollowing.Text = user.FriendsCount.ToString();
109             this.LinkLabelFollowing.Tag = followingUrl;
110             this.ToolTip1.SetToolTip(this.LinkLabelFollowing, followingUrl);
111
112             var followersUrl = "https://twitter.com/" + user.ScreenName + "/followers";
113             this.LinkLabelFollowers.Text = user.FollowersCount.ToString();
114             this.LinkLabelFollowers.Tag = followersUrl;
115             this.ToolTip1.SetToolTip(this.LinkLabelFollowers, followersUrl);
116
117             var favoritesUrl = "https://twitter.com/" + user.ScreenName + "/favorites";
118             this.LinkLabelFav.Text = user.FavouritesCount.ToString();
119             this.LinkLabelFav.Tag = favoritesUrl;
120             this.ToolTip1.SetToolTip(this.LinkLabelFav, favoritesUrl);
121
122             var profileUrl = "https://twitter.com/" + user.ScreenName;
123             this.LinkLabelTweet.Text = user.StatusesCount.ToString();
124             this.LinkLabelTweet.Tag = profileUrl;
125             this.ToolTip1.SetToolTip(this.LinkLabelTweet, profileUrl);
126
127             if (this.twitter.UserId == user.Id)
128             {
129                 this.ButtonEdit.Enabled = true;
130                 this.ChangeIconToolStripMenuItem.Enabled = true;
131                 this.ButtonBlock.Enabled = false;
132                 this.ButtonReportSpam.Enabled = false;
133                 this.ButtonBlockDestroy.Enabled = false;
134             }
135             else
136             {
137                 this.ButtonEdit.Enabled = false;
138                 this.ChangeIconToolStripMenuItem.Enabled = false;
139                 this.ButtonBlock.Enabled = true;
140                 this.ButtonReportSpam.Enabled = true;
141                 this.ButtonBlockDestroy.Enabled = true;
142             }
143
144             await Task.WhenAll(new[]
145             {
146                 this.SetDescriptionAsync(user.Description, user.Entities.Description, cancellationToken),
147                 this.SetRecentStatusAsync(user.Status, cancellationToken),
148                 this.SetLinkLabelWebAsync(user.Url, user.Entities.Url, cancellationToken),
149                 this.SetUserImageAsync(user.ProfileImageUrlHttps, cancellationToken),
150                 this.LoadFriendshipAsync(user.ScreenName, cancellationToken),
151             });
152         }
153
154         private async Task SetDescriptionAsync(string descriptionText, TwitterEntities entities, CancellationToken cancellationToken)
155         {
156             if (descriptionText != null)
157             {
158                 var atlist = new List<string>();
159
160                 // description に含まれる < > " の記号のみエスケープを一旦解除する
161                 var decodedText = descriptionText.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&quot;", "\"");
162
163                 var html = WebUtility.HtmlEncode(decodedText);
164                 html = await this.twitter.CreateHtmlAnchorAsync(html, atlist, entities, null);
165                 html = this.mainForm.createDetailHtml(html);
166
167                 if (cancellationToken.IsCancellationRequested)
168                     return;
169
170                 this.DescriptionBrowser.DocumentText = html;
171             }
172             else
173             {
174                 this.DescriptionBrowser.DocumentText = "";
175             }
176         }
177
178         private async Task SetUserImageAsync(string imageUri, CancellationToken cancellationToken)
179         {
180             var oldImage = this.UserPicture.Image;
181             this.UserPicture.Image = null;
182             oldImage?.Dispose();
183
184             // ProfileImageUrlHttps が null になる場合があるらしい
185             // 参照: https://sourceforge.jp/ticket/browse.php?group_id=6526&tid=33871
186             if (imageUri == null)
187                 return;
188
189             await this.UserPicture.SetImageFromTask(async () =>
190             {
191                 var uri = imageUri.Replace("_normal", "_bigger");
192
193                 using (var imageStream = await Networking.Http.GetStreamAsync(uri).ConfigureAwait(false))
194                 {
195                     var image = await MemoryImage.CopyFromStreamAsync(imageStream)
196                         .ConfigureAwait(false);
197
198                     if (cancellationToken.IsCancellationRequested)
199                     {
200                         image.Dispose();
201                         throw new OperationCanceledException(cancellationToken);
202                     }
203
204                     return image;
205                 }
206             });
207         }
208
209         private async Task SetLinkLabelWebAsync(string uri, TwitterEntities entities, CancellationToken cancellationToken)
210         {
211             if (entities != null)
212             {
213                 var expandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entities.Urls[0].ExpandedUrl);
214
215                 if (cancellationToken.IsCancellationRequested)
216                     return;
217
218                 this.LinkLabelWeb.Text = expandedUrl;
219                 this.LinkLabelWeb.Tag = expandedUrl;
220                 this.ToolTip1.SetToolTip(this.LinkLabelWeb, expandedUrl);
221             }
222             else if (uri != null)
223             {
224                 var expandedUrl = await ShortUrl.Instance.ExpandUrlAsync(uri);
225
226                 if (cancellationToken.IsCancellationRequested)
227                     return;
228
229                 this.LinkLabelWeb.Text = uri;
230                 this.LinkLabelWeb.Tag = expandedUrl;
231                 this.ToolTip1.SetToolTip(this.LinkLabelWeb, expandedUrl);
232             }
233             else
234             {
235                 this.LinkLabelWeb.Text = "";
236                 this.LinkLabelWeb.Tag = null;
237                 this.ToolTip1.SetToolTip(this.LinkLabelWeb, null);
238             }
239         }
240
241         private async Task SetRecentStatusAsync(TwitterStatus status, CancellationToken cancellationToken)
242         {
243             var atlist = new List<string>();
244
245             if (status != null)
246             {
247                 var html = await this.twitter.CreateHtmlAnchorAsync(status.Text, atlist, status.MergedEntities, null);
248                 html = this.mainForm.createDetailHtml(html +
249                     " Posted at " + MyCommon.DateTimeParse(status.CreatedAt) +
250                     " via " + status.Source);
251
252                 if (cancellationToken.IsCancellationRequested)
253                     return;
254
255                 this.RecentPostBrowser.DocumentText = html;
256             }
257             else
258             {
259                 this.RecentPostBrowser.DocumentText = Properties.Resources.ShowUserInfo2;
260             }
261         }
262
263         private async Task LoadFriendshipAsync(string screenName, CancellationToken cancellationToken)
264         {
265             this.LabelIsFollowing.Text = "";
266             this.LabelIsFollowed.Text = "";
267             this.ButtonFollow.Enabled = false;
268             this.ButtonUnFollow.Enabled = false;
269
270             if (this.twitter.Username == screenName)
271                 return;
272
273             TwitterFriendship friendship;
274             try
275             {
276                 friendship = await Task.Run(() => this.twitter.GetFriendshipInfo(screenName));
277             }
278             catch (WebApiException)
279             {
280                 LabelIsFollowed.Text = Properties.Resources.GetFriendshipInfo6;
281                 LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo6;
282                 return;
283             }
284
285             if (cancellationToken.IsCancellationRequested)
286                 return;
287
288             var isFollowing = friendship.Relationship.Source.Following;
289             var isFollowedBy = friendship.Relationship.Source.FollowedBy;
290
291             this.LabelIsFollowing.Text = isFollowing
292                 ? Properties.Resources.GetFriendshipInfo1
293                 : Properties.Resources.GetFriendshipInfo2;
294
295             this.LabelIsFollowed.Text = isFollowedBy
296                 ? Properties.Resources.GetFriendshipInfo3
297                 : Properties.Resources.GetFriendshipInfo4;
298
299             this.ButtonFollow.Enabled = !isFollowing;
300             this.ButtonUnFollow.Enabled = isFollowing;
301         }
302
303         private void ShowUserInfo_Load(object sender, EventArgs e)
304         {
305             this.TextBoxName.Location = this.LabelName.Location;
306             this.TextBoxName.Height = this.LabelName.Height;
307             this.TextBoxName.Width = this.LabelName.Width;
308             this.TextBoxName.BackColor = this.mainForm.InputBackColor;
309             this.TextBoxName.MaxLength = 20;
310
311             this.TextBoxLocation.Location = this.LabelLocation.Location;
312             this.TextBoxLocation.Height = this.LabelLocation.Height;
313             this.TextBoxLocation.Width = this.LabelLocation.Width;
314             this.TextBoxLocation.BackColor = this.mainForm.InputBackColor;
315             this.TextBoxLocation.MaxLength = 30;
316
317             this.TextBoxWeb.Location = this.LinkLabelWeb.Location;
318             this.TextBoxWeb.Height = this.LinkLabelWeb.Height;
319             this.TextBoxWeb.Width = this.LinkLabelWeb.Width;
320             this.TextBoxWeb.BackColor = this.mainForm.InputBackColor;
321             this.TextBoxWeb.MaxLength = 100;
322
323             this.TextBoxDescription.Location = this.DescriptionBrowser.Location;
324             this.TextBoxDescription.Height = this.DescriptionBrowser.Height;
325             this.TextBoxDescription.Width = this.DescriptionBrowser.Width;
326             this.TextBoxDescription.BackColor = this.mainForm.InputBackColor;
327             this.TextBoxDescription.MaxLength = 160;
328             this.TextBoxDescription.Multiline = true;
329             this.TextBoxDescription.ScrollBars = ScrollBars.Vertical;
330         }
331
332         private async void LinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
333         {
334             var linkLabel = (LinkLabel)sender;
335
336             var linkUrl = (string)linkLabel.Tag;
337             if (linkUrl == null)
338                 return;
339
340             await this.mainForm.OpenUriInBrowserAsync(linkUrl);
341         }
342
343         private void ButtonFollow_Click(object sender, EventArgs e)
344         {
345             try
346             {
347                 this.twitter.PostFollowCommand(this._displayUser.ScreenName);
348             }
349             catch (WebApiException ex)
350             {
351                 MessageBox.Show(Properties.Resources.FRMessage2 + ex.Message);
352                 return;
353             }
354
355             MessageBox.Show(Properties.Resources.FRMessage3);
356             LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo1;
357             ButtonFollow.Enabled = false;
358             ButtonUnFollow.Enabled = true;
359         }
360
361         private void ButtonUnFollow_Click(object sender, EventArgs e)
362         {
363             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonUnFollow_ClickText1,
364                                Properties.Resources.ButtonUnFollow_ClickText2,
365                                MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
366             {
367                 try
368                 {
369                     this.twitter.PostRemoveCommand(this._displayUser.ScreenName);
370                 }
371                 catch (WebApiException ex)
372                 {
373                     MessageBox.Show(Properties.Resources.FRMessage2 + ex.Message);
374                     return;
375                 }
376
377                 MessageBox.Show(Properties.Resources.FRMessage3);
378                 LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo2;
379                 ButtonFollow.Enabled = true;
380                 ButtonUnFollow.Enabled = false;
381             }
382         }
383
384         private void ShowUserInfo_Activated(object sender, EventArgs e)
385         {
386             //画面が他画面の裏に隠れると、アイコン画像が再描画されない問題の対応
387             if (UserPicture.Image != null)
388                 UserPicture.Invalidate(false);
389         }
390
391         private void ShowUserInfo_Shown(object sender, EventArgs e)
392         {
393             ButtonClose.Focus();
394         }
395
396         private async void WebBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
397         {
398             if (e.Url.AbsoluteUri != "about:blank")
399             {
400                 e.Cancel = true;
401
402                 // Ctrlを押しながらリンクを開いた場合は、設定と逆の動作をするフラグを true としておく
403                 await this.mainForm.OpenUriAsync(e.Url, MyCommon.IsKeyDown(Keys.Control));
404             }
405         }
406
407         private void SelectAllToolStripMenuItem_Click(object sender, EventArgs e)
408         {
409             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
410             browser.Document.ExecCommand("SelectAll", false, null);
411         }
412
413         private void SelectionCopyToolStripMenuItem_Click(object sender, EventArgs e)
414         {
415             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
416             var selectedText = browser.GetSelectedText();
417             if (selectedText != null)
418             {
419                 try
420                 {
421                     Clipboard.SetDataObject(selectedText, false, 5, 100);
422                 }
423                 catch (Exception ex)
424                 {
425                     MessageBox.Show(ex.Message);
426                 }
427             }
428         }
429
430         private void ContextMenuRecentPostBrowser_Opening(object sender, CancelEventArgs e)
431         {
432             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
433             var selectedText = browser.GetSelectedText();
434
435             this.SelectionCopyToolStripMenuItem.Enabled = selectedText != null;
436         }
437
438         private async void LinkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
439         {
440             await this.mainForm.OpenUriInBrowserAsync("https://support.twitter.com/groups/31-twitter-basics/topics/111-features/articles/268350-x8a8d-x8a3c-x6e08-x307f-x30a2-x30ab-x30a6-x30f3-x30c8-x306b-x3064-x3044-x3066");
441         }
442
443         private async void LinkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
444         {
445             await this.mainForm.OpenUriInBrowserAsync("https://support.twitter.com/groups/31-twitter-basics/topics/107-my-profile-account-settings/articles/243055-x516c-x958b-x3001-x975e-x516c-x958b-x30a2-x30ab-x30a6-x30f3-x30c8-x306b-x3064-x3044-x3066");
446         }
447
448         private void ButtonSearchPosts_Click(object sender, EventArgs e)
449         {
450             this.mainForm.AddNewTabForUserTimeline(this._displayUser.ScreenName);
451         }
452
453         private async void UserPicture_Click(object sender, EventArgs e)
454         {
455             var imageUrl = this._displayUser.ProfileImageUrlHttps;
456             imageUrl = imageUrl.Remove(imageUrl.LastIndexOf("_normal"), 7);
457
458             await this.mainForm.OpenUriInBrowserAsync(imageUrl);
459         }
460
461         private bool IsEditing = false;
462         private string ButtonEditText = "";
463
464         private async void ButtonEdit_Click(object sender, EventArgs e)
465         {
466             // 自分以外のプロフィールは変更できない
467             if (this.twitter.UserId != this._displayUser.Id)
468                 return;
469
470             using (ControlTransaction.Disabled(this.ButtonEdit))
471             {
472                 if (!IsEditing)
473                 {
474                     ButtonEditText = ButtonEdit.Text;
475                     ButtonEdit.Text = Properties.Resources.UserInfoButtonEdit_ClickText1;
476
477                     TextBoxName.Text = LabelName.Text;
478                     TextBoxName.Enabled = true;
479                     TextBoxName.Visible = true;
480                     LabelName.Visible = false;
481
482                     TextBoxLocation.Text = LabelLocation.Text;
483                     TextBoxLocation.Enabled = true;
484                     TextBoxLocation.Visible = true;
485                     LabelLocation.Visible = false;
486
487                     TextBoxWeb.Text = this._displayUser.Url;
488                     TextBoxWeb.Enabled = true;
489                     TextBoxWeb.Visible = true;
490                     LinkLabelWeb.Visible = false;
491
492                     TextBoxDescription.Text = this._displayUser.Description;
493                     TextBoxDescription.Enabled = true;
494                     TextBoxDescription.Visible = true;
495                     DescriptionBrowser.Visible = false;
496
497                     TextBoxName.Focus();
498                     TextBoxName.Select(TextBoxName.Text.Length, 0);
499
500                     IsEditing = true;
501                 }
502                 else
503                 {
504                     Task showUserTask = null;
505
506                     if (TextBoxName.Modified ||
507                         TextBoxLocation.Modified ||
508                         TextBoxWeb.Modified ||
509                         TextBoxDescription.Modified)
510                     {
511                         try
512                         {
513                             var user = await Task.Run(() =>
514                                 this.twitter.PostUpdateProfile(
515                                     this.TextBoxName.Text,
516                                     this.TextBoxWeb.Text,
517                                     this.TextBoxLocation.Text,
518                                     this.TextBoxDescription.Text));
519
520                             showUserTask = this.ShowUserAsync(user);
521                         }
522                         catch (WebApiException ex)
523                         {
524                             MessageBox.Show(ex.Message);
525                             return;
526                         }
527                     }
528
529                     TextBoxName.Enabled = false;
530                     TextBoxName.Visible = false;
531                     LabelName.Visible = true;
532
533                     TextBoxLocation.Enabled = false;
534                     TextBoxLocation.Visible = false;
535                     LabelLocation.Visible = true;
536
537                     TextBoxWeb.Enabled = false;
538                     TextBoxWeb.Visible = false;
539                     LinkLabelWeb.Visible = true;
540
541                     TextBoxDescription.Enabled = false;
542                     TextBoxDescription.Visible = false;
543                     DescriptionBrowser.Visible = true;
544
545                     ButtonEdit.Text = ButtonEditText;
546
547                     IsEditing = false;
548
549                     if (showUserTask != null)
550                         await showUserTask;
551                 }
552             }
553         }
554
555         private async Task DoChangeIcon(string filename)
556         {
557             try
558             {
559                 await Task.Run(() => this.twitter.PostUpdateProfileImage(filename));
560             }
561             catch (WebApiException ex)
562             {
563                 // "Err:"が付いたエラーメッセージが返ってくる
564                 MessageBox.Show(ex.Message + Environment.NewLine + Properties.Resources.ChangeIconToolStripMenuItem_ClickText4);
565                 return;
566             }
567
568             MessageBox.Show(Properties.Resources.ChangeIconToolStripMenuItem_ClickText5);
569
570             try
571             {
572                 var user = await Task.Run(() => this.twitter.GetUserInfo(this._displayUser.ScreenName));
573
574                 if (user != null)
575                     await this.ShowUserAsync(user);
576             }
577             catch (WebApiException ex)
578             {
579                 MessageBox.Show(ex.Message);
580             }
581         }
582
583         private async void ChangeIconToolStripMenuItem_Click(object sender, EventArgs e)
584         {
585             OpenFileDialogIcon.Filter = Properties.Resources.ChangeIconToolStripMenuItem_ClickText1;
586             OpenFileDialogIcon.Title = Properties.Resources.ChangeIconToolStripMenuItem_ClickText2;
587             OpenFileDialogIcon.FileName = "";
588
589             DialogResult rslt = OpenFileDialogIcon.ShowDialog();
590
591             if (rslt != DialogResult.OK)
592             {
593                 return;
594             }
595
596             string fn = OpenFileDialogIcon.FileName;
597             if (this.IsValidIconFile(new FileInfo(fn)))
598             {
599                 await this.DoChangeIcon(fn);
600             }
601             else
602             {
603                 MessageBox.Show(Properties.Resources.ChangeIconToolStripMenuItem_ClickText6);
604             }
605         }
606
607         private void ButtonBlock_Click(object sender, EventArgs e)
608         {
609             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonBlock_ClickText1,
610                                 Properties.Resources.ButtonBlock_ClickText2,
611                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
612             {
613                 try
614                 {
615                     this.twitter.PostCreateBlock(this._displayUser.ScreenName);
616                 }
617                 catch (WebApiException ex)
618                 {
619                     MessageBox.Show(ex.Message + Environment.NewLine + Properties.Resources.ButtonBlock_ClickText3);
620                     return;
621                 }
622
623                 MessageBox.Show(Properties.Resources.ButtonBlock_ClickText4);
624             }
625         }
626
627         private void ButtonReportSpam_Click(object sender, EventArgs e)
628         {
629             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonReportSpam_ClickText1,
630                                 Properties.Resources.ButtonReportSpam_ClickText2,
631                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
632             {
633                 try
634                 {
635                     this.twitter.PostReportSpam(this._displayUser.ScreenName);
636                 }
637                 catch (WebApiException ex)
638                 {
639                     MessageBox.Show(ex.Message + Environment.NewLine + Properties.Resources.ButtonReportSpam_ClickText3);
640                     return;
641                 }
642
643                 MessageBox.Show(Properties.Resources.ButtonReportSpam_ClickText4);
644             }
645         }
646
647         private void ButtonBlockDestroy_Click(object sender, EventArgs e)
648         {
649             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonBlockDestroy_ClickText1,
650                                 Properties.Resources.ButtonBlockDestroy_ClickText2,
651                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
652             {
653                 try
654                 {
655                     this.twitter.PostDestroyBlock(this._displayUser.ScreenName);
656                 }
657                 catch (WebApiException ex)
658                 {
659                     MessageBox.Show(ex.Message + Environment.NewLine + Properties.Resources.ButtonBlockDestroy_ClickText3);
660                     return;
661                 }
662
663                 MessageBox.Show(Properties.Resources.ButtonBlockDestroy_ClickText4);
664             }
665         }
666
667         private bool IsValidExtension(string ext)
668         {
669             ext = ext.ToLower();
670
671             return ext.Equals(".jpg", StringComparison.Ordinal) ||
672                 ext.Equals(".jpeg", StringComparison.Ordinal) ||
673                 ext.Equals(".png", StringComparison.Ordinal) ||
674                 ext.Equals(".gif", StringComparison.Ordinal);
675         }
676
677         private bool IsValidIconFile(FileInfo info)
678         {
679             return this.IsValidExtension(info.Extension) &&
680                 info.Length < 700 * 1024 &&
681                 !MyCommon.IsAnimatedGif(info.FullName);
682         }
683
684         private void ShowUserInfo_DragEnter(object sender, DragEventArgs e)
685         {
686             if (e.Data.GetDataPresent(DataFormats.FileDrop) &&
687                 !e.Data.GetDataPresent(DataFormats.Html, false))  // WebBrowserコントロールからの絵文字画像D&Dは弾く
688             {
689                 var files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
690                 if (files.Length != 1)
691                     return;
692
693                 var finfo = new FileInfo(files[0]);
694                 if (this.IsValidIconFile(finfo))
695                 {
696                     e.Effect = DragDropEffects.Copy;
697                 }
698             }
699         }
700
701         private async void ShowUserInfo_DragDrop(object sender, DragEventArgs e)
702         {
703             if (e.Data.GetDataPresent(DataFormats.FileDrop) &&
704                 !e.Data.GetDataPresent(DataFormats.Html, false))  // WebBrowserコントロールからの絵文字画像D&Dは弾く
705             {
706                 var ret = MessageBox.Show(this, Properties.Resources.ChangeIconToolStripMenuItem_Confirm,
707                     Application.ProductName, MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
708                 if (ret != DialogResult.OK)
709                     return;
710
711                 string filename = ((string[])e.Data.GetData(DataFormats.FileDrop, false))[0];
712                 await this.DoChangeIcon(filename);
713             }
714         }
715
716         protected override void Dispose(bool disposing)
717         {
718             if (disposing)
719             {
720                 var cts = this.cancellationTokenSource;
721                 cts.Cancel();
722                 cts.Dispose();
723
724                 var oldImage = this.UserPicture.Image;
725                 this.UserPicture.Image = null;
726                 oldImage?.Dispose();
727
728                 this.components?.Dispose();
729             }
730
731             base.Dispose(disposing);
732         }
733     }
734 }