OSDN Git Service

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