OSDN Git Service

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