001    /////////////////////////////////////////////////
002    // This file is part of Sears project.
003    // Subtitle Editor And Re-Synch
004    // A tool to easily modify and resynch movies subtitles.
005    /////////////////////////////////////////////////
006    //This program is free software; 
007    //you can redistribute it and/or modify it under the terms 
008    //of the GNU General Public License 
009    //as published by the Free Software Foundation; 
010    //either version 2 of the License, or (at your option) any later version.
011    /////////////////////////////////////////////////
012    //Sears project is available under sourceforge
013    // at adress: http://sourceforge.net/projects/sears/
014    //Copyright (C) 2005 Booba Skaya
015    //Mail: booba.skaya@gmail.com
016    ////////////////////////////////////////////////
017    package sears.gui;
018    
019    import java.awt.BorderLayout;
020    import java.awt.GridBagConstraints;
021    import java.awt.GridBagLayout;
022    import java.awt.GridLayout;
023    import java.awt.event.ActionEvent;
024    import java.awt.event.ActionListener;
025    import java.io.File;
026    import java.io.IOException;
027    import java.util.ArrayList;
028    
029    import javax.swing.BorderFactory;
030    import javax.swing.JButton;
031    import javax.swing.JCheckBox;
032    import javax.swing.JLabel;
033    import javax.swing.JOptionPane;
034    import javax.swing.JPanel;
035    import javax.swing.JSlider;
036    import javax.swing.JTextField;
037    import javax.swing.event.ChangeEvent;
038    import javax.swing.event.ChangeListener;
039    
040    import sears.file.Subtitle;
041    import sears.file.SubtitleFile;
042    import sears.gui.resources.SearsResources;
043    import sears.tools.SearsResourceBundle;
044    import sears.tools.Trace;
045    
046    /**
047     * Class JDialogSplit.
048     * <br><b>Summary:</b><br>
049     * This Class permit to enter Split parameters.
050     */
051    public class JDialogSplit extends SearsJDialog implements ChangeListener {
052        private static final long serialVersionUID = -1582903664124772065L;
053    
054        private JPanel jContentPane = null;
055    
056        private JPanel jPanelCenter = null;
057    
058        private JPanel jPanelOutputFiles = null;
059    
060        private JLabel jLabelPart1 = null;
061    
062        private JTextField jTextFieldPart1 = null;
063    
064        private JButton jButtonPart1 = null;
065    
066        private JLabel jLabelPart2 = null;
067    
068        private JTextField jTextFieldPart2 = null;
069    
070        private JButton jButtonPart2 = null;
071    
072        private JPanel jPanelSubtitle = null;
073    
074        private JLabel jLabelSubtitle = null;
075    
076        private JSlider jSliderSubtitle = null;
077    
078        private JPanel jPanelChecks = null;
079    
080        private JCheckBox jCheckBoxDelay = null;
081    
082        private JTextField jTextFieldDelay = null;
083    
084        private JCheckBox jCheckBoxOpenSecond = null;
085    
086        /**The entered destination files. This array is filled in the checkParameters method*/
087        private File[] destinationFiles;
088    
089        /**The subtitleList, that contains all the subtitles.*/
090        private ArrayList<Subtitle> subtitleList;
091    
092        /**The first choosen subtitle and the last choosen one, when user validates the dialog.*/
093        private int choosenSubtitle;
094    
095        /**The opened subtitleFile.*/
096        private SubtitleFile subtitleFile;
097    
098        /**
099         * This is the default constructor
100         * @param _subtitleList     the subtitle list
101         * @param _choosenSubtitle  the subtitle number where the split will occur
102         * @param _subtitleFile     the subtitle file
103         */
104        public JDialogSplit(ArrayList<Subtitle> _subtitleList, int _choosenSubtitle, SubtitleFile _subtitleFile) {
105            super(SearsResourceBundle.getResource("split_title"));
106            //Initialize attributes.
107            choosenSubtitle = _choosenSubtitle;
108            subtitleList = _subtitleList;
109            validationStatus = false;
110            subtitleFile = _subtitleFile;
111            initializeDestinationFiles();
112            setContentPane(getJContentPane());
113            configureSize();
114        }
115    
116        /**
117         * Method initializeDestinationFiles.
118         * <br><b>Summary:</b><br>
119         * Call to method to initialize the destination files array.
120         */
121        private void initializeDestinationFiles() {
122            //construct array.
123            destinationFiles = new File[2];
124            //Construct filename 1 (which will contains the first range)
125            String fileName = subtitleFile.getFile().getName();
126            //remove extension
127            fileName = fileName.substring(0, fileName.lastIndexOf("."));
128            destinationFiles[0] = new File(subtitleFile.getFile().getParent() + 
129                            File.separator + fileName + "_1." + subtitleFile.extension());
130            //construct filename 2
131            destinationFiles[1] = new File(subtitleFile.getFile().getParent() + 
132                            File.separator + fileName + "_2." + subtitleFile.extension());
133        }
134    
135        /**
136         * This method initializes jContentPane
137         * 
138         * @return javax.swing.JPanel
139         */
140        private JPanel getJContentPane() {
141            if (jContentPane == null) {
142                jContentPane = new JPanel();
143                jContentPane.setLayout(new BorderLayout());
144                jContentPane.setBorder(super.createEmptyBorder());
145                jContentPane.add(getJPanelButtons(), java.awt.BorderLayout.SOUTH);
146                jContentPane.add(getJPanelCenter(), java.awt.BorderLayout.CENTER);
147            }
148            return jContentPane;
149        }
150    
151        /**
152         * Method okAction.
153         * <br><b>Summary:</b><br>
154         * This method is called when user validate the dialog.
155         */
156        protected void okAction() {
157            //Just have to check parameters validity, if valids, dispose dialog,and set validation status to true.
158            String error = checkParameters();
159            if (error != null && !error.equals("")) {
160                //Show error message.
161                JOptionPane.showMessageDialog(this, error, SearsResourceBundle.getResource("error_splitConfigurationError"),
162                        JOptionPane.ERROR_MESSAGE);
163                Trace.trace("Error in Split parameters:" + error, Trace.WARNING_PRIORITY);
164            } else {
165                //else split configuration is valid, release dialog.
166                validationStatus = true;
167                dispose();
168            }
169        }
170    
171        /**
172         * Method checkParameters.
173         * <br><b>Summary:</b><br>
174         * This method permits to check the split parameters validity.
175         * It sets the final parameters that will be returned by the public methods.
176         * @return  <b>String</b>   "" if there is no error in parameters, error message otherwise.
177         */
178        private String checkParameters() {
179            //The error message that will be returned.
180            String errorMessage = "";
181            //Check first destination file.
182            errorMessage += checkFile(getJTextFieldPart1().getText(), 0);
183            //check second file.
184            errorMessage += checkFile(getJTextFieldPart2().getText(), 1);
185            //check choosen subtitle.
186            //nothing to check... slider is obsviously between min and max :/
187            choosenSubtitle = getJSliderSubtitle().getValue();
188            //check delay.
189            if (getJCheckBoxDelay().isSelected()) {
190                String delay = getJTextFieldDelay().getText();
191                if (delay != null && !delay.equals("")) {
192                    try {
193                        Double.parseDouble(delay);
194                    } catch (NumberFormatException e) {
195                        //Delay value is not a number.
196                        errorMessage += SearsResourceBundle.getResource("error_delayNotValid") + "\n";
197                    }
198                } else {
199                    //Delay value is null.
200                    errorMessage += SearsResourceBundle.getResource("error_delayNull") + "\n";
201                }
202            }
203            return errorMessage;
204        }
205    
206        /**
207         * Method checkFile.
208         * <br><b>Summary:</b><br>
209         * This method check a destination fileName validity.
210         * @param fileName          The fileNAme to check.
211         * @param destinationIndex  The destination index, 0 for first part, and 1 for the second part.
212         * @return String           The new error message.
213         */
214        private String checkFile(String fileName, int destinationIndex) {
215            String errorMessage = "";
216            if (fileName != null && !fileName.equals("")) {
217                File file = new File(fileName);
218                try {
219                    if ((file.exists() && file.canWrite()) || file.createNewFile()) {
220                        //set destination file
221                        destinationFiles[destinationIndex] = file;
222                    } else {
223                        //cannot write to file!
224                        errorMessage += SearsResourceBundle.getResource("error_cannotWrite") + fileName + ".\n";
225                    }
226                } catch (IOException e) {
227                    //cannot write to file!
228                    errorMessage += SearsResourceBundle.getResource("error_cannotWrite") + fileName + ".\n";
229                }
230            } else {
231                //cannot write to file!
232                errorMessage += SearsResourceBundle.getResource("error_destinationNotDefined1") + " " + (destinationIndex + 1) + " "
233                        + SearsResourceBundle.getResource("error_destinationNotDefined2") + "\n";
234            }
235            return errorMessage;
236        }
237    
238        /**
239         * This method initializes jPanel   
240         *  
241         * @return javax.swing.JPanel       
242         */
243        private JPanel getJPanelCenter() {
244            if (jPanelCenter == null) {
245                GridBagConstraints gridBagConstraints12 = new GridBagConstraints();
246                gridBagConstraints12.gridx = 0;
247                gridBagConstraints12.gridy = 4;
248                gridBagConstraints12.anchor = java.awt.GridBagConstraints.WEST;
249                gridBagConstraints12.fill = java.awt.GridBagConstraints.HORIZONTAL;
250                GridBagConstraints gridBagConstraints11 = new GridBagConstraints();
251                gridBagConstraints11.fill = java.awt.GridBagConstraints.HORIZONTAL;
252                gridBagConstraints11.gridx = 0;
253                gridBagConstraints11.gridy = 2;
254                gridBagConstraints11.gridheight = 2;
255                GridBagConstraints gridBagConstraints = new GridBagConstraints();
256                gridBagConstraints.gridx = 0;
257                gridBagConstraints.gridheight = 2;
258                gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
259                gridBagConstraints.gridy = 0;
260                jPanelCenter = new JPanel();
261                jPanelCenter.setLayout(new GridBagLayout());
262                jPanelCenter.add(getJPanelOutputFiles(), gridBagConstraints);
263                jPanelCenter.add(getJPanelSubtitle(), gridBagConstraints11);
264                jPanelCenter.add(getJPanelChecks(), gridBagConstraints12);
265            }
266            return jPanelCenter;
267        }
268    
269        /**
270         * This method initializes jPanel   
271         *  
272         * @return javax.swing.JPanel       
273         */
274        private JPanel getJPanelOutputFiles() {
275            if (jPanelOutputFiles == null) {
276                GridBagConstraints gridBagConstraints4 = new GridBagConstraints();
277                gridBagConstraints4.fill = java.awt.GridBagConstraints.HORIZONTAL;
278                gridBagConstraints4.weightx = 1.0;
279                gridBagConstraints4.gridx = 1;
280                gridBagConstraints4.gridy = 1;
281                GridBagConstraints gridBagConstraints5 = new GridBagConstraints();
282                gridBagConstraints5.gridx = 2;
283                gridBagConstraints5.insets = new java.awt.Insets(1, 0, 7, 0);
284                gridBagConstraints5.ipadx = 0;
285                gridBagConstraints5.gridy = 1;
286                GridBagConstraints gridBagConstraints3 = new GridBagConstraints();
287                gridBagConstraints3.gridx = 0;
288                gridBagConstraints3.gridy = 1;
289                gridBagConstraints3.ipadx = 4;
290                jLabelPart2 = new JLabel();
291                jLabelPart2.setText(SearsResourceBundle.getResource("split_part") + " 2");
292                jLabelPart1 = new JLabel();
293                jLabelPart1.setText(SearsResourceBundle.getResource("split_part") + " 1");
294                GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
295                gridBagConstraints2.gridx = 2;
296                gridBagConstraints2.gridy = 0;
297                GridBagConstraints gridBagConstraints1 = new GridBagConstraints();
298                gridBagConstraints1.gridx = 1;
299                gridBagConstraints1.gridy = 0;
300                gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL;
301                gridBagConstraints1.weightx = 1.0;
302                // jLabelPart1 constraints:
303                GridBagConstraints gridBagConstraints0 = new GridBagConstraints();
304                gridBagConstraints0.ipadx = 4;
305                jPanelOutputFiles = new JPanel();
306                jPanelOutputFiles.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), SearsResourceBundle
307                        .getResource("split_outputFiles")));
308                jPanelOutputFiles.setSize(400, 200);
309                jPanelOutputFiles.setLayout(new GridBagLayout());
310                jPanelOutputFiles.add(jLabelPart1, gridBagConstraints0);
311                jPanelOutputFiles.add(getJTextFieldPart1(), gridBagConstraints1);
312                jPanelOutputFiles.add(getJButtonPart1(), gridBagConstraints2);
313                jPanelOutputFiles.add(jLabelPart2, gridBagConstraints3);
314                jPanelOutputFiles.add(getJTextFieldPart2(), gridBagConstraints4);
315                jPanelOutputFiles.add(getJButtonPart2(), gridBagConstraints5);
316            }
317            return jPanelOutputFiles;
318        }
319    
320        /**
321         * This method initializes jTextField       
322         *  
323         * @return javax.swing.JTextField   
324         */
325        private JTextField getJTextFieldPart1() {
326            if (jTextFieldPart1 == null) {
327                jTextFieldPart1 = new JTextField(18);
328                jTextFieldPart1.setText(destinationFiles[0].getAbsolutePath());
329            }
330            return jTextFieldPart1;
331        }
332    
333        /**
334         * This method initializes jButton  
335         *  
336         * @return javax.swing.JButton      
337         */
338        private JButton getJButtonPart1() {
339            if (jButtonPart1 == null) {
340                jButtonPart1 = new JButton(SearsResources.getIcon("BrowseIcon"));
341                jButtonPart1.addActionListener(new ActionListener() {
342                    public void actionPerformed(ActionEvent e) {
343                        browseDestinationFile(0);
344                    }
345                });
346            }
347            return jButtonPart1;
348        }
349    
350        /**
351         * Method browseDestinationFile.
352         * <br><b>Summary:</b><br>
353         * Use thid method to choose destination files.
354         * @param i     An <b>int</b> equals to 0 to choose first part, or 1 to choose second part.
355         */
356        protected void browseDestinationFile(int i) {
357            File choosenFile = MainWindow.instance.showSRTBrowser();
358            if (choosenFile != null) {
359                if (i == 0) {
360                    getJTextFieldPart1().setText(choosenFile.getAbsolutePath());
361                } else {
362                    getJTextFieldPart2().setText(choosenFile.getAbsolutePath());
363                }
364            }
365        }
366    
367        /**
368         * This method initializes jTextField       
369         *  
370         * @return javax.swing.JTextField   
371         */
372        private JTextField getJTextFieldPart2() {
373            if (jTextFieldPart2 == null) {
374                jTextFieldPart2 = new JTextField(18);
375                jTextFieldPart2.setText(destinationFiles[1].getAbsolutePath());
376            }
377            return jTextFieldPart2;
378        }
379    
380        /**
381         * This method initializes jButton  
382         *  
383         * @return javax.swing.JButton      
384         */
385        private JButton getJButtonPart2() {
386            if (jButtonPart2 == null) {
387                jButtonPart2 = new JButton(SearsResources.getIcon("BrowseIcon"));
388                jButtonPart2.addActionListener(new ActionListener() {
389                    public void actionPerformed(ActionEvent e) {
390                        browseDestinationFile(1);
391                    }
392                });
393            }
394            return jButtonPart2;
395        }
396    
397        /**
398         * This method initializes jPanel   
399         *  
400         * @return javax.swing.JPanel       
401         */
402        private JPanel getJPanelSubtitle() {
403            if (jPanelSubtitle == null) {
404                jPanelSubtitle = new JPanel();
405                jPanelSubtitle.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), SearsResourceBundle
406                        .getResource("split_cutOnST")));
407                jPanelSubtitle.setLayout(new GridLayout(2, 1));
408                jPanelSubtitle.add(getJLabelSubtitle(), null);
409                jPanelSubtitle.add(getJSliderSubtitle(), null);
410            }
411            return jPanelSubtitle;
412        }
413    
414        /**
415         * This method initialize the JLabelSubtitle.
416         * 
417         * @return javax.swing.JLabel
418         */
419        private JLabel getJLabelSubtitle() {
420            if (jLabelSubtitle == null) {
421                jLabelSubtitle = new JLabel();
422                setSubtitleLabel(choosenSubtitle);
423            }
424            return jLabelSubtitle;
425        }
426    
427        /**
428         * This method initializes jSlider  
429         *  
430         * @return javax.swing.JSlider      
431         */
432        private JSlider getJSliderSubtitle() {
433            if (jSliderSubtitle == null) {
434                jSliderSubtitle = new JSlider(0, subtitleList.size() - 1, choosenSubtitle);
435                jSliderSubtitle.addChangeListener(this);
436            }
437            return jSliderSubtitle;
438        }
439    
440        /**
441         * This method initializes jPanel   
442         *  
443         * @return javax.swing.JPanel       
444         */
445        private JPanel getJPanelChecks() {
446            if (jPanelChecks == null) {
447                GridBagConstraints gridBagConstraints8 = new GridBagConstraints();
448                gridBagConstraints8.gridx = 0;
449                gridBagConstraints8.anchor = java.awt.GridBagConstraints.WEST;
450                gridBagConstraints8.gridwidth = 2;
451                gridBagConstraints8.gridy = 1;
452                GridBagConstraints gridBagConstraints7 = new GridBagConstraints();
453                gridBagConstraints7.fill = java.awt.GridBagConstraints.NONE;
454                gridBagConstraints7.weightx = 1.0;
455                GridBagConstraints gridBagConstraints6 = new GridBagConstraints();
456                gridBagConstraints6.anchor = java.awt.GridBagConstraints.WEST;
457                jPanelChecks = new JPanel();
458                jPanelChecks.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), SearsResourceBundle
459                        .getResource("split_options")));
460                jPanelChecks.setLayout(new GridBagLayout());
461                jPanelChecks.add(getJCheckBoxDelay(), gridBagConstraints6);
462                jPanelChecks.add(getJTextFieldDelay(), gridBagConstraints7);
463                jPanelChecks.add(getJCheckBoxOpenSecond(), gridBagConstraints8);
464            }
465            return jPanelChecks;
466        }
467    
468        /**
469         * This method initializes jCheckBox        
470         *  
471         * @return javax.swing.JCheckBox    
472         */
473        private JCheckBox getJCheckBoxDelay() {
474            if (jCheckBoxDelay == null) {
475                jCheckBoxDelay = new JCheckBox(SearsResourceBundle.getResource("split_delay2ndPart"));
476            }
477            return jCheckBoxDelay;
478        }
479    
480        /**
481         * This method initializes jTextField       
482         *  
483         * @return javax.swing.JTextField   
484         */
485        private JTextField getJTextFieldDelay() {
486            if (jTextFieldDelay == null) {
487                jTextFieldDelay = new JTextField("1", 4);
488                jTextFieldDelay.setToolTipText(SearsResourceBundle.getResource("delay_tip"));
489            }
490            return jTextFieldDelay;
491        }
492    
493        /**
494         * This method initializes jCheckBox        
495         *  
496         * @return javax.swing.JCheckBox    
497         */
498        private JCheckBox getJCheckBoxOpenSecond() {
499            if (jCheckBoxOpenSecond == null) {
500                jCheckBoxOpenSecond = new JCheckBox(SearsResourceBundle.getResource("split_open2ndPart"));
501            }
502            return jCheckBoxOpenSecond;
503        }
504    
505        /**
506         * Method getDestinationsFiles.
507         * <br><b>Summary:</b><br>
508         * This method returns the entered destinations files.
509         * @return  <b>Files[]</b>      The entered destination files. or null if user didn't validate.
510         */
511        public File[] getDestinationsFiles() {
512            //The result of the method.
513            return destinationFiles;
514        }
515    
516        /* (non-Javadoc)
517         * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
518         */
519        public void stateChanged(ChangeEvent e) {
520            //When slider change of value, change the content of the jLabelSubtitle.
521            if (e.getSource() == jSliderSubtitle) {
522                setSubtitleLabel(jSliderSubtitle.getValue());
523            }
524        }
525    
526        /**
527         * Method setSubtitleLabel.
528         * <br><b>Summary:</b><br>
529         * Call this method to fill the subtitle label with the content of the subtitle at the given index. 
530         * @param index     The <b>int</b> index to find the subtitle.
531         */
532        private void setSubtitleLabel(int index) {
533            //Get the subtitle at the given index.
534            Subtitle subtitle = (Subtitle) subtitleList.get(index);
535            //construct message to show in label.
536            //Put ST number, and append the message.
537            String message = "[" + subtitle.getNumber() + "]" + subtitle.getSubtitle();
538            //Cut long ST, not to perturb GUI.
539            int maxLength = 35;
540            if (message.length() > maxLength) {
541                message = message.substring(0, maxLength - 1) + "...";
542            }
543            //set the label to the content of the subtitle.
544            getJLabelSubtitle().setText(message);
545        }
546    
547        /**
548         * Method getDelay.
549         * <br><b>Summary:</b><br>
550         * This method return the delay entered for the second part.
551         * @return  double     The delay entered for the second part, or 1 if not delay is necessary (1s will be the delay by default).
552         */
553        public double getSecondPartDelay() {
554            //the result of the method.
555            double result = -1;
556            if (getJCheckBoxDelay().isSelected()) {
557                try {
558                    //get the delay from the textField.
559                    result = Double.parseDouble(getJTextFieldDelay().getText());
560                } catch (NumberFormatException e) {
561                    Trace.trace("Bad number entered in Delay ! Delay is ignored.", Trace.ERROR_PRIORITY);
562                    //set a delay of 1
563                    result = 1;
564                }
565            }
566            //return the result
567            return result;
568        }
569    
570        /**
571         * Method needToOpenSecondPart.
572         * <br><b>Summary:</b><br>
573         * Return true if the open second part checkBox is selected.
574         * @return  boolean True if the open second part checkBox is selected. False otherwise.
575         */
576        public boolean needToOpenSecondPart() {
577            //return true if checkBox is selectd.
578            return getJCheckBoxOpenSecond().isSelected();
579        }
580    
581        /**
582         * Method getChoosenSubtitle.
583         * <br><b>Summary:</b><br>
584         * This method return the choosen subtitle index for spliting.
585         * @return  <b>int</b>  The choosen subtitle index for spliting.
586         */
587        public int getChoosenSubtitle() {
588            return choosenSubtitle;
589        }
590    
591        /* (non-Javadoc)
592         * @see sears.gui.SearsJDialog#getDialogName()
593         */
594        protected String getDialogName() {
595            return "split";
596        }
597    }