OSDN Git Service

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