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.awt.event.FocusEvent;
026    import java.awt.event.FocusListener;
027    
028    import javax.swing.JLabel;
029    import javax.swing.JOptionPane;
030    import javax.swing.JPanel;
031    import javax.swing.JSlider;
032    import javax.swing.JTextField;
033    import javax.swing.event.ChangeEvent;
034    import javax.swing.event.ChangeListener;
035    
036    import sears.tools.SearsResourceBundle;
037    import sears.tools.Trace;
038    
039    
040    /**
041     * Class JDialogNormalizeAction.
042     * This dialog permits to define the parameters of a normalizeDuration action.
043     * i.e the min duration and the max duration of the ST.
044     */
045    public class JDialogNormalizeAction extends SearsJDialog {
046    
047            /** (<b>long</b>) serialVersionUID: The serialVersionUID */
048            private static final long serialVersionUID = -6105540923502362579L;
049            private JPanel jContentPane;  
050            private JPanel jPanelCenter;
051            
052            protected static final int MIN = 0;
053            
054            protected static final int MAX = 1;
055            
056            private static final int NB_TIMES = 2;
057            
058            private static final String[] MIN_MAX_SUFFIXES = new String[]{"min", "max"};
059            private static final String[] DEFAULT_MIN_MAX =  new String[]{"0.5", "5"};
060            
061            private JPanel[] jPanelTime = null;
062            private JLabel[] jLabelTime = null;
063            private JTextField[] jTextFieldTime = null;
064            private JSlider[] jSliderTime = null;
065            
066            /**
067         * This is the default constructor
068         */
069        public JDialogNormalizeAction() {
070            super(SearsResourceBundle.getResource("normalizeDuration_title"));
071            jPanelTime = new JPanel[NB_TIMES];
072            jTextFieldTime = new JTextField[NB_TIMES];
073            jSliderTime = new JSlider[NB_TIMES];
074            jLabelTime = new JLabel[NB_TIMES];
075            setContentPane(getJContentPane());
076            configureSize();
077        }
078            
079        /**
080         * Method getJContentPane.
081         * <br><b>Summary:</b><br>
082         * return the contentPane
083         * @return  (<b>JPanel</b>)   A JPanel.
084         */
085        private JPanel getJContentPane() {
086            if (jContentPane == null) {
087                jContentPane = new JPanel();
088                jContentPane.setBorder(super.createEmptyBorder());
089                jContentPane.setLayout(new BorderLayout());
090                jContentPane.add(getJPanelCenter(), java.awt.BorderLayout.CENTER);
091                jContentPane.add(getJPanelButtons(), java.awt.BorderLayout.SOUTH);
092            }
093            return jContentPane;
094        }
095        
096        /**
097         * This method initializes jPanel   
098         *  
099         * @return javax.swing.JPanel       
100         */
101        private JPanel getJPanelCenter() {
102            if (jPanelCenter == null) {
103                jPanelCenter = new JPanel();
104                jPanelCenter.setLayout(new GridLayout(NB_TIMES,1));
105                jPanelCenter.add(getJPanelTime(MIN));
106                jPanelCenter.add(getJPanelTime(MAX));
107            }
108            return jPanelCenter;
109        }
110    
111        /**
112         * Method getTimePanel.
113         * <br><b>Summary:</b><br>
114         * Return a penl to define a time, that use a slider, and a textField.
115         * @param minMax    MIN or MAX.
116         * @return  (<b>JPanel</b>)   A JPanel.
117         */
118        private JPanel getJPanelTime(int minMax) {
119            if (jPanelTime[minMax] == null) {
120                jLabelTime[minMax] = new JLabel();
121                jLabelTime[minMax].setText(SearsResourceBundle.getResource("normalizeDuration_"+MIN_MAX_SUFFIXES[minMax]+"_label"));
122                jPanelTime[minMax] = new JPanel();
123                jPanelTime[minMax].setLayout(new GridBagLayout());
124                GridBagConstraints gbc1 = new GridBagConstraints();
125                gbc1.gridx = 0;
126                gbc1.gridy = 0;
127                jPanelTime[minMax].add(jLabelTime[minMax], gbc1);
128                GridBagConstraints gbc2 = new GridBagConstraints();
129                gbc2.gridx = 1;
130                gbc2.gridy = 0;
131                gbc2.fill = GridBagConstraints.HORIZONTAL;
132                gbc2.weightx = 1;
133                jPanelTime[minMax].add(getJTextFieldTime(minMax), gbc2);
134                GridBagConstraints gbc3 = new GridBagConstraints();
135                gbc3.gridx = 0;
136                gbc3.gridy = 1;
137                gbc3.gridwidth = 2;
138                gbc3.fill = GridBagConstraints.HORIZONTAL;
139                gbc3.weightx = 1;
140                jPanelTime[minMax].add(getJSliderTime(minMax), gbc3);
141            }
142            return jPanelTime[minMax];
143        }
144        
145            
146            /**
147             * Method getJSliderTime.
148             * <br><b>Summary:</b><br>
149             * return the slider that correspond to the given min or max.
150             * @param minMax        MIN or MAX.
151             * @return  (<b>JSlider</b>)   A JSlider.
152             */
153            private JSlider getJSliderTime(int minMax) {
154                    if(jSliderTime[minMax] == null){
155                            jSliderTime[minMax] = new JSlider(0, 100);
156                            jSliderTime[minMax].setValue((int) Double.parseDouble(DEFAULT_MIN_MAX[minMax])*10);
157                            final int minMaxFinal = minMax;
158                            jSliderTime[minMax].addChangeListener(new ChangeListener() {
159                    public void stateChanged(ChangeEvent e) {
160                        valueChanged(e.getSource(), minMaxFinal);
161                    }
162                });
163            }
164            return jSliderTime[minMax];
165            }
166    
167            /**
168             * Method valueChanged.
169             * <br><b>Summary:</b><br>
170             * this method is called when a slider changed or value.
171             * @param source                The slider that generates the event.
172             * @param minMax                To know if it is min or max that changed.
173             */
174            private void valueChanged(Object source, int minMax) {
175            //If the slider triggered the change, update the textField (if necessary).
176            if (source instanceof JSlider) {
177                //by default we will not update the textField value.
178                boolean updateTextField = false;
179                //retrieve the slider delay value
180                double sliderDelayValue = ((double) jSliderTime[minMax].getValue()) / 10;
181                updateTextField = true;
182                try {
183                    //  retrieve the TextField value.
184                    double delay = getDoubleTextDelayValue(minMax);
185                    if (delay != sliderDelayValue) {
186                        updateTextField = true;
187                    }
188                } catch (NumberFormatException e) {
189                    //TextField value is not valid : update !
190                    updateTextField = true;
191                }
192                //update textField if necessary
193                if (updateTextField) {
194                    jTextFieldTime[minMax].setText("" + sliderDelayValue);
195                }
196            } else if (source instanceof JTextField) {
197                //If the textDelay triggered the event, update the slider if necessary.
198                //Source is the TextDelay
199                try {
200                    //  retrieve the TextField value.
201                    double delay = getDoubleTextDelayValue(minMax);
202                    double sliderDelayValue = ((double) jSliderTime[minMax].getValue()) / 10;
203                    if (delay != sliderDelayValue) {
204                        // Udate slider.
205                        //Beware if textField value is bigger than slider max
206                        int newSliderValue =  (int) (delay * 10);
207                        if(Math.abs(newSliderValue) > jSliderTime[minMax].getMaximum()){
208                            jSliderTime[minMax].setMaximum(Math.abs(newSliderValue));
209                        }
210                        jSliderTime[minMax].setValue(newSliderValue);
211                    }
212                } catch (NumberFormatException e) {
213                    //TextField value is not valid : do not update slider!
214                }
215            }
216                    
217            }
218            
219        /**
220         * Method getDoubleTextDelayValue.
221         * <br><b>Summary:</b><br>
222         * This method returns the double value of the delay in the textfield for given minMax
223         * @param minMax    To select the MIN or the MAX
224         * @return double   The double value of the delay in the textfield.
225         * @throws NumberFormatException
226         */
227        public double getDoubleTextDelayValue(int minMax) throws NumberFormatException{
228            //the result of the method.
229            double result = 0;
230            String delayString = jTextFieldTime[minMax].getText();
231            if (delayString != null && !delayString.equals("")) {
232                    result = Double.parseDouble(delayString);
233            }else{
234                result = 0;
235            }
236            //return the result;
237            return result;
238        }
239            
240            
241            /**
242             * Method getJTextFieldTime.
243             * <br><b>Summary:</b><br>
244             * return the textField that correspond to the given min or max.
245             * @param minMax        MIN or MAX
246             * @return  (<b>JTextField</b>)   A JTextField.
247             */
248            private JTextField getJTextFieldTime(int minMax) {
249                    if (jTextFieldTime[minMax] == null) {
250                            jTextFieldTime[minMax] = new JTextField(5);
251                            jTextFieldTime[minMax].setToolTipText(SearsResourceBundle.getResource("delay_tip"));
252                            jTextFieldTime[minMax].setText(DEFAULT_MIN_MAX[minMax]);
253                //add an action listener to validate when user press 'enter' in textField.
254                            jTextFieldTime[minMax].addActionListener(new ActionListener() {
255                    public void actionPerformed(ActionEvent e) {
256                        okAction();
257                    }
258                });
259                //Add a focus listener, to update slider when textfield lost focus.
260                            final int minMaxFinal = minMax;
261                            jTextFieldTime[minMax].addFocusListener(new FocusListener() {
262                    public void focusLost(FocusEvent e) {
263                        valueChanged(e.getSource(), minMaxFinal);
264                    }
265                
266                    public void focusGained(FocusEvent e) {
267                        //Nothing to do on focus gain
268                    }
269                });
270            }
271            return jTextFieldTime[minMax];
272            }
273            
274             /**
275         * Method okAction.
276         * <br><b>Summary:</b><br>
277         * This method is called when user validate the dialog.
278         */
279        protected void okAction() {
280            //Just have to check parameters validity, if valids, dispose dialog,and set validation status to true.
281            String error = checkParameters();
282            if (error != null && !error.equals("")) {
283                //Show error message.
284                JOptionPane.showMessageDialog(this, error, SearsResourceBundle.getResource("error_delayConfigurationError"), JOptionPane.ERROR_MESSAGE);
285                Trace.trace("Error in Delay parameters:" + error, Trace.WARNING_PRIORITY);
286            } else {
287                //else split configuration is valid, release dialog.
288                validationStatus = true;
289                dispose();
290            }
291        }
292        
293        /**
294         * Method checkParameters.
295         * <br><b>Summary:</b><br>
296         * This method checks the parameters.
297         * @return  <b>String</b>      "" if there is no error, or the error message.
298         */
299        private String checkParameters() {
300            //Check file to open
301            String errorMessage = "";
302            //check time for min and max.
303            for(int i= 0; i < NB_TIMES;i++ ){
304                    String delay = jTextFieldTime[i].getText();
305                    if (delay != null && !delay.equals("")) {
306                        try {
307                            Double.parseDouble(delay);
308                        } catch (NumberFormatException e) {
309                            //Delay value is not a number.
310                            errorMessage += "["+MIN_MAX_SUFFIXES[i]+"]"+SearsResourceBundle.getResource("error_delayNotValid")+"\n";
311                        }
312                    } else {
313                        //Delay value is null.
314                        errorMessage += "["+MIN_MAX_SUFFIXES[i]+"]"+SearsResourceBundle.getResource("error_delayNull")+"\n";
315                    }
316            }
317            return errorMessage;
318        }
319    
320        /**
321         * Method getTimes.
322         * <br><b>Summary:</b><br>
323         * return the times configured by the user.
324         * chack that user validates the dialog.
325         * @return  (<b>double[]</b>)   The result of the normalize dialog, double[]=[MIN, MAX].
326         */
327        public double[] getTimes(){
328            //The result of the method.
329            double[] result = new double[NB_TIMES];
330            for(int i= 0; i<NB_TIMES;i++){
331                    result[i] = Double.parseDouble(jTextFieldTime[i].getText());
332            }
333            //return the result.
334            return result;
335        }
336        
337            /* (non-Javadoc)
338             * @see sears.gui.SearsJDialog#getDialogName()
339             */
340            protected String getDialogName() {
341                    return "normalizeDuration";
342            }
343    
344    }