OSDN Git Service

206f827905775ab3ea238cc4ddb10573d3ce670c
[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.Download;
53 import saccubus.worker.profile.CommentProfile;
54 import saccubus.worker.profile.DownloadProfile;
55 import saccubus.worker.profile.GeneralProfile;
56 import saccubus.worker.profile.LoginProfile;
57 import saccubus.worker.profile.ProxyProfile;
58 import saccubus.worker.profile.VideoProfile;
59 import yukihane.Util;
60 import yukihane.inqubus.Config;
61 import yukihane.inqubus.model.Target;
62 import yukihane.inqubus.model.TargetsTableModel;
63
64 /**
65  *
66  * @author yuki
67  */
68 public class MainFrame extends JFrame {
69
70     private static final long serialVersionUID = 1L;
71     private static final Logger logger = Logger.getLogger(MainFrame.class.getName());
72     private static final String ID_FIELD_TOOLTIP = "動画のIDまたはURLを入力します。";
73     private static final String FILE_LOCALBUTTON_TOOLTIP
74             = "ダウンロードする場合はチェックを外します。ローカルファイルを使用する場合はチェックを入れます。";
75     private static final String FILE_INPUTFIELD_TOOLTIP
76             = "ダウンロードする場合はファイル命名規則を入力します。"
77             + "ローカルファイルを使用する場合はパスを含むファイル名を入力します。";
78     private static final String FILE_OUTPUTFIELD_TOOLTIP
79             = "ファイル命名規則入力します。";
80     private final TargetsTableModel targetModel = new TargetsTableModel();
81
82     /** Creates new form MainFrame */
83     public MainFrame() {
84         final URL url = MainFrame_AboutBox.class.getResource("icon.png");
85         final Image icon1 = Toolkit.getDefaultToolkit().createImage(url);
86         final URL url32 = MainFrame_AboutBox.class.getResource("icon32.png");
87         final Image icon2 = Toolkit.getDefaultToolkit().createImage(url32);
88         final List<Image> images = new ArrayList<Image>(2);
89         images.add(icon1);
90         images.add(icon2);
91         setIconImages(images);
92
93         final JPanel pnlMain = new JPanel();
94         final JScrollPane scrDisplay = new JScrollPane();
95         tblDisplay = new JTable();
96         final JPanel pnlButton = new JPanel();
97         btnStart = new JButton();
98         btnStop = new JButton();
99         btnDeselect = new JButton();
100         final JPanel pnlInputMain = new JPanel();
101         final JLabel lblId = new JLabel();
102         fldId = new JTextField();
103         fldId.setToolTipText(ID_FIELD_TOOLTIP);
104         final JLabel lblVideo = new JLabel();
105         cbVideoLocal = new JCheckBox();
106         cbVideoLocal.setToolTipText(FILE_LOCALBUTTON_TOOLTIP);
107         fldVideo = new JTextField();
108         fldVideo.setToolTipText(FILE_INPUTFIELD_TOOLTIP);
109         btnVideo = new JButton();
110         final JLabel lblComment = new JLabel();
111         cbCommentLocal = new JCheckBox();
112         cbCommentLocal.setToolTipText(FILE_LOCALBUTTON_TOOLTIP);
113         cbCommentLocal.addItemListener(new ItemListener() {
114
115             @Override
116             public void itemStateChanged(ItemEvent e) {
117                 final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
118                 cbBackLogReduce.setEnabled(!selected);
119                 cbBackLog.setEnabled(!selected);
120                 if(selected) {
121                     cbBackLog.setSelected(false);
122                 }
123             }
124         });
125         fldComment = new JTextField();
126         fldComment.setToolTipText(FILE_INPUTFIELD_TOOLTIP);
127         btnComment = new JButton();
128         final JLabel lblOutput = new JLabel();
129         cbOutputEnable = new JCheckBox();
130         fldOutput = new JTextField();
131         fldOutput.setToolTipText(FILE_OUTPUTFIELD_TOOLTIP);
132
133         setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
134
135         btnApply.addActionListener(new ApplyActionListener());
136
137         pnlMain.setBorder(BorderFactory.createEtchedBorder());
138
139         tblDisplay.setModel(targetModel);
140         tblDisplay.setDropMode(DropMode.INSERT_ROWS);
141         scrDisplay.setViewportView(tblDisplay);
142
143         pnlButton.setBorder(BorderFactory.createEtchedBorder());
144
145         btnStart.setText("開始");
146
147         btnStop.setText("停止");
148
149         btnDeselect.setText("選択解除");
150
151         GroupLayout gl_pnlButton = new GroupLayout(pnlButton);
152         pnlButton.setLayout(gl_pnlButton);
153         gl_pnlButton.setHorizontalGroup(
154             gl_pnlButton.createParallelGroup(Alignment.LEADING)
155             .addGroup(gl_pnlButton.createSequentialGroup()
156                 .addContainerGap()
157                 .addComponent(btnStart)
158                 .addPreferredGap(ComponentPlacement.RELATED)
159                 .addComponent(btnStop)
160                 .addPreferredGap(ComponentPlacement.RELATED, 250, Short.MAX_VALUE)
161                 .addComponent(btnDeselect)
162                 .addContainerGap())
163         );
164         gl_pnlButton.setVerticalGroup(
165             gl_pnlButton.createParallelGroup(Alignment.LEADING)
166             .addGroup(gl_pnlButton.createSequentialGroup()
167                 .addContainerGap()
168                 .addGroup(gl_pnlButton.createParallelGroup(Alignment.BASELINE)
169                     .addComponent(btnStart)
170                     .addComponent(btnStop)
171                     .addComponent(btnDeselect))
172                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
173         );
174
175         pnlInputMain.setBorder(BorderFactory.createEtchedBorder());
176
177         lblId.setText("ID");
178
179         fldId.addFocusListener(new java.awt.event.FocusAdapter() {
180
181             public void focusLost(java.awt.event.FocusEvent evt) {
182                 idFieldFocusLost(evt);
183             }
184         });
185
186         lblVideo.setText("動画");
187
188         cbVideoLocal.setText("local");
189         cbVideoLocal.addItemListener(new java.awt.event.ItemListener() {
190
191             public void itemStateChanged(java.awt.event.ItemEvent evt) {
192                 useMovieLocalCheckBoxItemStateChanged(evt);
193             }
194         });
195
196         btnVideo.setText("...");
197
198         lblComment.setText("コメント");
199
200         cbCommentLocal.setText("local");
201         cbCommentLocal.addItemListener(new java.awt.event.ItemListener() {
202
203             public void itemStateChanged(java.awt.event.ItemEvent evt) {
204                 useMovieLocalCheckBoxItemStateChanged(evt);
205             }
206         });
207
208         btnComment.setText("...");
209
210         lblOutput.setText("出力");
211
212         cbOutputEnable.setText("変換");
213         cbOutputEnable.addItemListener(new java.awt.event.ItemListener() {
214
215             public void itemStateChanged(java.awt.event.ItemEvent evt) {
216                 outputConvertCheckBoxItemStateChanged(evt);
217             }
218         });
219
220
221         GroupLayout gl_pnlInputMain = new GroupLayout(pnlInputMain);
222         pnlInputMain.setLayout(gl_pnlInputMain);
223         gl_pnlInputMain.setHorizontalGroup(
224             gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
225             .addGroup(gl_pnlInputMain.createSequentialGroup()
226                 .addContainerGap()
227                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
228                     .addGroup(gl_pnlInputMain.createSequentialGroup()
229                         .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
230                             .addComponent(lblComment)
231                             .addComponent(lblVideo)
232                             .addComponent(lblId)
233                             .addComponent(lblOutput))
234                         .addPreferredGap(ComponentPlacement.RELATED)
235                         .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
236                             .addGroup(gl_pnlInputMain.createSequentialGroup()
237                                 .addComponent(cbVideoLocal)
238                                 .addPreferredGap(ComponentPlacement.RELATED)
239                                 .addComponent(fldVideo, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE)
240                                 .addPreferredGap(ComponentPlacement.RELATED)
241                                 .addComponent(btnVideo))
242                             .addGroup(gl_pnlInputMain.createSequentialGroup()
243                                 .addComponent(fldId, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE)
244                                 .addPreferredGap(ComponentPlacement.UNRELATED)
245                                 .addComponent(cbBackLogReduce)
246                                 .addComponent(cbBackLog)
247                                 .addComponent(fldBackLog, GroupLayout.PREFERRED_SIZE, 150, GroupLayout.PREFERRED_SIZE)
248                             )
249                             .addGroup(Alignment.TRAILING, gl_pnlInputMain.createSequentialGroup()
250                                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.TRAILING)
251                                     .addGroup(Alignment.LEADING, gl_pnlInputMain.createSequentialGroup()
252                                         .addComponent(cbOutputEnable)
253                                         .addPreferredGap(ComponentPlacement.RELATED)
254                                         .addComponent(fldOutput, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE))
255                                     .addGroup(gl_pnlInputMain.createSequentialGroup()
256                                         .addComponent(cbCommentLocal)
257                                         .addPreferredGap(ComponentPlacement.RELATED)
258                                         .addComponent(fldComment, GroupLayout.DEFAULT_SIZE, 317, Short.MAX_VALUE)))
259                                 .addPreferredGap(ComponentPlacement.RELATED)
260                                 .addComponent(btnComment))))
261                     .addComponent(btnApply, Alignment.TRAILING))
262                 .addContainerGap())
263         );
264         gl_pnlInputMain.setVerticalGroup(
265             gl_pnlInputMain.createParallelGroup(Alignment.LEADING)
266             .addGroup(gl_pnlInputMain.createSequentialGroup()
267                 .addContainerGap()
268                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
269                     .addComponent(fldId, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
270                     .addComponent(lblId)
271                     .addComponent(cbBackLogReduce)
272                     .addComponent(cbBackLog)
273                     .addComponent(fldBackLog)
274                 )
275                 .addPreferredGap(ComponentPlacement.RELATED)
276                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
277                     .addComponent(lblVideo)
278                     .addComponent(fldVideo, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
279                     .addComponent(btnVideo)
280                     .addComponent(cbVideoLocal))
281                 .addPreferredGap(ComponentPlacement.RELATED)
282                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
283                     .addComponent(lblComment)
284                     .addComponent(fldComment, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
285                     .addComponent(btnComment)
286                     .addComponent(cbCommentLocal))
287                 .addPreferredGap(ComponentPlacement.RELATED)
288                 .addGroup(gl_pnlInputMain.createParallelGroup(Alignment.BASELINE)
289                     .addComponent(lblOutput)
290                     .addComponent(fldOutput, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
291                     .addComponent(cbOutputEnable))
292                 .addPreferredGap(ComponentPlacement.UNRELATED)
293                 .addComponent(btnApply)
294                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
295         );
296
297         GroupLayout gl_pnlMain = new GroupLayout(pnlMain);
298         pnlMain.setLayout(gl_pnlMain);
299         gl_pnlMain.setHorizontalGroup(
300             gl_pnlMain.createParallelGroup(Alignment.LEADING)
301             .addGroup(Alignment.TRAILING, gl_pnlMain.createSequentialGroup()
302                 .addContainerGap()
303                 .addGroup(gl_pnlMain.createParallelGroup(Alignment.TRAILING)
304                     .addComponent(pnlInputMain, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
305                     .addComponent(scrDisplay, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 480, Short.MAX_VALUE)
306                     .addComponent(pnlButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
307                 .addContainerGap())
308         );
309         gl_pnlMain.setVerticalGroup(
310             gl_pnlMain.createParallelGroup(Alignment.LEADING)
311             .addGroup(Alignment.TRAILING, gl_pnlMain.createSequentialGroup()
312                 .addContainerGap()
313                 .addComponent(scrDisplay, GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
314                 .addPreferredGap(ComponentPlacement.RELATED)
315                 .addComponent(pnlButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
316                 .addPreferredGap(ComponentPlacement.RELATED)
317                 .addComponent(pnlInputMain, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
318                 .addGap(24, 24, 24))
319         );
320
321
322         JMenuBar menuBar = initMenuBar();
323         setJMenuBar(menuBar);
324
325         GroupLayout layout = new GroupLayout(getContentPane());
326         getContentPane().setLayout(layout);
327         layout.setHorizontalGroup(
328             layout.createParallelGroup(Alignment.LEADING)
329             .addComponent(pnlMain, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
330         );
331         layout.setVerticalGroup(
332             layout.createParallelGroup(Alignment.LEADING)
333             .addComponent(pnlMain, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
334         );
335
336         pack();
337         initInputPanel();
338         pnlMain.setTransferHandler(new DownloadListTransferHandler());
339         tblDisplay.setTransferHandler(new TableTransferHandler());
340     }
341
342     private class ApplyActionListener implements ActionListener {
343
344         @Override
345         public void actionPerformed(ActionEvent e) {
346             final DownloadProfile prof = new InqubusDownloadProfile();
347             final String id = Util.getVideoId(fldId.getText());
348             logger.log(Level.INFO, prof.toString());
349             new Download(prof, id).execute();
350         }
351     }
352     /** This method is called from within the constructor to
353      * initialize the form.
354      * WARNING: Do NOT modify this code. The content of this method is
355      * always regenerated by the Form Editor.
356      */
357     @SuppressWarnings("unchecked")
358     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
359     private void initComponents() {
360     }// </editor-fold>//GEN-END:initComponents
361
362     private File searchFileMatchId(final File dir, final String id) {
363         // TODO 候補は複数返すようにして、その後の対処は呼び出しもとで行ってもらった方が良いかも
364         if (id.isEmpty()) {
365             return null;
366         }
367
368         final File[] lists = dir.listFiles(new FilenameFilter() {
369
370             final Pattern pattern = Pattern.compile(id + "\\D");
371
372             @Override
373             public boolean accept(File dir, String name) {
374                 return pattern.matcher(name).find();
375             }
376         });
377
378         if (lists.length == 1) {
379             return lists[0];
380         } else if (lists.length > 1) {
381             throw new UnsupportedOperationException();
382         } else {
383             return null;
384         }
385     }
386
387     private void useMovieLocalCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_useMovieLocalCheckBoxItemStateChanged
388         final Config p = Config.INSTANCE;
389
390         final ItemSelectable source = evt.getItemSelectable();
391
392         JButton btn;
393         JTextField field;
394         File dir;
395         if (source == cbVideoLocal) {
396             btn = btnVideo;
397             field = fldVideo;
398             dir = new File(p.getVideoDir());
399         } else {
400             btn = btnComment;
401             field = fldComment;
402             dir = new File(p.getCommentDir());
403         }
404
405         final boolean useLocal = (evt.getStateChange() == ItemEvent.SELECTED);
406         btn.setEnabled(useLocal);
407
408         String text;
409         if (useLocal) {
410             final File f = searchFileMatchId(dir, fldId.getText());
411             if (f != null) {
412                 text = f.getPath();
413             } else {
414                 text = "";
415             }
416         } else {
417             text = p.getVideoFileNamePattern();
418         }
419         field.setText(text);
420
421         fldId.setEnabled(!(cbVideoLocal.isSelected() && cbCommentLocal.isSelected()));
422
423     }//GEN-LAST:event_useMovieLocalCheckBoxItemStateChanged
424
425     private void outputConvertCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_outputConvertCheckBoxItemStateChanged
426         final boolean convert = (evt.getStateChange() == ItemEvent.SELECTED);
427         fldOutput.setEnabled(convert);
428     }//GEN-LAST:event_outputConvertCheckBoxItemStateChanged
429
430     private void idFieldFocusLost(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_idFieldFocusLost
431         final Config p = Config.INSTANCE;
432         final String id = fldId.getText();
433         if (id.isEmpty()) {
434             return;
435         }
436
437         if (cbVideoLocal.isSelected() && fldVideo.getText().isEmpty()) {
438             final File dir = new File(p.getVideoDir());
439             final File file = searchFileMatchId(dir, id);
440             if (file != null) {
441                 fldVideo.setText(file.getPath());
442             }
443         }
444
445         if (cbCommentLocal.isSelected() && fldComment.getText().isEmpty()) {
446             final File dir = new File(p.getCommentDir());
447             final File file = searchFileMatchId(dir, id);
448             if (file != null) {
449                 fldComment.setText(file.getPath());
450             }
451         }
452
453     }//GEN-LAST:event_idFieldFocusLost
454     // Variables declaration - do not modify//GEN-BEGIN:variables
455     private final JTable tblDisplay;
456     // ボタン領域
457     private final JButton btnStart;
458     private final JButton btnStop;
459     private final JButton btnDeselect;
460     //入力領域 - 標準
461     private final JTextField fldId;
462     private final JCheckBox cbBackLogReduce = new JCheckBox("コメ数減少");
463     private final JCheckBox cbBackLog = new JCheckBox("過去ログ");
464     private final JTextField fldBackLog = new JTextField();
465     private final JCheckBox cbVideoLocal;
466     private final JTextField fldVideo;
467     private final JButton btnVideo;
468     private final JCheckBox cbCommentLocal;
469     private final JTextField fldComment;
470     private final JButton btnComment;
471     private final JCheckBox cbOutputEnable;
472     private final JTextField fldOutput;
473     // 適用
474     private final JButton btnApply = new JButton("適用");
475     // End of variables declaration//GEN-END:variables
476
477     private void initInputPanel() {
478         final Config p = Config.INSTANCE;
479
480         fldId.setText("");
481         fldBackLog.setEnabled(false);
482         fldBackLog.setToolTipText("YYYY/MM/DD hh:mm:ss形式、あるいは1970/01/01からの経過秒を入力します。");
483         cbBackLog.setEnabled(true);
484         cbBackLog.addItemListener(new ItemListener() {
485
486             @Override
487             public void itemStateChanged(ItemEvent e) {
488                 final boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
489                 fldBackLog.setEnabled(selected);
490             }
491         });
492         cbBackLogReduce.setToolTipText("「コメントの量を減らす」場合はチェックを付けます。");
493
494         final boolean movieLocal = p.getVideoUseLocal();
495         cbVideoLocal.setSelected(movieLocal);
496         btnVideo.setEnabled(movieLocal);
497         if (!movieLocal) {
498             fldVideo.setText(p.getVideoFileNamePattern());
499         }
500
501         final boolean commentLocal = p.getCommentUseLocal();
502         cbCommentLocal.setSelected(commentLocal);
503         btnComment.setEnabled(commentLocal);
504         if (!commentLocal) {
505             fldComment.setText(p.getCommentFileNamePattern());
506         }
507
508         final boolean convert = p.getOutputEnable();
509         cbOutputEnable.setSelected(convert);
510         fldOutput.setEnabled(convert);
511         fldOutput.setText(p.getOutputFileNamePattern());
512
513     }
514
515     private JMenuBar initMenuBar() {
516         final JMenuBar menuBar = new JMenuBar();
517
518         final JMenu mnFile = new JMenu("ファイル(F)");
519         menuBar.add(mnFile);
520
521         final JMenuItem itExit = new JMenuItem("終了(X)", KeyEvent.VK_X);
522         itExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.CTRL_MASK));
523         itExit.addActionListener(new ActionListener() {
524
525             @Override
526             public void actionPerformed(ActionEvent e) {
527                 throw new UnsupportedOperationException("Not supported yet.");
528             }
529         });
530         mnFile.add(itExit);
531
532         final JMenu mnTool = new JMenu("ツール(T)");
533         menuBar.add(mnTool);
534
535         final JMenuItem itOption = new JMenuItem("オプション(O)...", KeyEvent.VK_O);
536         // TODO ショートカットキー
537         itOption.addActionListener(new ActionListener() {
538
539             @Override
540             public void actionPerformed(ActionEvent e) {
541                 final yukihane.inqubus.gui.ConfigDialog dlg = new yukihane.inqubus.gui.ConfigDialog(MainFrame.this);
542                 dlg.setLocationRelativeTo(MainFrame.this);
543                 dlg.setModal(true);
544                 dlg.setVisible(true);
545             }
546         });
547         mnTool.add(itOption);
548
549         final JMenu mnHelp = new JMenu("ヘルプ(H)");
550         menuBar.add(mnHelp);
551
552         final JMenuItem itAbout = new JMenuItem("このソフトウェアについて(A)...", KeyEvent.VK_A);
553         itAbout.addActionListener(new ActionListener() {
554
555             @Override
556             public void actionPerformed(ActionEvent e) {
557                 MainFrame_AboutBox dlg = new MainFrame_AboutBox(MainFrame.this);
558                 dlg.pack();
559                 dlg.setLocationRelativeTo(MainFrame.this);
560                 dlg.setModal(true);
561                 dlg.setVisible(true);
562             }
563         });
564         mnHelp.add(itAbout);
565
566         return menuBar;
567     }
568
569     private class DownloadListTransferHandler extends TransferHandler {
570
571         private static final long serialVersionUID = 1L;
572         private final Pattern movieIdPattern = Pattern.compile("(\\w\\w\\d+)");
573
574         @Override
575         public boolean canImport(TransferHandler.TransferSupport support) {
576             Transferable transferable = support.getTransferable();
577             if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)
578                     || transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
579                 return true;
580             }
581             return false;
582         }
583
584         @Override
585         public boolean importData(TransferHandler.TransferSupport support) {
586             try {
587                 Transferable transferable = support.getTransferable();
588                 if (transferable.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
589                     @SuppressWarnings("unchecked")
590                     final List<File> data = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor);
591                     Collection<Target> targets = Target.from(data);
592                     targetModel.addTarget(targets);
593                 } else if (transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {
594                     String data = (String) transferable.getTransferData(DataFlavor.stringFlavor);
595                     Matcher matcher = movieIdPattern.matcher(data);
596                     if (matcher.find()) {
597                         String movieId = matcher.group(1);
598                         Target target = Target.fromId(movieId);
599                         targetModel.addTarget(target);
600                     } else {
601                         return false;
602                     }
603
604                 }
605                 return false;
606             } catch (Exception e) {
607                 logger.log(Level.SEVERE, null, e);
608                 return false;
609             }
610         }
611     }
612
613     private class TableTransferHandler extends DownloadListTransferHandler {
614
615         private static final long serialVersionUID = 1L;
616
617         @Override
618         public boolean canImport(TransferHandler.TransferSupport support) {
619             return super.canImport(support);
620         }
621
622         @Override
623         public boolean importData(TransferHandler.TransferSupport support) {
624             return super.importData(support);
625         }
626     }
627
628
629     private class InqubusDownloadProfile implements DownloadProfile {
630
631         private final LoginProfile loginProfile;
632         private final ProxyProfile proxyProfile;
633         private final VideoProfile videoProfile;
634         private final CommentProfile commentProfile;
635         private final GeneralProfile generalProfile;
636
637         private InqubusDownloadProfile() {
638             this.loginProfile = new InqubusLoginProfile();
639             this.proxyProfile = new InqubusProxyProfile();
640             this.videoProfile = new InqubusVideoProfile();
641             this.commentProfile = new InqubusCommentProfile();
642             this.generalProfile = new InqubusGeneralProfile();
643         }
644
645         @Override
646         public LoginProfile getLoginInfo() {
647             return this.loginProfile;
648         }
649
650         @Override
651         public ProxyProfile getProxyProfile() {
652             return this.proxyProfile;
653         }
654
655         @Override
656         public VideoProfile getVideoProfile() {
657             return this.videoProfile;
658         }
659
660         @Override
661         public CommentProfile getCommentProfile() {
662             return this.commentProfile;
663         }
664
665         @Override
666         public GeneralProfile getGeneralProfile() {
667             return this.generalProfile;
668         }
669
670         @Override
671         public String toString(){
672             return ToStringBuilder.reflectionToString(this);
673         }
674     }
675
676     private class InqubusLoginProfile implements LoginProfile {
677         private final String mail;
678         private final String password;
679
680         private InqubusLoginProfile(){
681             final Config p = Config.INSTANCE;
682             this.mail = p.getId();
683             this.password = p.getPassword();
684         }
685
686         @Override
687         public String getMail() {
688             return this.mail;
689         }
690
691         @Override
692         public String getPassword() {
693             return this.password;
694         }
695
696         @Override
697         public String toString(){
698             return ToStringBuilder.reflectionToString(this);
699         }
700     }
701
702     private class InqubusProxyProfile implements ProxyProfile {
703         private final boolean use;
704         private final String host;
705         private final int port;
706
707         private InqubusProxyProfile(){
708             final Config p = Config.INSTANCE;
709             this.use = p.getProxyUse();
710             this.host = p.getProxyHost();
711             final String pp = p.getProxyPort();
712             this.port = StringUtils.isBlank(pp) ? -1 : Integer.parseInt(pp);
713         }
714
715         @Override
716         public boolean use() {
717             return this.use;
718         }
719
720         @Override
721         public String getHost() {
722             return this.host;
723         }
724
725         @Override
726         public int getPort() {
727             return this.port;
728         }
729
730         @Override
731         public String toString(){
732             return ToStringBuilder.reflectionToString(this);
733         }
734     }
735
736     private class InqubusVideoProfile implements VideoProfile {
737         private final boolean download;
738         private final File dir;
739         private final String fileName;
740         private final File localFile;
741
742         private InqubusVideoProfile(){
743             final Config p = Config.INSTANCE;
744             this.download = !cbVideoLocal.isSelected();
745             if (this.download) {
746                 this.dir = new File(p.getVideoDir());
747                 this.fileName = fldVideo.getText();
748                 this.localFile = null;
749             } else {
750                 this.dir = null;
751                 this.fileName = null;
752                 this.localFile = new File(fldVideo.getText());
753             }
754         }
755
756         @Override
757         public boolean isDownload() {
758             return this.download;
759         }
760
761         @Override
762         public File getDir() {
763             return this.dir;
764         }
765
766         @Override
767         public String getFileName() {
768             return this.fileName;
769         }
770
771         @Override
772         public File getLocalFile() {
773             return this.localFile;
774         }
775
776         @Override
777         public String toString(){
778             return ToStringBuilder.reflectionToString(this);
779         }
780     }
781
782     private class InqubusCommentProfile implements CommentProfile {
783         private final boolean download;
784         private final File dir;
785         private final String fileName;
786         private final File localFile;
787         private final int lengthRelatedCommentSize;
788         private final boolean disablePerMinComment;
789         private final int perMinCommentSize;
790         private final long backLogPoint;
791
792         private InqubusCommentProfile() {
793             final Config p = Config.INSTANCE;
794             this.download = !cbVideoLocal.isSelected();
795             if (this.download) {
796                 this.dir = new File(p.getVideoDir());
797                 this.fileName = fldVideo.getText();
798                 this.localFile = null;
799             } else {
800                 this.dir = null;
801                 this.fileName = null;
802                 this.localFile = new File(fldVideo.getText());
803             }
804
805             if(cbBackLog.isSelected()) {
806                 try {
807                     this.backLogPoint = WayBackTimeParser.parse(fldBackLog.getText());
808                 } catch (IOException ex) {
809                     throw new IllegalArgumentException("過去ログ時刻指定が誤っています。", ex);
810                 }
811             } else {
812                 this.backLogPoint = -1L;
813             }
814
815             this.disablePerMinComment = cbBackLogReduce.isSelected();
816             this.lengthRelatedCommentSize
817                     = (p.getCommentSizeAutosize()) ? -1 : Integer.parseInt(p.getCommentSizeManual());
818             this.perMinCommentSize
819                     = (p.getCommentMinSizeAutosize()) ? -1 : Integer.parseInt(p.getCommentMinSizeManual());
820         }
821
822         @Override
823         public boolean isDownload() {
824             return this.download;
825         }
826
827         @Override
828         public File getDir() {
829             return this.dir;
830         }
831
832         @Override
833         public String getFileName() {
834             return this.fileName;
835         }
836
837         @Override
838         public File getLocalFile() {
839             return this.localFile;
840         }
841
842         @Override
843         public int getLengthRelatedCommentSize() {
844             return this.lengthRelatedCommentSize;
845         }
846
847         @Override
848         public boolean isDisablePerMinComment() {
849             return this.disablePerMinComment;
850         }
851
852         @Override
853         public int getPerMinCommentSize() {
854             return this.perMinCommentSize;
855         }
856
857         @Override
858         public long getBackLogPoint() {
859             return this.backLogPoint;
860         }
861
862         @Override
863         public String toString(){
864             return ToStringBuilder.reflectionToString(this);
865         }
866     }
867
868     private class InqubusGeneralProfile implements GeneralProfile {
869         private final String replaceFrom;
870         private final String replaceTo;
871         private InqubusGeneralProfile() {
872             final Config p = Config.INSTANCE;
873             this.replaceFrom = p.getReplaceFrom();
874             this.replaceTo = p.getReplaceTo();
875         }
876
877         @Override
878         public String getReplaceFrom() {
879             return this.replaceFrom;
880         }
881
882         @Override
883         public String getReplaceTo() {
884             return this.replaceTo;
885         }
886
887         @Override
888         public String toString(){
889             return ToStringBuilder.reflectionToString(this);
890         }
891     }
892 }