OSDN Git Service

c59578ab9027d7b04189ea5a0447264b93560a20
[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 (user == null || user == this._displayUser)
80                 return;
81
82             this.CancelLoading();
83
84             var cancellationToken = this.cancellationTokenSource.Token;
85
86             this._displayUser = user;
87
88             this.LabelId.Text = user.IdStr;
89             this.LabelScreenName.Text = user.ScreenName;
90             this.LabelName.Text = user.Name;
91             this.LabelLocation.Text = user.Location ?? "";
92             this.LabelCreatedAt.Text = MyCommon.DateTimeParse(user.CreatedAt).ToString();
93
94             if (user.Protected)
95                 this.LabelIsProtected.Text = Properties.Resources.Yes;
96             else
97                 this.LabelIsProtected.Text = Properties.Resources.No;
98
99             if (user.Verified)
100                 this.LabelIsVerified.Text = Properties.Resources.Yes;
101             else
102                 this.LabelIsVerified.Text = Properties.Resources.No;
103
104             var followingUrl = "https://twitter.com/" + user.ScreenName + "/following";
105             this.LinkLabelFollowing.Text = user.FriendsCount.ToString();
106             this.LinkLabelFollowing.Tag = followingUrl;
107             this.ToolTip1.SetToolTip(this.LinkLabelFollowing, followingUrl);
108
109             var followersUrl = "https://twitter.com/" + user.ScreenName + "/followers";
110             this.LinkLabelFollowers.Text = user.FollowersCount.ToString();
111             this.LinkLabelFollowers.Tag = followersUrl;
112             this.ToolTip1.SetToolTip(this.LinkLabelFollowers, followersUrl);
113
114             var favoritesUrl = "https://twitter.com/" + user.ScreenName + "/favorites";
115             this.LinkLabelFav.Text = user.FavouritesCount.ToString();
116             this.LinkLabelFav.Tag = favoritesUrl;
117             this.ToolTip1.SetToolTip(this.LinkLabelFav, favoritesUrl);
118
119             var profileUrl = "https://twitter.com/" + user.ScreenName;
120             this.LinkLabelTweet.Text = user.StatusesCount.ToString();
121             this.LinkLabelTweet.Tag = profileUrl;
122             this.ToolTip1.SetToolTip(this.LinkLabelTweet, profileUrl);
123
124             if (this.twitter.UserId == user.Id)
125             {
126                 this.ButtonEdit.Enabled = true;
127                 this.ChangeIconToolStripMenuItem.Enabled = true;
128                 this.ButtonBlock.Enabled = false;
129                 this.ButtonReportSpam.Enabled = false;
130                 this.ButtonBlockDestroy.Enabled = false;
131             }
132             else
133             {
134                 this.ButtonEdit.Enabled = false;
135                 this.ChangeIconToolStripMenuItem.Enabled = false;
136                 this.ButtonBlock.Enabled = true;
137                 this.ButtonReportSpam.Enabled = true;
138                 this.ButtonBlockDestroy.Enabled = true;
139             }
140
141             await Task.WhenAll(new[]
142             {
143                 this.SetDescriptionAsync(user.Description, cancellationToken),
144                 this.SetRecentStatusAsync(user.Status, cancellationToken),
145                 this.SetLinkLabelWebAsync(user.Url, cancellationToken),
146                 this.SetUserImageAsync(user.ProfileImageUrlHttps, cancellationToken),
147                 this.LoadFriendshipAsync(user.ScreenName, cancellationToken),
148             });
149         }
150
151         private async Task SetDescriptionAsync(string descriptionText, CancellationToken cancellationToken)
152         {
153             if (descriptionText != null)
154             {
155                 var atlist = new List<string>();
156
157                 // description に含まれる < > " の記号のみエスケープを一旦解除する
158                 var decodedText = descriptionText.Replace("&lt;", "<").Replace("&gt;", ">").Replace("&quot;", "\"");
159
160                 var html = WebUtility.HtmlEncode(decodedText);
161                 html = await this.twitter.CreateHtmlAnchorAsync(html, atlist, null);
162                 html = this.mainForm.createDetailHtml(html);
163
164                 if (cancellationToken.IsCancellationRequested)
165                     return;
166
167                 this.DescriptionBrowser.DocumentText = html;
168             }
169             else
170             {
171                 this.DescriptionBrowser.DocumentText = "";
172             }
173         }
174
175         private async Task SetUserImageAsync(string imageUri, CancellationToken cancellationToken)
176         {
177             var oldImage = this.UserPicture.Image;
178             if (oldImage != null)
179             {
180                 this.UserPicture.Image = null;
181                 oldImage.Dispose();
182             }
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, CancellationToken cancellationToken)
210         {
211             if (uri != null)
212             {
213                 var expandedUrl = await ShortUrl.Instance.ExpandUrlAsync(uri);
214
215                 if (cancellationToken.IsCancellationRequested)
216                     return;
217
218                 this.LinkLabelWeb.Text = uri;
219                 this.LinkLabelWeb.Tag = expandedUrl;
220                 this.ToolTip1.SetToolTip(this.LinkLabelWeb, expandedUrl);
221             }
222             else
223             {
224                 this.LinkLabelWeb.Text = "";
225                 this.LinkLabelWeb.Tag = null;
226                 this.ToolTip1.SetToolTip(this.LinkLabelWeb, null);
227             }
228         }
229
230         private async Task SetRecentStatusAsync(TwitterStatus status, CancellationToken cancellationToken)
231         {
232             var atlist = new List<string>();
233
234             if (status != null)
235             {
236                 var html = await this.twitter.CreateHtmlAnchorAsync(status.Text, atlist, status.MergedEntities, null);
237                 html = this.mainForm.createDetailHtml(html +
238                     " Posted at " + MyCommon.DateTimeParse(status.CreatedAt) +
239                     " via " + status.Source);
240
241                 if (cancellationToken.IsCancellationRequested)
242                     return;
243
244                 this.RecentPostBrowser.DocumentText = html;
245             }
246             else
247             {
248                 this.RecentPostBrowser.DocumentText = Properties.Resources.ShowUserInfo2;
249             }
250         }
251
252         private async Task LoadFriendshipAsync(string screenName, CancellationToken cancellationToken)
253         {
254             this.LabelIsFollowing.Text = "";
255             this.LabelIsFollowed.Text = "";
256             this.ButtonFollow.Enabled = false;
257             this.ButtonUnFollow.Enabled = false;
258
259             if (this.twitter.Username == screenName)
260                 return;
261
262             var friendship = await Task.Run(() =>
263             {
264                 var IsFollowing = false;
265                 var IsFollowedBy = false;
266
267                 var ret = this.twitter.GetFriendshipInfo(screenName, ref IsFollowing, ref IsFollowedBy);
268                 if (!string.IsNullOrEmpty(ret))
269                     return null;
270
271                 return new { IsFollowing, IsFollowedBy };
272             });
273
274             if (cancellationToken.IsCancellationRequested)
275                 return;
276
277             if (friendship == null)
278             {
279                 LabelIsFollowed.Text = Properties.Resources.GetFriendshipInfo6;
280                 LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo6;
281                 return;
282             }
283
284             this.LabelIsFollowing.Text = friendship.IsFollowing
285                 ? Properties.Resources.GetFriendshipInfo1
286                 : Properties.Resources.GetFriendshipInfo2;
287
288             this.LabelIsFollowed.Text = friendship.IsFollowedBy
289                 ? Properties.Resources.GetFriendshipInfo3
290                 : Properties.Resources.GetFriendshipInfo4;
291
292             this.ButtonFollow.Enabled = !friendship.IsFollowing;
293             this.ButtonUnFollow.Enabled = friendship.IsFollowing;
294         }
295
296         private void ShowUserInfo_Load(object sender, EventArgs e)
297         {
298             this.TextBoxName.Location = this.LabelName.Location;
299             this.TextBoxName.Height = this.LabelName.Height;
300             this.TextBoxName.Width = this.LabelName.Width;
301             this.TextBoxName.BackColor = this.mainForm.InputBackColor;
302             this.TextBoxName.MaxLength = 20;
303
304             this.TextBoxLocation.Location = this.LabelLocation.Location;
305             this.TextBoxLocation.Height = this.LabelLocation.Height;
306             this.TextBoxLocation.Width = this.LabelLocation.Width;
307             this.TextBoxLocation.BackColor = this.mainForm.InputBackColor;
308             this.TextBoxLocation.MaxLength = 30;
309
310             this.TextBoxWeb.Location = this.LinkLabelWeb.Location;
311             this.TextBoxWeb.Height = this.LinkLabelWeb.Height;
312             this.TextBoxWeb.Width = this.LinkLabelWeb.Width;
313             this.TextBoxWeb.BackColor = this.mainForm.InputBackColor;
314             this.TextBoxWeb.MaxLength = 100;
315
316             this.TextBoxDescription.Location = this.DescriptionBrowser.Location;
317             this.TextBoxDescription.Height = this.DescriptionBrowser.Height;
318             this.TextBoxDescription.Width = this.DescriptionBrowser.Width;
319             this.TextBoxDescription.BackColor = this.mainForm.InputBackColor;
320             this.TextBoxDescription.MaxLength = 160;
321             this.TextBoxDescription.Multiline = true;
322             this.TextBoxDescription.ScrollBars = ScrollBars.Vertical;
323         }
324
325         private async void LinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
326         {
327             var linkLabel = (LinkLabel)sender;
328
329             var linkUrl = (string)linkLabel.Tag;
330             if (linkUrl == null)
331                 return;
332
333             await this.mainForm.OpenUriAsync(linkUrl);
334         }
335
336         private void ButtonFollow_Click(object sender, EventArgs e)
337         {
338             string ret = this.twitter.PostFollowCommand(this._displayUser.ScreenName);
339             if (!string.IsNullOrEmpty(ret))
340             {
341                 MessageBox.Show(Properties.Resources.FRMessage2 + ret);
342             }
343             else
344             {
345                 MessageBox.Show(Properties.Resources.FRMessage3);
346                 LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo1;
347                 ButtonFollow.Enabled = false;
348                 ButtonUnFollow.Enabled = true;
349             }
350         }
351
352         private void ButtonUnFollow_Click(object sender, EventArgs e)
353         {
354             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonUnFollow_ClickText1,
355                                Properties.Resources.ButtonUnFollow_ClickText2,
356                                MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
357             {
358                 string ret = this.twitter.PostRemoveCommand(this._displayUser.ScreenName);
359                 if (!string.IsNullOrEmpty(ret))
360                 {
361                     MessageBox.Show(Properties.Resources.FRMessage2 + ret);
362                 }
363                 else
364                 {
365                     MessageBox.Show(Properties.Resources.FRMessage3);
366                     LabelIsFollowing.Text = Properties.Resources.GetFriendshipInfo2;
367                     ButtonFollow.Enabled = true;
368                     ButtonUnFollow.Enabled = false;
369                 }
370             }
371         }
372
373         private void ShowUserInfo_Activated(object sender, EventArgs e)
374         {
375             //画面が他画面の裏に隠れると、アイコン画像が再描画されない問題の対応
376             if (UserPicture.Image != null)
377                 UserPicture.Invalidate(false);
378         }
379
380         private void ShowUserInfo_Shown(object sender, EventArgs e)
381         {
382             ButtonClose.Focus();
383         }
384
385         private void WebBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
386         {
387             if (e.Url.AbsoluteUri != "about:blank")
388             {
389                 e.Cancel = true;
390
391                 if (e.Url.AbsoluteUri.StartsWith("http://twitter.com/search?q=%23") ||
392                     e.Url.AbsoluteUri.StartsWith("https://twitter.com/search?q=%23"))
393                 {
394                     //ハッシュタグの場合は、タブで開く
395                     string urlStr = Uri.UnescapeDataString(e.Url.AbsoluteUri);
396                     string hash = urlStr.Substring(urlStr.IndexOf("#"));
397                     this.mainForm.HashSupl.AddItem(hash);
398                     this.mainForm.HashMgr.AddHashToHistory(hash.Trim(), false);
399                     this.mainForm.AddNewTabForSearch(hash);
400                     return;
401                 }
402                 else
403                 {
404                     Match m = Regex.Match(e.Url.AbsoluteUri, @"^https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)$");
405                     if (SettingCommon.Instance.OpenUserTimeline && m.Success && this.mainForm.IsTwitterId(m.Result("${ScreenName}")))
406                     {
407                         this.mainForm.AddNewTabForUserTimeline(m.Result("${ScreenName}"));
408                     }
409                     else
410                     {
411                         this.mainForm.OpenUriAsync(e.Url.OriginalString);
412                     }
413                 }
414             }
415         }
416
417         private void SelectAllToolStripMenuItem_Click(object sender, EventArgs e)
418         {
419             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
420             browser.Document.ExecCommand("SelectAll", false, null);
421         }
422
423         private void SelectionCopyToolStripMenuItem_Click(object sender, EventArgs e)
424         {
425             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
426             var selectedText = this.mainForm.WebBrowser_GetSelectionText(ref browser);
427             if (selectedText != null)
428             {
429                 try
430                 {
431                     Clipboard.SetDataObject(selectedText, false, 5, 100);
432                 }
433                 catch (Exception ex)
434                 {
435                     MessageBox.Show(ex.Message);
436                 }
437             }
438         }
439
440         private void ContextMenuRecentPostBrowser_Opening(object sender, CancelEventArgs e)
441         {
442             var browser = (WebBrowser)this.ContextMenuRecentPostBrowser.SourceControl;
443             var selectedText = this.mainForm.WebBrowser_GetSelectionText(ref browser);
444
445             this.SelectionCopyToolStripMenuItem.Enabled = selectedText != null;
446         }
447
448         private void LinkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
449         {
450             this.mainForm.OpenUriAsync("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");
451         }
452
453         private void LinkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
454         {
455             this.mainForm.OpenUriAsync("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");
456         }
457
458         private void ButtonSearchPosts_Click(object sender, EventArgs e)
459         {
460             this.mainForm.AddNewTabForUserTimeline(this._displayUser.ScreenName);
461         }
462
463         private async void UserPicture_Click(object sender, EventArgs e)
464         {
465             var imageUrl = this._displayUser.ProfileImageUrlHttps;
466             imageUrl = imageUrl.Remove(imageUrl.LastIndexOf("_normal"), 7);
467
468             await this.mainForm.OpenUriAsync(imageUrl);
469         }
470
471         private bool IsEditing = false;
472         private string ButtonEditText = "";
473
474         private async void ButtonEdit_Click(object sender, EventArgs e)
475         {
476             // 自分以外のプロフィールは変更できない
477             if (this.twitter.UserId != this._displayUser.Id)
478                 return;
479
480             this.ButtonEdit.Enabled = false;
481
482             if (!IsEditing)
483             {
484                 ButtonEditText = ButtonEdit.Text;
485                 ButtonEdit.Text = Properties.Resources.UserInfoButtonEdit_ClickText1;
486
487                 TextBoxName.Text = LabelName.Text;
488                 TextBoxName.Enabled = true;
489                 TextBoxName.Visible = true;
490                 LabelName.Visible = false;
491
492                 TextBoxLocation.Text = LabelLocation.Text;
493                 TextBoxLocation.Enabled = true;
494                 TextBoxLocation.Visible = true;
495                 LabelLocation.Visible = false;
496
497                 TextBoxWeb.Text = this._displayUser.Url;
498                 TextBoxWeb.Enabled = true;
499                 TextBoxWeb.Visible = true;
500                 LinkLabelWeb.Visible = false;
501
502                 TextBoxDescription.Text = this._displayUser.Description;
503                 TextBoxDescription.Enabled = true;
504                 TextBoxDescription.Visible = true;
505                 DescriptionBrowser.Visible = false;
506
507                 TextBoxName.Focus();
508                 TextBoxName.Select(TextBoxName.Text.Length, 0);
509
510                 IsEditing = true;
511             }
512             else
513             {
514                 Task showUserTask = null;
515
516                 if (TextBoxName.Modified ||
517                     TextBoxLocation.Modified ||
518                     TextBoxWeb.Modified ||
519                     TextBoxDescription.Modified)
520                 {
521                     try
522                     {
523                         var user = await Task.Run(() =>
524                             this.twitter.PostUpdateProfile(
525                                 this.TextBoxName.Text,
526                                 this.TextBoxWeb.Text,
527                                 this.TextBoxLocation.Text,
528                                 this.TextBoxDescription.Text));
529
530                         showUserTask = this.ShowUserAsync(user);
531                     }
532                     catch (WebApiException ex)
533                     {
534                         MessageBox.Show(ex.Message);
535                         return;
536                     }
537                 }
538
539                 TextBoxName.Enabled = false;
540                 TextBoxName.Visible = false;
541                 LabelName.Visible = true;
542
543                 TextBoxLocation.Enabled = false;
544                 TextBoxLocation.Visible = false;
545                 LabelLocation.Visible = true;
546
547                 TextBoxWeb.Enabled = false;
548                 TextBoxWeb.Visible = false;
549                 LinkLabelWeb.Visible = true;
550
551                 TextBoxDescription.Enabled = false;
552                 TextBoxDescription.Visible = false;
553                 DescriptionBrowser.Visible = true;
554
555                 ButtonEdit.Text = ButtonEditText;
556
557                 IsEditing = false;
558
559                 if (showUserTask != null)
560                     await showUserTask;
561             }
562
563             this.ButtonEdit.Enabled = true;
564         }
565
566         private async Task DoChangeIcon(string filename)
567         {
568             var ret = await Task.Run(() =>
569                 this.twitter.PostUpdateProfileImage(filename));
570
571             if (!string.IsNullOrEmpty(ret))
572             {
573                 // "Err:"が付いたエラーメッセージが返ってくる
574                 MessageBox.Show(ret + Environment.NewLine + Properties.Resources.ChangeIconToolStripMenuItem_ClickText4);
575             }
576             else
577             {
578                 MessageBox.Show(Properties.Resources.ChangeIconToolStripMenuItem_ClickText5);
579             }
580
581             try
582             {
583                 var user = await Task.Run(() =>
584                 {
585                     TwitterUser result = null;
586
587                     var err = this.twitter.GetUserInfo(this._displayUser.ScreenName, ref result);
588                     if (!string.IsNullOrEmpty(err))
589                         throw new WebApiException(err);
590
591                     return result;
592                 });
593
594                 if (user != null)
595                     await this.ShowUserAsync(user);
596             }
597             catch (WebApiException ex)
598             {
599                 MessageBox.Show(ex.Message);
600             }
601         }
602
603         private async void ChangeIconToolStripMenuItem_Click(object sender, EventArgs e)
604         {
605             OpenFileDialogIcon.Filter = Properties.Resources.ChangeIconToolStripMenuItem_ClickText1;
606             OpenFileDialogIcon.Title = Properties.Resources.ChangeIconToolStripMenuItem_ClickText2;
607             OpenFileDialogIcon.FileName = "";
608
609             DialogResult rslt = OpenFileDialogIcon.ShowDialog();
610
611             if (rslt != DialogResult.OK)
612             {
613                 return;
614             }
615
616             string fn = OpenFileDialogIcon.FileName;
617             if (this.IsValidIconFile(new FileInfo(fn)))
618             {
619                 await this.DoChangeIcon(fn);
620             }
621             else
622             {
623                 MessageBox.Show(Properties.Resources.ChangeIconToolStripMenuItem_ClickText6);
624             }
625         }
626
627         private void ButtonBlock_Click(object sender, EventArgs e)
628         {
629             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonBlock_ClickText1,
630                                 Properties.Resources.ButtonBlock_ClickText2,
631                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
632             {
633                 string res = this.twitter.PostCreateBlock(this._displayUser.ScreenName);
634                 if (!string.IsNullOrEmpty(res))
635                 {
636                     MessageBox.Show(res + Environment.NewLine + Properties.Resources.ButtonBlock_ClickText3);
637                 }
638                 else
639                 {
640                     MessageBox.Show(Properties.Resources.ButtonBlock_ClickText4);
641                 }
642             }
643         }
644
645         private void ButtonReportSpam_Click(object sender, EventArgs e)
646         {
647             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonReportSpam_ClickText1,
648                                 Properties.Resources.ButtonReportSpam_ClickText2,
649                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
650             {
651                 string res = this.twitter.PostReportSpam(this._displayUser.ScreenName);
652                 if (!string.IsNullOrEmpty(res))
653                 {
654                     MessageBox.Show(res + Environment.NewLine + Properties.Resources.ButtonReportSpam_ClickText3);
655                 }
656                 else
657                 {
658                     MessageBox.Show(Properties.Resources.ButtonReportSpam_ClickText4);
659                 }
660             }
661         }
662
663         private void ButtonBlockDestroy_Click(object sender, EventArgs e)
664         {
665             if (MessageBox.Show(this._displayUser.ScreenName + Properties.Resources.ButtonBlockDestroy_ClickText1,
666                                 Properties.Resources.ButtonBlockDestroy_ClickText2,
667                                 MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
668             {
669                 string res = this.twitter.PostDestroyBlock(this._displayUser.ScreenName);
670                 if (!string.IsNullOrEmpty(res))
671                 {
672                     MessageBox.Show(res + Environment.NewLine + Properties.Resources.ButtonBlockDestroy_ClickText3);
673                 }
674                 else
675                 {
676                     MessageBox.Show(Properties.Resources.ButtonBlockDestroy_ClickText4);
677                 }
678             }
679         }
680
681         private bool IsValidExtension(string ext)
682         {
683             ext = ext.ToLower();
684
685             return ext.Equals(".jpg", StringComparison.Ordinal) ||
686                 ext.Equals(".jpeg", StringComparison.Ordinal) ||
687                 ext.Equals(".png", StringComparison.Ordinal) ||
688                 ext.Equals(".gif", StringComparison.Ordinal);
689         }
690
691         private bool IsValidIconFile(FileInfo info)
692         {
693             return this.IsValidExtension(info.Extension) &&
694                 info.Length < 700 * 1024 &&
695                 !MyCommon.IsAnimatedGif(info.FullName);
696         }
697
698         private void ShowUserInfo_DragOver(object sender, DragEventArgs e)
699         {
700             if (e.Data.GetDataPresent(DataFormats.FileDrop))
701             {
702                 var files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
703                 if (files.Length != 1)
704                     return;
705
706                 var finfo = new FileInfo(files[0]);
707                 if (this.IsValidIconFile(finfo))
708                 {
709                     e.Effect = DragDropEffects.Copy;
710                 }
711             }
712         }
713
714         private async void ShowUserInfo_DragDrop(object sender, DragEventArgs e)
715         {
716             if (e.Data.GetDataPresent(DataFormats.FileDrop))
717             {
718                 string filename = ((string[])e.Data.GetData(DataFormats.FileDrop, false))[0];
719                 await this.DoChangeIcon(filename);
720             }
721         }
722
723         protected override void Dispose(bool disposing)
724         {
725             if (disposing)
726             {
727                 var cts = this.cancellationTokenSource;
728                 cts.Cancel();
729                 cts.Dispose();
730
731                 var oldImage = this.UserPicture.Image;
732                 if (oldImage != null)
733                 {
734                     this.UserPicture.Image = null;
735                     oldImage.Dispose();
736                 }
737
738                 if (this.components != null)
739                     this.components.Dispose();
740             }
741
742             base.Dispose(disposing);
743         }
744     }
745 }