OSDN Git Service

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