OSDN Git Service

2fd268ae2f972e018aa050120341f6393cb500c9
[coroid/inqubus.git] / frontend / src / yukihane / inqubus / gui / MainFrame.java
1 /*
2  * MainFrame.java
3  *
4  * Created on 2011/05/28, 18:14:51
5  */
6 package yukihane.inqubus.gui;
7
8 import java.awt.Image;
9 import java.awt.ItemSelectable;
10 import java.awt.Toolkit;
11 import java.awt.datatransfer.DataFlavor;
12 import java.awt.datatransfer.Transferable;
13 import java.awt.event.ActionEvent;
14 import java.awt.event.ActionListener;
15 import java.awt.event.ItemEvent;
16 import java.awt.event.ItemListener;
17 import java.awt.event.KeyEvent;
18 import java.io.File;
19 import java.io.FilenameFilter;
20 import java.io.IOException;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 import javax.swing.BorderFactory;
30 import javax.swing.DropMode;
31 import javax.swing.GroupLayout;
32 import javax.swing.GroupLayout.Alignment;
33 import javax.swing.JButton;
34 import javax.swing.JCheckBox;
35 import javax.swing.JFrame;
36 import javax.swing.JLabel;
37 import javax.swing.JMenu;
38 import javax.swing.JMenuBar;
39 import javax.swing.JMenuItem;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42 import javax.swing.JTable;
43 import javax.swing.JTextField;
44 import javax.swing.KeyStroke;
45 import javax.swing.LayoutStyle.ComponentPlacement;
46 import javax.swing.TransferHandler;
47 import javax.swing.WindowConstants;
48 import org.apache.commons.lang.StringUtils;
49 import org.apache.commons.lang.builder.ToStringBuilder;
50 import saccubus.MainFrame_AboutBox;
51 import saccubus.util.WayBackTimeParser;
52 import saccubus.worker.convert.ConvertProgress;
53 import saccubus.worker.download.DownloadProgress;
54 import saccubus.worker.WorkerListener;
55 import saccubus.worker.convert.ConvertResult;
56 import saccubus.worker.download.DownloadResult;
57 import saccubus.worker.profile.CommentProfile;
58 import saccubus.worker.profile.ConvertProfile;
59 import saccubus.worker.profile.DownloadProfile;
60 import saccubus.worker.profile.FfmpegProfile;
61 import saccubus.worker.profile.GeneralProfile;
62 import saccubus.worker.profile.LoginProfile;
63 import saccubus.worker.profile.OutputProfile;
64 import saccubus.worker.profile.ProxyProfile;
65 import saccubus.worker.profile.VideoProfile;
66 import yukihane.Util;
67 import yukihane.inqubus.Config;
68 import yukihane.inqubus.manager.RequestProcess;
69 import yukihane.inqubus.manager.TaskManage;
70 import yukihane.inqubus.model.Target;
71 import yukihane.inqubus.model.TargetsTableModel;
72
73 /**
74  *
75  * @author yuki
76  */
77 public class MainFrame extends JFrame {
78
79     private static final long serialVersionUID = 1L;
80     private static final Logger logger = Logger.getLogger(MainFrame.class.getName());
81     private static final String ID_FIELD_TOOLTIP = "動画のIDまたはURLを入力します。";
82     private static final String FILE_LOCALBUTTON_TOOLTIP
83             = "ダウンロードする場合はチェックを外します。ローカルファイルを使用する場合はチェックを入れます。";
84     private static final String FILE_INPUTFIELD_TOOLTIP
85             = "ダウンロードする場合はファイル命名規則を入力します。"
86             + "ローカルファイルを使用する場合はパスを含むファイル名を入力します。";
87     private static final String FILE_OUTPUTFIELD_TOOLTIP
88             = "ファイル命名規則入力します。";
89     private final TargetsTableModel targetModel = new TargetsTableModel();
90     private final TaskManage taskManager;
91     private final DownloadProgressListener downloadProgressListener = new DownloadProgressListener();
92     private final ConvertProgressListener convertProgressListener = new ConvertProgressListener();
93
94     /** Creates new form MainFrame */
95     public MainFrame() {
96         final URL url = MainFrame_AboutBox.class.getResource("icon.png");
97         final Image icon1 = Toolkit.getDefaultToolkit().createImage(url);
98         final URL url32 = MainFrame_AboutBox.class.getResource("icon32.png");
99         final Image icon2 = Toolkit.getDefaultToolkit().createImage(url32);
100         final List<Image> images = new ArrayList<Image>(2);
101         images.add(icon1);
102         images.add(icon2);
103         setIconImages(images);
104
105         final JPanel pnlMain = new JPanel();
106         final JScrollPane scrDisplay = new JScrollPane();
107         tblDisplay = new JTable();
108         final JPanel pnlButton = new JPanel();
109         btnStart = new JButton();
110         btnStop = new JButton();
111         btnDeselect = new JButton();
112         final JPanel pnlInputMain = new JPanel();
113         final JLabel lblId = new JLabel();
114         fldId = new JTextField();
115         fldId.setToolTipText(ID_FIELD_TOOLTIP);
116         final JLabel lblVideo = new JLabel();
117         cbVideoLocal = new JCheckBox();
118         cbVideoLocal.setToolTipText(FILE_LOCALBUTTON_TOOLTIP);
119         fldVideo = new JTextField();
120         fldVideo.setToolTipText(FILE_INPUTFIELD_TOOLTIP);
121         btnVideo = new JButton();
122         final JLabel lblComment = new JLabel();
123         cbCommentLocal = new JCheckBox();
124         cbCommentLocal.setToolTipText(FILE_LOCALBUTTON_TOOLTIP);
125         cbCommentLocal.addItemListener(new ItemListener() {
126
127             @Override
128             public void itemStateChanged(ItemEvent e) {
129                 final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
130                 cbBackLogReduce.setEnabled(!selected);
131                 cbBackLog.setEnabled(!selected);
132                 if(selected) {
133                     cbBackLog.setSelected(false);
134                 }
135             }
136         });
137         fldComment = new JTextField();
138         fldComment.setToolTipText(FILE_INPUTFIELD_TOOLTIP);
139         btnComment = new JButton();
140         final JLabel lblOutput = new JLabel();
141         cbOutputEnable = new JCheckBox();
142         fldOutput = new JTextField();
143         fldOutput.setToolTipText(FILE_OUTPUTFIELD_TOOLTIP);
144
145         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
146
147         btnApply.addActionListener(new ApplyActionListener());
148
149         pnlMain.setBorder(BorderFactory.createEtchedBorder());
150
151         tblDisplay.setModel(targetModel);
152         tblDisplay.setDropMode(DropMode.INSERT_ROWS);
153         scrDisplay.setViewportView(tblDisplay);
154
155         pnlButton.setBorder(BorderFactory.createEtchedBorder());
156
157         btnStart.setText("開始");
158
159         btnStop.setText("停止");
160
161         btnDeselect.setText("選択解除");
162
163         GroupLayout gl_pnlButton = new GroupLayout(pnlButton);
164         pnlButton.setLayout(gl_pnlButton);
165         gl_pnlButton.setHorizontalGroup(
166             gl_pnlButton.createParallelGroup(Alignment.LEADING)
167             .addGroup(gl_pnlButton.createSequentialGroup()
168                 .addContainerGap()
169                 .addComponent(btnStart)
170                 .addPreferredGap(ComponentPlacement.RELATED)
171                 .addComponent(btnStop)
172                 .addPreferredGap(ComponentPlacement.RELATED, 250, Short.MAX_VALUE)
173                 .addComponent(btnDeselect)
174                 .addContainerGap())
175         );
176         gl_pnlButton.setVerticalGroup(
177             gl_pnlButton.createParallelGroup(Alignment.LEADING)
178             .addGroup(gl_pnlButton.createSequentialGroup()
179                 .addContainerGap()
180                 .addGroup(gl_pnlButton.createParallelGroup(Alignment.BASELINE)
181                     .addComponent(btnStart)
182                     .addComponent(btnStop)
183                     .addComponent(btnDeselect))
184                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
185         );
186
187         pnlInputMain.setBorder(BorderFactory.createEtchedBorder());
188
189         lblId.setText("ID");
190
191         fldId.addFocusListener(new java.awt.event.FocusAdapter() {
192
193             public void focusLost(java.awt.event.FocusEvent evt) {
194                 idFieldFocusLost(evt);
195             }
196         });
197
198         lblVideo.setText("動画");
199
200         cbVideoLocal.setText("local");
201         cbVideoLocal.addItemListener(new java.awt.event.ItemListener() {
202
203             public void itemStateChanged(java.awt.event.ItemEvent evt) {
204                 useMovieLocalCheckBoxItemStateChanged(evt);
205             }
206         });
207
208         btnVideo.setText("...");
209
210         lblComment.setText("コメント");
211
212         cbCommentLocal.setText("local");
213         cbCommentLocal.addItemListener(new java.awt.event.ItemListener() {
214
215             public void itemStateChanged(java.awt.event.ItemEvent evt) {
216                 useMovieLocalCheckBoxItemStateChanged(evt);
217             }
218         });
219
220         btnComment.setText("...");
221
222         lblOutput.setText("出力");
223
224         cbOutputEnable.setText("変換");
225         cbOutputEnable.addItemListener(new java.awt.event.ItemListener() {
226
227             public void itemStateChanged(java.awt.event.ItemEvent evt) {
228                 outputConvertCheckBoxItemStateChanged(evt);
229             }
230         });
231
232
233         GroupLayout gl_pnlInputMain = new GroupLayout(pnlInputMain);
234         pnlInputMain.setLayout(gl_pnlInputMain);
235         gl_pnlInputMain.setHorizontalGroup(
236             gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
237             .addGroup(gl_pnlInputMain.createSequentialGroup()
238                 .addContainerGap()
239                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
240                     .addGroup(gl_pnlInputMain.createSequentialGroup()
241                         .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
242                             .addComponent(lblComment)
243                             .addComponent(lblVideo)
244                             .addComponent(lblId)
245                             .addComponent(lblOutput))
246                         .addPreferredGap(ComponentPlacement.RELATED)
247                         .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
248                             .addGroup(gl_pnlInputMain.createSequentialGroup()
249                                 .addComponent(cbVideoLocal)
250                                 .addPreferredGap(ComponentPlacement.RELATED)
251                                 .addComponent(fldVideo, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE)
252                                 .addPreferredGap(ComponentPlacement.RELATED)
253                                 .addComponent(btnVideo))
254                             .addGroup(gl_pnlInputMain.createSequentialGroup()
255                                 .addComponent(fldId, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE)
256                                 .addPreferredGap(ComponentPlacement.UNRELATED)
257                                 .addComponent(cbBackLogReduce)
258                                 .addComponent(cbBackLog)
259                                 .addComponent(fldBackLog, GroupLayout.PREFERRED_SIZE, 150, GroupLayout.PREFERRED_SIZE)
260                             )
261                             .addGroup(Alignment.TRAILING, gl_pnlInputMain.createSequentialGroup()
262                                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.TRAILING)
263                                     .addGroup(Alignment.LEADING, gl_pnlInputMain.createSequentialGroup()
264                                         .addComponent(cbOutputEnable)
265                                         .addPreferredGap(ComponentPlacement.RELATED)
266                                         .addComponent(fldOutput, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
267                                     .addGroup(gl_pnlInputMain.createSequentialGroup()
268                                         .addComponent(cbCommentLocal)
269                                         .addPreferredGap(ComponentPlacement.RELATED)
270                                         .addComponent(fldComment, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE)))
271                                 .addPreferredGap(ComponentPlacement.RELATED)
272                                 .addComponent(btnComment))))
273                     .addComponent(btnApply, Alignment.TRAILING))
274                 .addContainerGap())
275         );
276         gl_pnlInputMain.setVerticalGroup(
277             gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
278             .addGroup(gl_pnlInputMain.createSequentialGroup()
279                 .addContainerGap()
280                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
281                     .addComponent(fldId, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
282                     .addComponent(lblId)
283                     .addComponent(cbBackLogReduce)
284                     .addComponent(cbBackLog)
285                     .addComponent(fldBackLog)
286                 )
287                 .addPreferredGap(ComponentPlacement.RELATED)
288                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
289                     .addComponent(lblVideo)
290                     .addComponent(fldVideo, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
291                     .addComponent(btnVideo)
292                     .addComponent(cbVideoLocal))
293                 .addPreferredGap(ComponentPlacement.RELATED)
294                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
295                     .addComponent(lblComment)
296                     .addComponent(fldComment, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
297                     .addComponent(btnComment)
298                     .addComponent(cbCommentLocal))
299                 .addPreferredGap(ComponentPlacement.RELATED)
300                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
301                     .addComponent(lblOutput)
302                     .addComponent(fldOutput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
303                     .addComponent(cbOutputEnable))
304                 .addPreferredGap(ComponentPlacement.UNRELATED)
305                 .addComponent(btnApply)
306                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
307         );
308
309         GroupLayout gl_pnlMain = new GroupLayout(pnlMain);
310         pnlMain.setLayout(gl_pnlMain);
311         gl_pnlMain.setHorizontalGroup(
312             gl_pnlMain.createParallelGroup(Alignment.LEADING)
313             .addGroup(Alignment.TRAILING, gl_pnlMain.createSequentialGroup()
314                 .addContainerGap()
315                 .addGroup(gl_pnlMain.createParallelGroup(Alignment.TRAILING)
316                     .addComponent(pnlInputMain, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
317                     .addComponent(scrDisplay, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 480, Short.MAX_VALUE)
318                     .addComponent(pnlButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
319                 .addContainerGap())
320         );
321         gl_pnlMain.setVerticalGroup(
322             gl_pnlMain.createParallelGroup(Alignment.LEADING)
323             .addGroup(Alignment.TRAILING, gl_pnlMain.createSequentialGroup()
324                 .addContainerGap()
325                 .addComponent(scrDisplay, GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
326                 .addPreferredGap(ComponentPlacement.RELATED)
327                 .addComponent(pnlButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
328                 .addPreferredGap(ComponentPlacement.RELATED)
329                 .addComponent(pnlInputMain, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
330                 .addGap(24, 24, 24))
331         );
332
333
334         JMenuBar menuBar = initMenuBar();
335         setJMenuBar(menuBar);
336
337         GroupLayout layout = new GroupLayout(getContentPane());
338         getContentPane().setLayout(layout);
339         layout.setHorizontalGroup(
340             layout.createParallelGroup(Alignment.LEADING)
341             .addComponent(pnlMain, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
342         );
343         layout.setVerticalGroup(
344             layout.createParallelGroup(Alignment.LEADING)
345             .addComponent(pnlMain, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
346         );
347
348         pack();
349         initInputPanel();
350         pnlMain.setTransferHandler(new DownloadListTransferHandler());
351         tblDisplay.setTransferHandler(new TableTransferHandler());
352
353         final Config p = Config.INSTANCE;
354         // TODO コンフィグからスレッド数
355         taskManager = new TaskManage(1, 1);
356
357     }
358
359     private class ApplyActionListener implements ActionListener {
360
361         @Override
362         public void actionPerformed(ActionEvent e) {
363             final DownloadProfile downProf = new InqubusDownloadProfile();
364             final String id = Util.getVideoId(fldId.getText());
365             final InqubusConvertProfile convProf = new InqubusConvertProfile();
366             logger.log(Level.INFO, downProf.toString());
367             logger.log(Level.INFO, convProf.toString());
368             taskManager.add(new RequestProcess(downProf, id, convProf));
369         }
370     }
371     /** This method is called from within the constructor to
372      * initialize the form.
373      * WARNING: Do NOT modify this code. The content of this method is
374      * always regenerated by the Form Editor.
375      */
376     @SuppressWarnings("unchecked")
377     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
378     private void initComponents() {
379     }// </editor-fold>//GEN-END:initComponents
380
381     private File searchFileMatchId(final File dir, final String id) {
382         // TODO 候補は複数返すようにして、その後の対処は呼び出しもとで行ってもらった方が良いかも
383         if (id.isEmpty()) {
384             return null;
385         }
386
387         final File[] lists = dir.listFiles(new FilenameFilter() {
388
389             final Pattern pattern = Pattern.compile(id + "\\D");
390
391             @Override
392             public boolean accept(File dir, String name) {
393                 return pattern.matcher(name).find();
394             }
395         });
396
397         if (lists.length == 1) {
398             return lists[0];
399         } else if (lists.length > 1) {
400             throw new UnsupportedOperationException();
401         } else {
402             return null;
403         }
404     }
405
406     private void useMovieLocalCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_useMovieLocalCheckBoxItemStateChanged
407         final Config p = Config.INSTANCE;
408
409         final ItemSelectable source = evt.getItemSelectable();
410
411         JButton btn;
412         JTextField field;
413         File dir;
414         if (source == cbVideoLocal) {
415             btn = btnVideo;
416             field = fldVideo;
417             dir = new File(p.getVideoDir());
418         } else {
419             btn = btnComment;
420             field = fldComment;
421             dir = new File(p.getCommentDir());
422         }
423
424         final boolean useLocal = (evt.getStateChange() == ItemEvent.SELECTED);
425         btn.setEnabled(useLocal);
426
427         String text;
428         if (useLocal) {
429             final File f = searchFileMatchId(dir, fldId.getText());
430             if (f != null) {
431                 text = f.getPath();
432             } else {
433                 text = "";
434             }
435         } else {
436             text = p.getVideoFileNamePattern();
437         }
438         field.setText(text);
439
440         fldId.setEnabled(!(cbVideoLocal.isSelected() && cbCommentLocal.isSelected()));
441
442     }//GEN-LAST:event_useMovieLocalCheckBoxItemStateChanged
443
444     private void outputConvertCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_outputConvertCheckBoxItemStateChanged
445         final boolean convert = (evt.getStateChange() == ItemEvent.SELECTED);
446         fldOutput.setEnabled(convert);
447     }//GEN-LAST:event_outputConvertCheckBoxItemStateChanged
448
449     private void idFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_idFieldFocusLost
450         final Config p = Config.INSTANCE;
451         final String id = fldId.getText();
452         if (id.isEmpty()) {
453             return;
454         }
455
456         if (cbVideoLocal.isSelected() && fldVideo.getText().isEmpty()) {
457             final File dir = new File(p.getVideoDir());
458             final File file = searchFileMatchId(dir, id);
459             if (file != null) {
460                 fldVideo.setText(file.getPath());
461             }
462         }
463
464         if (cbCommentLocal.isSelected() && fldComment.getText().isEmpty()) {
465             final File dir = new File(p.getCommentDir());
466             final File file = searchFileMatchId(dir, id);
467             if (file != null) {
468                 fldComment.setText(file.getPath());
469             }
470         }
471
472     }//GEN-LAST:event_idFieldFocusLost
473     // Variables declaration - do not modify//GEN-BEGIN:variables
474     private final JTable tblDisplay;
475     // ボタン領域
476     private final JButton btnStart;
477     private final JButton btnStop;
478     private final JButton btnDeselect;
479     //入力領域 - 標準
480     private final JTextField fldId;
481     private final JCheckBox cbBackLogReduce = new JCheckBox("コメ数減少");
482     private final JCheckBox cbBackLog = new JCheckBox("過去ログ");
483     private final JTextField fldBackLog = new JTextField();
484     private final JCheckBox cbVideoLocal;
485     private final JTextField fldVideo;
486     private final JButton btnVideo;
487     private final JCheckBox cbCommentLocal;
488     private final JTextField fldComment;
489     private final JButton btnComment;
490     private final JCheckBox cbOutputEnable;
491     private final JTextField fldOutput;
492     // 適用
493     private final JButton btnApply = new JButton("適用");
494     // End of variables declaration//GEN-END:variables
495
496     private void initInputPanel() {
497         final Config p = Config.INSTANCE;
498
499         fldId.setText("");
500         fldBackLog.setEnabled(false);
501         fldBackLog.setToolTipText("YYYY/MM/DD hh:mm:ss形式、あるいは1970/01/01からの経過秒を入力します。");
502         cbBackLog.setEnabled(true);
503         cbBackLog.addItemListener(new ItemListener() {
504
505             @Override
506             public void itemStateChanged(ItemEvent e) {
507                 final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
508                 fldBackLog.setEnabled(selected);
509             }
510         });
511         cbBackLogReduce.setToolTipText("「コメントの量を減らす」場合はチェックを付けます。");
512
513         final boolean movieLocal = p.getVideoUseLocal();
514         cbVideoLocal.setSelected(movieLocal);
515         btnVideo.setEnabled(movieLocal);
516         if (!movieLocal) {
517             fldVideo.setText(p.getVideoFileNamePattern());
518         }
519
520         final boolean commentLocal = p.getCommentUseLocal();
521         cbCommentLocal.setSelected(commentLocal);
522         btnComment.setEnabled(commentLocal);
523         if (!commentLocal) {
524             fldComment.setText(p.getCommentFileNamePattern());
525         }
526
527         final boolean convert = p.getOutputEnable();
528         cbOutputEnable.setSelected(convert);
529         fldOutput.setEnabled(convert);
530         fldOutput.setText(p.getOutputFileNamePattern());
531
532     }
533
534     private JMenuBar initMenuBar() {
535         final JMenuBar menuBar = new JMenuBar();
536
537         final JMenu mnFile = new JMenu("ファイル(F)");
538         menuBar.add(mnFile);
539
540         final JMenuItem itExit = new JMenuItem("終了(X)", KeyEvent.VK_X);
541         itExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
542         itExit.addActionListener(new ActionListener() {
543
544             @Override
545             public void actionPerformed(ActionEvent e) {
546                 throw new UnsupportedOperationException("Not supported yet.");
547             }
548         });
549         mnFile.add(itExit);
550
551         final JMenu mnTool = new JMenu("ツール(T)");
552         menuBar.add(mnTool);
553
554         final JMenuItem itOption = new JMenuItem("オプション(O)...", KeyEvent.VK_O);
555         // TODO ショートカットキー
556         itOption.addActionListener(new ActionListener() {
557
558             @Override
559             public void actionPerformed(ActionEvent e) {
560                 final yukihane.inqubus.gui.ConfigDialog dlg = new yukihane.inqubus.gui.ConfigDialog(MainFrame.this);
561                 dlg.setLocationRelativeTo(MainFrame.this);
562                 dlg.setModal(true);
563                 dlg.setVisible(true);
564             }
565         });
566         mnTool.add(itOption);
567
568         final JMenu mnHelp = new JMenu("ヘルプ(H)");
569         menuBar.add(mnHelp);
570
571         final JMenuItem itAbout = new JMenuItem("このソフトウェアについて(A)...", KeyEvent.VK_A);
572         itAbout.addActionListener(new ActionListener() {
573
574             @Override
575             public void actionPerformed(ActionEvent e) {
576                 MainFrame_AboutBox dlg = new MainFrame_AboutBox(MainFrame.this);
577                 dlg.pack();
578                 dlg.setLocationRelativeTo(MainFrame.this);
579                 dlg.setModal(true);
580                 dlg.setVisible(true);
581             }
582         });
583         mnHelp.add(itAbout);
584
585         return menuBar;
586     }
587
588     private class DownloadProgressListener implements WorkerListener<DownloadResult, DownloadProgress> {
589
590         @Override
591         public void process(DownloadProgress progress) {
592             throw new UnsupportedOperationException("Not supported yet.");
593         }
594
595         @Override
596         public void cancelled() {
597             throw new UnsupportedOperationException("Not supported yet.");
598         }
599
600         @Override
601         public void done(DownloadResult result) {
602             throw new UnsupportedOperationException("Not supported yet.");
603         }
604
605         @Override
606         public void error(Throwable th) {
607             throw new UnsupportedOperationException("Not supported yet.");
608         }
609     }
610
611     private class ConvertProgressListener implements WorkerListener<ConvertResult, ConvertProgress> {
612
613         @Override
614         public void process(ConvertProgress progress) {
615             throw new UnsupportedOperationException("Not supported yet.");
616         }
617
618         @Override
619         public void cancelled() {
620             throw new UnsupportedOperationException("Not supported yet.");
621         }
622
623         @Override
624         public void done(ConvertResult result) {
625             throw new UnsupportedOperationException("Not supported yet.");
626         }
627
628         @Override
629         public void error(Throwable th) {
630             throw new UnsupportedOperationException("Not supported yet.");
631         }
632     }
633
634
635
636     private class DownloadListTransferHandler extends TransferHandler {
637
638         private static final long serialVersionUID = 1L;
639         private final Pattern movieIdPattern = Pattern.compile("(\\w\\w\\d+)");
640
641         @Override
642         public boolean canImport(TransferHandler.TransferSupport support) {
643             Transferable transferable = support.getTransferable();
644             if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
645                     || transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
646                 return true;
647             }
648             return false;
649         }
650
651         @Override
652         public boolean importData(TransferHandler.TransferSupport support) {
653             try {
654                 Transferable transferable = support.getTransferable();
655                 if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
656                     @SuppressWarnings("unchecked")
657                     final List<File> data = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
658                     Collection<Target> targets = Target.from(data);
659                     targetModel.addTarget(targets);
660                 } else if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
661                     String data = (String) transferable.getTransferData(DataFlavor.stringFlavor);
662                     Matcher matcher = movieIdPattern.matcher(data);
663                     if (matcher.find()) {
664                         String movieId = matcher.group(1);
665                         Target target = Target.fromId(movieId);
666                         targetModel.addTarget(target);
667                     } else {
668                         return false;
669                     }
670
671                 }
672                 return false;
673             } catch (Exception e) {
674                 logger.log(Level.SEVERE, null, e);
675                 return false;
676             }
677         }
678     }
679
680     private class TableTransferHandler extends DownloadListTransferHandler {
681
682         private static final long serialVersionUID = 1L;
683
684         @Override
685         public boolean canImport(TransferHandler.TransferSupport support) {
686             return super.canImport(support);
687         }
688
689         @Override
690         public boolean importData(TransferHandler.TransferSupport support) {
691             return super.importData(support);
692         }
693     }
694
695     /*
696      * ここからDownloadProfile作成用クラスの定義
697      */
698
699     private class InqubusDownloadProfile implements DownloadProfile {
700
701         private final LoginProfile loginProfile;
702         private final ProxyProfile proxyProfile;
703         private final VideoProfile videoProfile;
704         private final CommentProfile commentProfile;
705         private final GeneralProfile generalProfile;
706
707         private InqubusDownloadProfile() {
708             this.loginProfile = new InqubusLoginProfile();
709             this.proxyProfile = new InqubusProxyProfile();
710             this.videoProfile = new InqubusVideoProfile();
711             this.commentProfile = new InqubusCommentProfile();
712             this.generalProfile = new InqubusGeneralProfile();
713         }
714
715         @Override
716         public LoginProfile getLoginInfo() {
717             return this.loginProfile;
718         }
719
720         @Override
721         public ProxyProfile getProxyProfile() {
722             return this.proxyProfile;
723         }
724
725         @Override
726         public VideoProfile getVideoProfile() {
727             return this.videoProfile;
728         }
729
730         @Override
731         public CommentProfile getCommentProfile() {
732             return this.commentProfile;
733         }
734
735         @Override
736         public GeneralProfile getGeneralProfile() {
737             return this.generalProfile;
738         }
739
740         @Override
741         public String toString(){
742             return ToStringBuilder.reflectionToString(this);
743         }
744     }
745
746     private class InqubusLoginProfile implements LoginProfile {
747         private final String mail;
748         private final String password;
749
750         private InqubusLoginProfile(){
751             final Config p = Config.INSTANCE;
752             this.mail = p.getId();
753             this.password = p.getPassword();
754         }
755
756         @Override
757         public String getMail() {
758             return this.mail;
759         }
760
761         @Override
762         public String getPassword() {
763             return this.password;
764         }
765
766         @Override
767         public String toString(){
768             return ToStringBuilder.reflectionToString(this);
769         }
770     }
771
772     private class InqubusProxyProfile implements ProxyProfile {
773         private final boolean use;
774         private final String host;
775         private final int port;
776
777         private InqubusProxyProfile(){
778             final Config p = Config.INSTANCE;
779             this.use = p.getProxyUse();
780             this.host = p.getProxyHost();
781             final String pp = p.getProxyPort();
782             this.port = StringUtils.isBlank(pp) ? -1 : Integer.parseInt(pp);
783         }
784
785         @Override
786         public boolean use() {
787             return this.use;
788         }
789
790         @Override
791         public String getHost() {
792             return this.host;
793         }
794
795         @Override
796         public int getPort() {
797             return this.port;
798         }
799
800         @Override
801         public String toString(){
802             return ToStringBuilder.reflectionToString(this);
803         }
804     }
805
806     private class InqubusVideoProfile implements VideoProfile {
807         private final boolean download;
808         private final File dir;
809         private final String fileName;
810         private final File localFile;
811
812         private InqubusVideoProfile(){
813             final Config p = Config.INSTANCE;
814             this.download = !cbVideoLocal.isSelected();
815             if (this.download) {
816                 this.dir = new File(p.getVideoDir());
817                 this.fileName = fldVideo.getText();
818                 this.localFile = null;
819             } else {
820                 this.dir = null;
821                 this.fileName = null;
822                 this.localFile = new File(fldVideo.getText());
823             }
824         }
825
826         @Override
827         public boolean isDownload() {
828             return this.download;
829         }
830
831         @Override
832         public File getDir() {
833             return this.dir;
834         }
835
836         @Override
837         public String getFileName() {
838             return this.fileName;
839         }
840
841         @Override
842         public File getLocalFile() {
843             return this.localFile;
844         }
845
846         @Override
847         public String toString(){
848             return ToStringBuilder.reflectionToString(this);
849         }
850     }
851
852     private class InqubusCommentProfile implements CommentProfile {
853         private final boolean download;
854         private final File dir;
855         private final String fileName;
856         private final File localFile;
857         private final int lengthRelatedCommentSize;
858         private final boolean disablePerMinComment;
859         private final int perMinCommentSize;
860         private final long backLogPoint;
861
862         private InqubusCommentProfile() {
863             final Config p = Config.INSTANCE;
864             this.download = !cbVideoLocal.isSelected();
865             if (this.download) {
866                 this.dir = new File(p.getVideoDir());
867                 this.fileName = fldVideo.getText();
868                 this.localFile = null;
869             } else {
870                 this.dir = null;
871                 this.fileName = null;
872                 this.localFile = new File(fldVideo.getText());
873             }
874
875             if(cbBackLog.isSelected()) {
876                 try {
877                     this.backLogPoint = WayBackTimeParser.parse(fldBackLog.getText());
878                 } catch (IOException ex) {
879                     throw new IllegalArgumentException("過去ログ時刻指定が誤っています。", ex);
880                 }
881             } else {
882                 this.backLogPoint = -1L;
883             }
884
885             this.disablePerMinComment = cbBackLogReduce.isSelected();
886             this.lengthRelatedCommentSize
887                     = (p.getCommentSizeAutosize()) ? -1 : Integer.parseInt(p.getCommentSizeManual());
888             this.perMinCommentSize
889                     = (p.getCommentMinSizeAutosize()) ? -1 : Integer.parseInt(p.getCommentMinSizeManual());
890         }
891
892         @Override
893         public boolean isDownload() {
894             return this.download;
895         }
896
897         @Override
898         public File getDir() {
899             return this.dir;
900         }
901
902         @Override
903         public String getFileName() {
904             return this.fileName;
905         }
906
907         @Override
908         public File getLocalFile() {
909             return this.localFile;
910         }
911
912         @Override
913         public int getLengthRelatedCommentSize() {
914             return this.lengthRelatedCommentSize;
915         }
916
917         @Override
918         public boolean isDisablePerMinComment() {
919             return this.disablePerMinComment;
920         }
921
922         @Override
923         public int getPerMinCommentSize() {
924             return this.perMinCommentSize;
925         }
926
927         @Override
928         public long getBackLogPoint() {
929             return this.backLogPoint;
930         }
931
932         @Override
933         public String toString(){
934             return ToStringBuilder.reflectionToString(this);
935         }
936     }
937
938     private class InqubusGeneralProfile implements GeneralProfile {
939         private final String replaceFrom;
940         private final String replaceTo;
941         private InqubusGeneralProfile() {
942             final Config p = Config.INSTANCE;
943             this.replaceFrom = p.getReplaceFrom();
944             this.replaceTo = p.getReplaceTo();
945         }
946
947         @Override
948         public String getReplaceFrom() {
949             return this.replaceFrom;
950         }
951
952         @Override
953         public String getReplaceTo() {
954             return this.replaceTo;
955         }
956
957         @Override
958         public String toString(){
959             return ToStringBuilder.reflectionToString(this);
960         }
961     }
962
963     /*
964      * ここからConvertProfile作成用クラスの定義
965      */
966     private class InqubusConvertProfile implements ConvertProfile {
967         private final OutputProfile outputProfile;
968         private final GeneralProfile generalProfile;
969         private final FfmpegProfile ffmpegProfile;
970         private final boolean convert;
971         private final File ffmpeg;
972         private final boolean vhookDisabled;
973         private final boolean commentOverlay;
974         private final File vhook;
975         private final File tmpDir;
976         private final File font;
977         private final int fontIndex;
978         private final boolean commentOpaque;
979         private final boolean disableFontSizeArrange;
980         private final int shadowIndex;
981         private final boolean showConvrting;
982         private final int maxNumOfComment;
983         private final HideCondition ngSetting;
984
985         private InqubusConvertProfile() {
986             final Config p = Config.INSTANCE;
987             this.outputProfile = new InqubusOutputProfile();
988             this.generalProfile = new InqubusGeneralProfile();
989             this.ffmpegProfile = new InqubusFfmpegProfile();
990             this.convert = cbOutputEnable.isSelected();
991             this.ffmpeg = new File(p.getFfmpegPath());
992             // TODO コンフィグに設定なし
993             this.vhookDisabled = false;
994             this.commentOverlay = p.getOutputCommentOverlay();
995             this.vhook = new File(p.getFfmpegDllPath());
996             // TODO コンフィグに設定なし
997             this.tmpDir = new File(".");
998             this.font = new File(p.getFontPath());
999             this.fontIndex = Integer.parseInt(p.getFontIndex());
1000             this.commentOpaque = p.getCommentOpaque();
1001             this.disableFontSizeArrange = p.getFontSizeArrangeDisable();
1002             this.shadowIndex = p.getFontShadow();
1003             // TODO コンフィグに設定なし
1004             this.showConvrting = true;
1005             this.maxNumOfComment = (p.getCommentDisplaySizeDefault()) ? -1 : Integer.parseInt(p.
1006                     getCommentDisplaySizeManual());
1007             this.ngSetting = new InqubusHideCondition();
1008         }
1009
1010         @Override
1011         public OutputProfile getOutputProfile() {
1012             return this.outputProfile;
1013         }
1014
1015         @Override
1016         public GeneralProfile getGeneralProfile() {
1017             return this.generalProfile;
1018         }
1019
1020         @Override
1021         public FfmpegProfile getFfmpegOption() {
1022             return this.ffmpegProfile;
1023         }
1024
1025         @Override
1026         public boolean isConvert() {
1027             return this.convert;
1028         }
1029
1030         @Override
1031         public File getFfmpeg() {
1032             return this.ffmpeg;
1033         }
1034
1035         @Override
1036         public boolean isVhookDisabled() {
1037             return this.vhookDisabled;
1038         }
1039
1040         @Override
1041         public boolean isCommentOverlay() {
1042             return this.commentOverlay;
1043         }
1044
1045         @Override
1046         public File getVhook() {
1047             return this.vhook;
1048         }
1049
1050         @Override
1051         public File getTempDir() {
1052             return this.tmpDir;
1053         }
1054
1055         @Override
1056         public File getFont() {
1057             return this.font;
1058         }
1059
1060         @Override
1061         public int getFontIndex() {
1062             return this.fontIndex;
1063         }
1064
1065         @Override
1066         public boolean isCommentOpaque() {
1067             return this.commentOpaque;
1068         }
1069
1070         @Override
1071         public boolean isDisableFontSizeArrange() {
1072             return this.disableFontSizeArrange;
1073         }
1074
1075         @Override
1076         public int getShadowIndex() {
1077             return this.shadowIndex;
1078         }
1079
1080         @Override
1081         public boolean isShowConverting() {
1082             return this.showConvrting;
1083         }
1084
1085         @Override
1086         public int getMaxNumOfComment() {
1087             return this.maxNumOfComment;
1088         }
1089
1090         @Override
1091         public HideCondition getNgSetting() {
1092             return this.ngSetting;
1093         }
1094
1095         @Override
1096         public String toString(){
1097             return ToStringBuilder.reflectionToString(this);
1098         }
1099     }
1100
1101     private class InqubusOutputProfile implements OutputProfile {
1102         private final File dir;
1103         private final String fileName;
1104         private final String videoId;
1105         private final String title;
1106
1107
1108         private InqubusOutputProfile(){
1109             final Config p = Config.INSTANCE;
1110             this.dir = new File(p.getOutputDir());
1111             this.fileName = fldOutput.getText();
1112             // TODO この時点でのID/Titleはどうするか…
1113             this.videoId = "";
1114             this.title = "";
1115         }
1116
1117         @Override
1118         public File getDir() {
1119             return this.dir;
1120         }
1121
1122         @Override
1123         public String getFileName() {
1124             return this.fileName;
1125         }
1126
1127         @Override
1128         public String getVideoId() {
1129             return this.videoId;
1130         }
1131
1132         @Override
1133         public String getTitile() {
1134             return this.title;
1135         }
1136
1137         @Override
1138         public String toString(){
1139             return ToStringBuilder.reflectionToString(this);
1140         }
1141     }
1142
1143     private class InqubusFfmpegProfile implements FfmpegProfile {
1144         private final String extOption;
1145         private final String inOption;
1146         private final String mainOption;
1147         private final String outOption;
1148         private final String avOption;
1149         private final boolean resize;
1150         private final int resizeWidth;
1151         private final int resizeHeight;
1152         private final boolean adjustRatio;
1153
1154         private InqubusFfmpegProfile() {
1155             // TODO FFMPEGオプションは、後でメイン画面でも設定できるようにするかも
1156             final Config p = Config.INSTANCE;
1157             this.extOption = p.getFfmpegExtension();
1158             this.inOption = p.getFfmpegInOption();
1159             this.mainOption = p.getFfmpegMainOption();
1160             this.outOption = p.getFfmpegOutOption();
1161             this.avOption = p.getFfmpegAvOption();
1162             this.resize = p.getFfmpegResizeEnable();
1163             this.resizeWidth = Integer.parseInt(p.getFfmpegResizeWidth());
1164             this.resizeHeight = Integer.parseInt(p.getFfmpegResizeHeight());
1165             this.adjustRatio = p.getFfmpegKeepAspect();
1166         }
1167
1168         @Override
1169         public String getExtOption() {
1170             return this.extOption;
1171         }
1172
1173         @Override
1174         public String getInOption() {
1175             return this.inOption;
1176         }
1177
1178         @Override
1179         public String getMainOption() {
1180             return this.mainOption;
1181         }
1182
1183         @Override
1184         public String getOutOption() {
1185             return this.outOption;
1186         }
1187
1188         @Override
1189         public String getAvfilterOption() {
1190             return this.avOption;
1191         }
1192
1193         @Override
1194         public boolean isResize() {
1195             return this.resize;
1196         }
1197
1198         @Override
1199         public int getResizeWidth() {
1200             return this.resizeWidth;
1201         }
1202
1203         @Override
1204         public int getResizeHeight() {
1205             return this.resizeHeight;
1206         }
1207
1208         @Override
1209         public boolean isAdjustRatio() {
1210             return this.adjustRatio;
1211         }
1212
1213         @Override
1214         public String toString(){
1215             return ToStringBuilder.reflectionToString(this);
1216         }
1217     }
1218
1219     private class InqubusHideCondition implements ConvertProfile.HideCondition{
1220
1221         @Override
1222         public String getWord() {
1223             // TODO
1224             return "";
1225         }
1226
1227         @Override
1228         public String getId() {
1229             // TODO
1230             return "";
1231         }
1232
1233         @Override
1234         public String toString(){
1235             return ToStringBuilder.reflectionToString(this);
1236         }
1237     }
1238 }