OSDN Git Service

2a3b97d8d410a328380f85e4c2537138c4c90784
[opentween/open-tween.git] / OpenTween / MediaSelector.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2014 spx (@5px)
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.Drawing;
26 using System.Data;
27 using System.IO;
28 using System.Linq;
29 using System.Text;
30 using System.Threading.Tasks;
31 using System.Windows.Forms;
32 using OpenTween.Api.DataModel;
33 using OpenTween.Connection;
34
35 namespace OpenTween
36 {
37     public partial class MediaSelector : UserControl
38     {
39         public event EventHandler BeginSelecting;
40         public event EventHandler EndSelecting;
41
42         public event EventHandler FilePickDialogOpening;
43         public event EventHandler FilePickDialogClosed;
44
45         public event EventHandler SelectedServiceChanged;
46
47         [Browsable(false)]
48         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
49         public OpenFileDialog FilePickDialog { get; set; }
50
51         /// <summary>
52         /// 選択されている投稿先名を取得する。
53         /// </summary>
54         [Browsable(false)]
55         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
56         public string ServiceName
57         {
58             get { return ImageServiceCombo.Text; }
59         }
60
61         /// <summary>
62         /// 選択されている投稿先を示すインデックスを取得する。
63         /// </summary>
64         [Browsable(false)]
65         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
66         public int ServiceIndex
67         {
68             get { return ImageServiceCombo.SelectedIndex; }
69         }
70
71         /// <summary>
72         /// 選択されている投稿先の IMediaUploadService を取得する。
73         /// </summary>
74         [Browsable(false)]
75         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
76         public IMediaUploadService SelectedService
77         {
78             get
79             {
80                 var serviceName = this.ServiceName;
81                 if (string.IsNullOrEmpty(serviceName))
82                     return null;
83
84                 IMediaUploadService service;
85                 return this.pictureService.TryGetValue(serviceName, out service) ? service : null;
86             }
87         }
88
89         /// <summary>
90         /// 指定された投稿先名から、作成済みの IMediaUploadService インスタンスを取得する。
91         /// </summary>
92         public IMediaUploadService GetService(string serviceName)
93         {
94             IMediaUploadService service;
95             this.pictureService.TryGetValue(serviceName, out service);
96             return service;
97         }
98
99         /// <summary>
100         /// 利用可能な全ての IMediaUploadService インスタンスを取得する。
101         /// </summary>
102         public ICollection<IMediaUploadService> GetServices()
103         {
104             return this.pictureService.Values;
105         }
106
107         private class SelectedMedia
108         {
109             public IMediaItem Item { get; set; }
110             public MyCommon.UploadFileType Type { get; set; }
111             public string Text { get; set; }
112
113             public SelectedMedia(IMediaItem item, MyCommon.UploadFileType type, string text)
114             {
115                 this.Item = item;
116                 this.Type = type;
117                 this.Text = text;
118             }
119
120             public SelectedMedia(string text)
121                 : this(null, MyCommon.UploadFileType.Invalid, text)
122             {
123             }
124
125             public bool IsValid
126             {
127                 get
128                 {
129                     return this.Item != null && this.Type != MyCommon.UploadFileType.Invalid;
130                 }
131             }
132
133             public string Path
134             {
135                 get
136                 {
137                     return this.Item?.Path ?? "";
138                 }
139             }
140
141             public string AltText => this.Item?.AltText ?? "";
142
143             public override string ToString()
144             {
145                 return this.Text;
146             }
147         }
148
149         private Dictionary<string, IMediaUploadService> pictureService;
150
151         private void CreateServices(Twitter tw, TwitterConfiguration twitterConfig)
152         {
153             this.pictureService?.Clear();
154             this.pictureService = null;
155
156             this.pictureService = new Dictionary<string, IMediaUploadService> {
157                 ["Twitter"] = new TwitterPhoto(tw, twitterConfig),
158                 ["img.ly"] = new imgly(tw, twitterConfig),
159                 ["yfrog"] = new yfrog(tw, twitterConfig),
160                 ["ついっぷるフォト"] = new TwipplePhoto(tw, twitterConfig),
161                 ["Imgur"] = new Imgur(tw, twitterConfig),
162                 ["Mobypicture"] = new Mobypicture(tw, twitterConfig),
163             };
164         }
165
166         public MediaSelector()
167         {
168             InitializeComponent();
169
170             this.ImageSelectedPicture.InitialImage = Properties.Resources.InitialImage;
171         }
172
173         /// <summary>
174         /// 投稿先サービスなどを初期化する。
175         /// </summary>
176         public void Initialize(Twitter tw, TwitterConfiguration twitterConfig, string svc, int? index = null)
177         {
178             CreateServices(tw, twitterConfig);
179
180             SetImageServiceCombo();
181             SetImagePageCombo();
182
183             SelectImageServiceComboItem(svc, index);
184         }
185
186         /// <summary>
187         /// 投稿先サービスを再作成する。
188         /// </summary>
189         public void Reset(Twitter tw, TwitterConfiguration twitterConfig)
190         {
191             CreateServices(tw, twitterConfig);
192
193             SetImageServiceCombo();
194         }
195
196         /// <summary>
197         /// 指定されたファイルの投稿に対応した投稿先があるかどうかを示す値を取得する。
198         /// </summary>
199         public bool HasUploadableService(string fileName, bool ignoreSize)
200         {
201             FileInfo fl = new FileInfo(fileName);
202             string ext = fl.Extension;
203             long? size = ignoreSize ? (long?)null : fl.Length;
204
205             if (IsUploadable(this.ServiceName, ext, size))
206                 return true;
207
208             foreach (string svc in ImageServiceCombo.Items)
209             {
210                 if (IsUploadable(svc, ext, size))
211                     return true;
212             }
213
214             return false;
215         }
216
217         /// <summary>
218         /// 指定された投稿先に投稿可能かどうかを示す値を取得する。
219         /// ファイルサイズの指定がなければ拡張子だけで判定する。
220         /// </summary>
221         private bool IsUploadable(string serviceName, string ext, long? size)
222         {
223             if (!string.IsNullOrEmpty(serviceName))
224             {
225                 var imageService = this.pictureService[serviceName];
226                 if (imageService.CheckFileExtension(ext))
227                 {
228                     if (!size.HasValue)
229                         return true;
230
231                     if (imageService.CheckFileSize(ext, size.Value))
232                         return true;
233                 }
234             }
235             return false;
236         }
237
238         /// <summary>
239         /// 投稿するファイルとその投稿先を選択するためのコントロールを表示する。
240         /// </summary>
241         private void BeginSelection(IMediaItem[] items)
242         {
243             if (items == null || items.Length == 0)
244             {
245                 BeginSelection();
246                 return;
247             }
248
249             var service = this.SelectedService;
250             if (service == null) return;
251
252             var count = Math.Min(items.Length, service.MaxMediaCount);
253             if (!this.Visible || count > 1)
254             {
255                 // 非表示時または複数のファイル指定は新規選択として扱う
256                 SetImagePageCombo();
257
258                 this.BeginSelecting?.Invoke(this, EventArgs.Empty);
259
260                 this.Visible = true;
261             }
262             this.Enabled = true;
263
264             if (count == 1)
265             {
266                 ImagefilePathText.Text = items[0].Path;
267                 AlternativeTextBox.Text = items[0].AltText;
268                 ImageFromSelectedFile(items[0], false);
269             }
270             else
271             {
272                 for (int i = 0; i < count; i++)
273                 {
274                     var index = ImagePageCombo.Items.Count - 1;
275                     if (index == 0)
276                     {
277                         ImagefilePathText.Text = items[i].Path;
278                         AlternativeTextBox.Text = items[i].AltText;
279                     }
280                     ImageFromSelectedFile(index, items[i], false);
281                 }
282             }
283         }
284
285         /// <summary>
286         /// 投稿するファイルとその投稿先を選択するためのコントロールを表示する(主にD&amp;D用)。
287         /// </summary>
288         public void BeginSelection(string[] fileNames)
289         {
290             if (fileNames == null || fileNames.Length == 0)
291             {
292                 BeginSelection();
293                 return;
294             }
295
296             var items = fileNames.Select(x => CreateFileMediaItem(x, false)).OfType<IMediaItem>().ToArray();
297             BeginSelection(items);
298         }
299
300         /// <summary>
301         /// 投稿するファイルとその投稿先を選択するためのコントロールを表示する。
302         /// </summary>
303         public void BeginSelection(Image image)
304         {
305             if (image == null)
306             {
307                 BeginSelection();
308                 return;
309             }
310
311             var items = new [] { CreateMemoryImageMediaItem(image, false) }.OfType<IMediaItem>().ToArray();
312             BeginSelection(items);
313         }
314
315         /// <summary>
316         /// 投稿するファイルとその投稿先を選択するためのコントロールを表示する。
317         /// </summary>
318         public void BeginSelection()
319         {
320             if (!this.Visible)
321             {
322                 this.BeginSelecting?.Invoke(this, EventArgs.Empty);
323
324                 this.Visible = true;
325                 this.Enabled = true;
326
327                 var media = (SelectedMedia)ImagePageCombo.SelectedItem;
328                 ImageFromSelectedFile(media.Item, true);
329                 ImagefilePathText.Focus();
330             }
331         }
332
333         /// <summary>
334         /// 選択処理を終了してコントロールを隠す。
335         /// </summary>
336         public void EndSelection()
337         {
338             if (this.Visible)
339             {
340                 ImagefilePathText.CausesValidation = false;
341
342                 this.EndSelecting?.Invoke(this, EventArgs.Empty);
343
344                 this.Visible = false;
345                 this.Enabled = false;
346                 ClearImageSelectedPicture();
347
348                 ImagefilePathText.CausesValidation = true;
349             }
350         }
351
352         /// <summary>
353         /// 選択された投稿先名と投稿する MediaItem を取得する。MediaItem は不要になったら呼び出し側にて破棄すること。
354         /// </summary>
355         public bool TryGetSelectedMedia(out string imageService, out IMediaItem[] mediaItems)
356         {
357             var validItems = ImagePageCombo.Items.Cast<SelectedMedia>()
358                              .Where(x => x.IsValid).Select(x => x.Item).OfType<IMediaItem>().ToArray();
359
360             if (validItems.Length > 0 &&
361                 ImageServiceCombo.SelectedIndex > -1)
362             {
363                 var serviceName = this.ServiceName;
364                 if (MessageBox.Show(string.Format(Properties.Resources.PostPictureConfirm1, serviceName, validItems.Length),
365                                    Properties.Resources.PostPictureConfirm2,
366                                    MessageBoxButtons.OKCancel,
367                                    MessageBoxIcon.Question,
368                                    MessageBoxDefaultButton.Button1)
369                                == DialogResult.OK)
370                 {
371                     //収集した MediaItem が破棄されないように、予め null を代入しておく
372                     foreach (SelectedMedia media in ImagePageCombo.Items)
373                     {
374                         if (media != null) media.Item = null;
375                     }
376
377                     imageService = serviceName;
378                     mediaItems = validItems;
379                     EndSelection();
380                     SetImagePageCombo();
381                     return true;
382                 }
383             }
384             else
385             {
386                 MessageBox.Show(Properties.Resources.PostPictureWarn1, Properties.Resources.PostPictureWarn2);
387             }
388
389             EndSelection();
390             imageService = null;
391             mediaItems = null;
392             return false;
393         }
394
395         private MemoryImageMediaItem CreateMemoryImageMediaItem(Image image, bool noMsgBox)
396         {
397             if (image == null) return null;
398
399             MemoryImage memoryImage = null;
400             try
401             {
402                 // image から png 形式の MemoryImage を生成
403                 memoryImage = MemoryImage.CopyFromImage(image);
404
405                 return new MemoryImageMediaItem(memoryImage);
406             }
407             catch
408             {
409                 memoryImage?.Dispose();
410
411                 if (!noMsgBox) MessageBox.Show("Unable to create MemoryImage.");
412                 return null;
413             }
414         }
415
416         private IMediaItem CreateFileMediaItem(string path, bool noMsgBox)
417         {
418             if (string.IsNullOrEmpty(path)) return null;
419
420             try
421             {
422                 return new FileMediaItem(path);
423             }
424             catch
425             {
426                 if (!noMsgBox) MessageBox.Show("Invalid file path: " + path);
427                 return null;
428             }
429         }
430
431         private void ValidateNewFileMediaItem(string path, bool noMsgBox)
432         {
433             var media = (SelectedMedia)ImagePageCombo.SelectedItem;
434             var item = media.Item;
435
436             if (path != media.Path)
437             {
438                 DisposeMediaItem(media.Item);
439                 media.Item = null;
440
441                 item = CreateFileMediaItem(path, noMsgBox);
442                 item.AltText = AlternativeTextBox.Text;
443             }
444
445             ImagefilePathText.Text = path;
446             ImageFromSelectedFile(item, noMsgBox);
447         }
448
449         private void DisposeMediaItem(IMediaItem item)
450         {
451             var disposableItem = item as IDisposable;
452             disposableItem?.Dispose();
453         }
454
455         private void FilePickButton_Click(object sender, EventArgs e)
456         {
457             var service = this.SelectedService;
458
459             if (FilePickDialog == null || service == null) return;
460             FilePickDialog.Filter = service.SupportedFormatsStrForDialog;
461             FilePickDialog.Title = Properties.Resources.PickPictureDialog1;
462             FilePickDialog.FileName = "";
463
464             this.FilePickDialogOpening?.Invoke(this, EventArgs.Empty);
465
466             try
467             {
468                 if (FilePickDialog.ShowDialog() == DialogResult.Cancel) return;
469             }
470             finally
471             {
472                 this.FilePickDialogClosed?.Invoke(this, EventArgs.Empty);
473             }
474
475             ValidateNewFileMediaItem(FilePickDialog.FileName, false);
476         }
477
478         private void ImagefilePathText_Validating(object sender, CancelEventArgs e)
479         {
480             if (ImageCancelButton.Focused)
481             {
482                 ImagefilePathText.CausesValidation = false;
483                 return;
484             }
485
486             ValidateNewFileMediaItem(ImagefilePathText.Text.Trim(), false);
487         }
488
489         private void ImageFromSelectedFile(IMediaItem item, bool noMsgBox)
490         {
491             ImageFromSelectedFile(-1, item, noMsgBox);
492         }
493
494         private void ImageFromSelectedFile(int index, IMediaItem item, bool noMsgBox)
495         {
496             var valid = false;
497
498             try
499             {
500                 var imageService = this.SelectedService;
501                 if (imageService == null) return;
502
503                 var selectedIndex = ImagePageCombo.SelectedIndex;
504                 if (index < 0) index = selectedIndex;
505
506                 if (index >= ImagePageCombo.Items.Count)
507                     throw new ArgumentOutOfRangeException(nameof(index));
508
509                 var isSelectedPage = (index == selectedIndex);
510
511                 if (isSelectedPage)
512                     this.ClearImageSelectedPicture();
513
514                 if (item == null || string.IsNullOrEmpty(item.Path)) return;
515
516                 try
517                 {
518                     var ext = item.Extension;
519                     var size = item.Size;
520
521                     if (!imageService.CheckFileExtension(ext))
522                     {
523                         //画像以外の形式
524                         if (!noMsgBox)
525                         {
526                             MessageBox.Show(
527                                 string.Format(Properties.Resources.PostPictureWarn3, this.ServiceName, MakeAvailableServiceText(ext, size), ext, item.Name),
528                                 Properties.Resources.PostPictureWarn4,
529                                 MessageBoxButtons.OK,
530                                 MessageBoxIcon.Warning);
531                         }
532                         return;
533                     }
534
535                     if (!imageService.CheckFileSize(ext, size))
536                     {
537                         // ファイルサイズが大きすぎる
538                         if (!noMsgBox)
539                         {
540                             MessageBox.Show(
541                                 string.Format(Properties.Resources.PostPictureWarn5, this.ServiceName, MakeAvailableServiceText(ext, size), item.Name),
542                                 Properties.Resources.PostPictureWarn4,
543                                 MessageBoxButtons.OK,
544                                 MessageBoxIcon.Warning);
545                         }
546                         return;
547                     }
548
549                     if (item.IsImage)
550                     {
551                         if (isSelectedPage)
552                             ImageSelectedPicture.Image = item.CreateImage();
553                         SetImagePage(index, item, MyCommon.UploadFileType.Picture);
554                     }
555                     else
556                     {
557                         SetImagePage(index, item, MyCommon.UploadFileType.MultiMedia);
558                     }
559
560                     valid = true;  //正常終了
561                 }
562                 catch (FileNotFoundException)
563                 {
564                     if (!noMsgBox) MessageBox.Show("File not found.");
565                 }
566                 catch (Exception)
567                 {
568                     if (!noMsgBox) MessageBox.Show("The type of this file is not image.");
569                 }
570             }
571             finally
572             {
573                 if (!valid)
574                 {
575                     ClearImagePage(index);
576                     DisposeMediaItem(item);
577                 }
578             }
579         }
580
581         private string MakeAvailableServiceText(string ext, long fileSize)
582         {
583             var text = string.Join(", ",
584                 ImageServiceCombo.Items.Cast<string>()
585                     .Where(serviceName =>
586                         !string.IsNullOrEmpty(serviceName) &&
587                         this.pictureService[serviceName].CheckFileExtension(ext) &&
588                         this.pictureService[serviceName].CheckFileSize(ext, fileSize)));
589
590             if (string.IsNullOrEmpty(text))
591                 return Properties.Resources.PostPictureWarn6;
592
593             return text;
594         }
595
596         private void ClearImageSelectedPicture()
597         {
598             var oldImage = this.ImageSelectedPicture.Image;
599             this.ImageSelectedPicture.Image = null;
600             oldImage?.Dispose();
601
602             this.ImageSelectedPicture.ShowInitialImage();
603         }
604
605         private void ImageCancelButton_Click(object sender, EventArgs e)
606         {
607             EndSelection();
608         }
609
610         private void ImageSelection_KeyDown(object sender, KeyEventArgs e)
611         {
612             if (e.KeyCode == Keys.Escape)
613             {
614                 EndSelection();
615             }
616         }
617
618         private void ImageSelection_KeyPress(object sender, KeyPressEventArgs e)
619         {
620             if (Convert.ToInt32(e.KeyChar) == 0x1B)
621             {
622                 ImagefilePathText.CausesValidation = false;
623                 e.Handled = true;
624             }
625         }
626
627         private void ImageSelection_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
628         {
629             if (e.KeyCode == Keys.Escape)
630             {
631                 ImagefilePathText.CausesValidation = false;
632             }
633         }
634
635         private void SetImageServiceCombo()
636         {
637             using (ControlTransaction.Update(ImageServiceCombo))
638             {
639                 string svc = "";
640                 if (ImageServiceCombo.SelectedIndex > -1) svc = ImageServiceCombo.Text;
641                 ImageServiceCombo.Items.Clear();
642
643                 // Add service names to combobox
644                 foreach (var key in pictureService.Keys)
645                 {
646                     ImageServiceCombo.Items.Add(key);
647                 }
648
649                 SelectImageServiceComboItem(svc);
650             }
651         }
652
653         private void SelectImageServiceComboItem(string svc, int? index = null)
654         {
655             int idx;
656             if (string.IsNullOrEmpty(svc))
657             {
658                 idx = index ?? 0;
659             }
660             else
661             {
662                 idx = ImageServiceCombo.Items.IndexOf(svc);
663                 if (idx == -1) idx = index ?? 0;
664             }
665
666             try
667             {
668                 ImageServiceCombo.SelectedIndex = idx;
669             }
670             catch (ArgumentOutOfRangeException)
671             {
672                 ImageServiceCombo.SelectedIndex = 0;
673             }
674
675             this.UpdateAltTextPanelVisible();
676         }
677
678         private void UpdateAltTextPanelVisible()
679             => this.AlternativeTextPanel.Visible = this.SelectedService.CanUseAltText;
680
681         private void ImageServiceCombo_SelectedIndexChanged(object sender, EventArgs e)
682         {
683             if (this.Visible)
684             {
685                 var imageService = this.SelectedService;
686                 if (imageService != null)
687                 {
688                     this.UpdateAltTextPanelVisible();
689
690                     if (ImagePageCombo.Items.Count > 0)
691                     {
692                         // 画像が選択された投稿先に対応しているかをチェックする
693                         // TODO: 複数の選択済み画像があるなら、できれば全てを再チェックしたほうがいい
694                         if (this.ServiceName == "Twitter")
695                         {
696                             ValidateSelectedImagePage();
697                         }
698                         else
699                         {
700                             if (ImagePageCombo.Items.Count > 1)
701                             {
702                                 // 複数の選択済み画像のうち、1枚目のみを残す
703                                 SetImagePageCombo((SelectedMedia)ImagePageCombo.Items[0]);
704                             }
705                             else
706                             {
707                                 ImagePageCombo.Enabled = false;
708                                 var valid = false;
709
710                                 try
711                                 {
712                                     var item = ((SelectedMedia)ImagePageCombo.Items[0]).Item;
713                                     if (item != null)
714                                     {
715                                         var ext = item.Extension;
716                                         if (imageService.CheckFileExtension(ext) &&
717                                             imageService.CheckFileSize(ext, item.Size))
718                                         {
719                                             valid = true;
720                                         }
721                                     }
722                                 }
723                                 catch
724                                 {
725                                 }
726                                 finally
727                                 {
728                                     if (!valid)
729                                     {
730                                         ClearImageSelectedPicture();
731                                         ClearSelectedImagePage();
732                                     }
733                                 }
734                             }
735                         }
736                     }
737                 }
738             }
739
740             this.SelectedServiceChanged?.Invoke(this, EventArgs.Empty);
741         }
742
743         private void SetImagePageCombo(SelectedMedia media = null)
744         {
745             using (ControlTransaction.Update(ImagePageCombo))
746             {
747                 ImagePageCombo.Enabled = false;
748
749                 foreach (SelectedMedia oldMedia in ImagePageCombo.Items)
750                 {
751                     if (oldMedia == null || oldMedia == media) continue;
752                     DisposeMediaItem(oldMedia.Item);
753                 }
754                 ImagePageCombo.Items.Clear();
755
756                 if (media == null)
757                     media = new SelectedMedia("1");
758
759                 ImagePageCombo.Items.Add(media);
760                 ImagefilePathText.Text = media.Path;
761                 AlternativeTextBox.Text = media.AltText;
762
763                 ImagePageCombo.SelectedIndex = 0;
764             }
765         }
766
767         private void AddNewImagePage(int selectedIndex)
768         {
769             var service = this.SelectedService;
770             if (service == null) return;
771
772             if (selectedIndex < service.MaxMediaCount - 1)
773             {
774                 // 投稿先の投稿可能枚数まで選択できるようにする
775                 var count = ImagePageCombo.Items.Count;
776                 if (selectedIndex == count - 1)
777                 {
778                     count++;
779                     ImagePageCombo.Items.Add(new SelectedMedia(count.ToString()));
780                     ImagePageCombo.Enabled = true;
781                 }
782             }
783         }
784
785         private void SetSelectedImagePage(IMediaItem item, MyCommon.UploadFileType type)
786         {
787             SetImagePage(-1, item, type);
788         }
789
790         private void SetImagePage(int index, IMediaItem item, MyCommon.UploadFileType type)
791         {
792             var selectedIndex = ImagePageCombo.SelectedIndex;
793             if (index < 0) index = selectedIndex;
794
795             var media = (SelectedMedia)ImagePageCombo.Items[index];
796             if (media.Item != item)
797             {
798                 DisposeMediaItem(media.Item);
799                 media.Item = item;
800             }
801             media.Type = type;
802
803             AddNewImagePage(index);
804         }
805
806         private void ClearSelectedImagePage()
807         {
808             ClearImagePage(-1);
809         }
810
811         private void ClearImagePage(int index)
812         {
813             var selectedIndex = ImagePageCombo.SelectedIndex;
814             if (index < 0) index = selectedIndex;
815
816             var media = (SelectedMedia)ImagePageCombo.Items[index];
817             DisposeMediaItem(media.Item);
818             media.Item = null;
819             media.Type = MyCommon.UploadFileType.Invalid;
820
821             if (index == selectedIndex)
822             {
823                 ImagefilePathText.Text = "";
824                 AlternativeTextBox.Text = "";
825             }
826         }
827
828         private void ValidateSelectedImagePage()
829         {
830             var idx = ImagePageCombo.SelectedIndex;
831             var media = (SelectedMedia)ImagePageCombo.Items[idx];
832             ImageServiceCombo.Enabled = (idx == 0);  // idx == 0 以外では投稿先サービスを選べないようにする
833             ImagefilePathText.Text = media.Path;
834             AlternativeTextBox.Text = media.AltText;
835             ImageFromSelectedFile(media.Item, true);
836         }
837
838         private void ImagePageCombo_SelectedIndexChanged(object sender, EventArgs e)
839         {
840             ValidateSelectedImagePage();
841         }
842
843         private void AlternativeTextBox_TextChanged(object sender, EventArgs e)
844         {
845             var selectedMedia = (SelectedMedia)this.ImagePageCombo.SelectedItem;
846             if (selectedMedia.Item == null)
847                 return;
848
849             selectedMedia.Item.AltText = this.AlternativeTextBox.Text;
850         }
851     }
852 }