OSDN Git Service

IMediaItem.AltTextのセットをValidatingイベント内で行う
[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, string altText, 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             }
443
444             if (item != null)
445                 item.AltText = altText;
446
447             ImagefilePathText.Text = path;
448             AlternativeTextBox.Text = altText;
449             ImageFromSelectedFile(item, noMsgBox);
450         }
451
452         private void DisposeMediaItem(IMediaItem item)
453         {
454             var disposableItem = item as IDisposable;
455             disposableItem?.Dispose();
456         }
457
458         private void FilePickButton_Click(object sender, EventArgs e)
459         {
460             var service = this.SelectedService;
461
462             if (FilePickDialog == null || service == null) return;
463             FilePickDialog.Filter = service.SupportedFormatsStrForDialog;
464             FilePickDialog.Title = Properties.Resources.PickPictureDialog1;
465             FilePickDialog.FileName = "";
466
467             this.FilePickDialogOpening?.Invoke(this, EventArgs.Empty);
468
469             try
470             {
471                 if (FilePickDialog.ShowDialog() == DialogResult.Cancel) return;
472             }
473             finally
474             {
475                 this.FilePickDialogClosed?.Invoke(this, EventArgs.Empty);
476             }
477
478             ValidateNewFileMediaItem(FilePickDialog.FileName, AlternativeTextBox.Text.Trim(), false);
479         }
480
481         private void ImagefilePathText_Validating(object sender, CancelEventArgs e)
482         {
483             if (ImageCancelButton.Focused)
484             {
485                 ImagefilePathText.CausesValidation = false;
486                 return;
487             }
488
489             ValidateNewFileMediaItem(ImagefilePathText.Text.Trim(), AlternativeTextBox.Text.Trim(), false);
490         }
491
492         private void ImageFromSelectedFile(IMediaItem item, bool noMsgBox)
493         {
494             ImageFromSelectedFile(-1, item, noMsgBox);
495         }
496
497         private void ImageFromSelectedFile(int index, IMediaItem item, bool noMsgBox)
498         {
499             var valid = false;
500
501             try
502             {
503                 var imageService = this.SelectedService;
504                 if (imageService == null) return;
505
506                 var selectedIndex = ImagePageCombo.SelectedIndex;
507                 if (index < 0) index = selectedIndex;
508
509                 if (index >= ImagePageCombo.Items.Count)
510                     throw new ArgumentOutOfRangeException(nameof(index));
511
512                 var isSelectedPage = (index == selectedIndex);
513
514                 if (isSelectedPage)
515                     this.ClearImageSelectedPicture();
516
517                 if (item == null || string.IsNullOrEmpty(item.Path)) return;
518
519                 try
520                 {
521                     var ext = item.Extension;
522                     var size = item.Size;
523
524                     if (!imageService.CheckFileExtension(ext))
525                     {
526                         //画像以外の形式
527                         if (!noMsgBox)
528                         {
529                             MessageBox.Show(
530                                 string.Format(Properties.Resources.PostPictureWarn3, this.ServiceName, MakeAvailableServiceText(ext, size), ext, item.Name),
531                                 Properties.Resources.PostPictureWarn4,
532                                 MessageBoxButtons.OK,
533                                 MessageBoxIcon.Warning);
534                         }
535                         return;
536                     }
537
538                     if (!imageService.CheckFileSize(ext, size))
539                     {
540                         // ファイルサイズが大きすぎる
541                         if (!noMsgBox)
542                         {
543                             MessageBox.Show(
544                                 string.Format(Properties.Resources.PostPictureWarn5, this.ServiceName, MakeAvailableServiceText(ext, size), item.Name),
545                                 Properties.Resources.PostPictureWarn4,
546                                 MessageBoxButtons.OK,
547                                 MessageBoxIcon.Warning);
548                         }
549                         return;
550                     }
551
552                     if (item.IsImage)
553                     {
554                         if (isSelectedPage)
555                             ImageSelectedPicture.Image = item.CreateImage();
556                         SetImagePage(index, item, MyCommon.UploadFileType.Picture);
557                     }
558                     else
559                     {
560                         SetImagePage(index, item, MyCommon.UploadFileType.MultiMedia);
561                     }
562
563                     valid = true;  //正常終了
564                 }
565                 catch (FileNotFoundException)
566                 {
567                     if (!noMsgBox) MessageBox.Show("File not found.");
568                 }
569                 catch (Exception)
570                 {
571                     if (!noMsgBox) MessageBox.Show("The type of this file is not image.");
572                 }
573             }
574             finally
575             {
576                 if (!valid)
577                 {
578                     ClearImagePage(index);
579                     DisposeMediaItem(item);
580                 }
581             }
582         }
583
584         private string MakeAvailableServiceText(string ext, long fileSize)
585         {
586             var text = string.Join(", ",
587                 ImageServiceCombo.Items.Cast<string>()
588                     .Where(serviceName =>
589                         !string.IsNullOrEmpty(serviceName) &&
590                         this.pictureService[serviceName].CheckFileExtension(ext) &&
591                         this.pictureService[serviceName].CheckFileSize(ext, fileSize)));
592
593             if (string.IsNullOrEmpty(text))
594                 return Properties.Resources.PostPictureWarn6;
595
596             return text;
597         }
598
599         private void ClearImageSelectedPicture()
600         {
601             var oldImage = this.ImageSelectedPicture.Image;
602             this.ImageSelectedPicture.Image = null;
603             oldImage?.Dispose();
604
605             this.ImageSelectedPicture.ShowInitialImage();
606         }
607
608         private void ImageCancelButton_Click(object sender, EventArgs e)
609         {
610             EndSelection();
611         }
612
613         private void ImageSelection_KeyDown(object sender, KeyEventArgs e)
614         {
615             if (e.KeyCode == Keys.Escape)
616             {
617                 EndSelection();
618             }
619         }
620
621         private void ImageSelection_KeyPress(object sender, KeyPressEventArgs e)
622         {
623             if (Convert.ToInt32(e.KeyChar) == 0x1B)
624             {
625                 ImagefilePathText.CausesValidation = false;
626                 e.Handled = true;
627             }
628         }
629
630         private void ImageSelection_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
631         {
632             if (e.KeyCode == Keys.Escape)
633             {
634                 ImagefilePathText.CausesValidation = false;
635             }
636         }
637
638         private void SetImageServiceCombo()
639         {
640             using (ControlTransaction.Update(ImageServiceCombo))
641             {
642                 string svc = "";
643                 if (ImageServiceCombo.SelectedIndex > -1) svc = ImageServiceCombo.Text;
644                 ImageServiceCombo.Items.Clear();
645
646                 // Add service names to combobox
647                 foreach (var key in pictureService.Keys)
648                 {
649                     ImageServiceCombo.Items.Add(key);
650                 }
651
652                 SelectImageServiceComboItem(svc);
653             }
654         }
655
656         private void SelectImageServiceComboItem(string svc, int? index = null)
657         {
658             int idx;
659             if (string.IsNullOrEmpty(svc))
660             {
661                 idx = index ?? 0;
662             }
663             else
664             {
665                 idx = ImageServiceCombo.Items.IndexOf(svc);
666                 if (idx == -1) idx = index ?? 0;
667             }
668
669             try
670             {
671                 ImageServiceCombo.SelectedIndex = idx;
672             }
673             catch (ArgumentOutOfRangeException)
674             {
675                 ImageServiceCombo.SelectedIndex = 0;
676             }
677
678             this.UpdateAltTextPanelVisible();
679         }
680
681         private void UpdateAltTextPanelVisible()
682             => this.AlternativeTextPanel.Visible = this.SelectedService.CanUseAltText;
683
684         private void ImageServiceCombo_SelectedIndexChanged(object sender, EventArgs e)
685         {
686             if (this.Visible)
687             {
688                 var imageService = this.SelectedService;
689                 if (imageService != null)
690                 {
691                     this.UpdateAltTextPanelVisible();
692
693                     if (ImagePageCombo.Items.Count > 0)
694                     {
695                         // 画像が選択された投稿先に対応しているかをチェックする
696                         // TODO: 複数の選択済み画像があるなら、できれば全てを再チェックしたほうがいい
697                         if (this.ServiceName == "Twitter")
698                         {
699                             ValidateSelectedImagePage();
700                         }
701                         else
702                         {
703                             if (ImagePageCombo.Items.Count > 1)
704                             {
705                                 // 複数の選択済み画像のうち、1枚目のみを残す
706                                 SetImagePageCombo((SelectedMedia)ImagePageCombo.Items[0]);
707                             }
708                             else
709                             {
710                                 ImagePageCombo.Enabled = false;
711                                 var valid = false;
712
713                                 try
714                                 {
715                                     var item = ((SelectedMedia)ImagePageCombo.Items[0]).Item;
716                                     if (item != null)
717                                     {
718                                         var ext = item.Extension;
719                                         if (imageService.CheckFileExtension(ext) &&
720                                             imageService.CheckFileSize(ext, item.Size))
721                                         {
722                                             valid = true;
723                                         }
724                                     }
725                                 }
726                                 catch
727                                 {
728                                 }
729                                 finally
730                                 {
731                                     if (!valid)
732                                     {
733                                         ClearImageSelectedPicture();
734                                         ClearSelectedImagePage();
735                                     }
736                                 }
737                             }
738                         }
739                     }
740                 }
741             }
742
743             this.SelectedServiceChanged?.Invoke(this, EventArgs.Empty);
744         }
745
746         private void SetImagePageCombo(SelectedMedia media = null)
747         {
748             using (ControlTransaction.Update(ImagePageCombo))
749             {
750                 ImagePageCombo.Enabled = false;
751
752                 foreach (SelectedMedia oldMedia in ImagePageCombo.Items)
753                 {
754                     if (oldMedia == null || oldMedia == media) continue;
755                     DisposeMediaItem(oldMedia.Item);
756                 }
757                 ImagePageCombo.Items.Clear();
758
759                 if (media == null)
760                     media = new SelectedMedia("1");
761
762                 ImagePageCombo.Items.Add(media);
763                 ImagefilePathText.Text = media.Path;
764                 AlternativeTextBox.Text = media.AltText;
765
766                 ImagePageCombo.SelectedIndex = 0;
767             }
768         }
769
770         private void AddNewImagePage(int selectedIndex)
771         {
772             var service = this.SelectedService;
773             if (service == null) return;
774
775             if (selectedIndex < service.MaxMediaCount - 1)
776             {
777                 // 投稿先の投稿可能枚数まで選択できるようにする
778                 var count = ImagePageCombo.Items.Count;
779                 if (selectedIndex == count - 1)
780                 {
781                     count++;
782                     ImagePageCombo.Items.Add(new SelectedMedia(count.ToString()));
783                     ImagePageCombo.Enabled = true;
784                 }
785             }
786         }
787
788         private void SetSelectedImagePage(IMediaItem item, MyCommon.UploadFileType type)
789         {
790             SetImagePage(-1, item, type);
791         }
792
793         private void SetImagePage(int index, IMediaItem item, MyCommon.UploadFileType type)
794         {
795             var selectedIndex = ImagePageCombo.SelectedIndex;
796             if (index < 0) index = selectedIndex;
797
798             var media = (SelectedMedia)ImagePageCombo.Items[index];
799             if (media.Item != item)
800             {
801                 DisposeMediaItem(media.Item);
802                 media.Item = item;
803             }
804             media.Type = type;
805
806             AddNewImagePage(index);
807         }
808
809         private void ClearSelectedImagePage()
810         {
811             ClearImagePage(-1);
812         }
813
814         private void ClearImagePage(int index)
815         {
816             var selectedIndex = ImagePageCombo.SelectedIndex;
817             if (index < 0) index = selectedIndex;
818
819             var media = (SelectedMedia)ImagePageCombo.Items[index];
820             DisposeMediaItem(media.Item);
821             media.Item = null;
822             media.Type = MyCommon.UploadFileType.Invalid;
823
824             if (index == selectedIndex)
825             {
826                 ImagefilePathText.Text = "";
827                 AlternativeTextBox.Text = "";
828             }
829         }
830
831         private void ValidateSelectedImagePage()
832         {
833             var idx = ImagePageCombo.SelectedIndex;
834             var media = (SelectedMedia)ImagePageCombo.Items[idx];
835             ImageServiceCombo.Enabled = (idx == 0);  // idx == 0 以外では投稿先サービスを選べないようにする
836             ImagefilePathText.Text = media.Path;
837             AlternativeTextBox.Text = media.AltText;
838             ImageFromSelectedFile(media.Item, true);
839         }
840
841         private void ImagePageCombo_SelectedIndexChanged(object sender, EventArgs e)
842         {
843             ValidateSelectedImagePage();
844         }
845
846         private void AlternativeTextBox_Validating(object sender, CancelEventArgs e)
847         {
848             var imageFilePath = this.ImagefilePathText.Text.Trim();
849             var altText = this.AlternativeTextBox.Text.Trim();
850             this.ValidateNewFileMediaItem(imageFilePath, altText, noMsgBox: false);
851         }
852     }
853 }