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.Dimension;
021    import java.awt.GridLayout;
022    import java.awt.Point;
023    import java.awt.Toolkit;
024    import java.awt.Window;
025    import java.awt.datatransfer.DataFlavor;
026    import java.awt.datatransfer.Transferable;
027    import java.awt.datatransfer.UnsupportedFlavorException;
028    import java.awt.event.ActionEvent;
029    import java.awt.event.ActionListener;
030    import java.awt.event.MouseEvent;
031    import java.awt.event.MouseListener;
032    import java.awt.event.WindowEvent;
033    import java.awt.event.WindowListener;
034    import java.io.File;
035    import java.io.IOException;
036    import java.util.ArrayList;
037    import java.util.Date;
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Locale;
041    import java.util.StringTokenizer;
042    
043    import javax.swing.ImageIcon;
044    import javax.swing.JButton;
045    import javax.swing.JComponent;
046    import javax.swing.JFileChooser;
047    import javax.swing.JFrame;
048    import javax.swing.JMenu;
049    import javax.swing.JMenuBar;
050    import javax.swing.JMenuItem;
051    import javax.swing.JOptionPane;
052    import javax.swing.JPanel;
053    import javax.swing.JPopupMenu;
054    import javax.swing.JScrollPane;
055    import javax.swing.JSlider;
056    import javax.swing.JTable;
057    import javax.swing.JToolBar;
058    import javax.swing.SwingUtilities;
059    import javax.swing.TransferHandler;
060    import javax.swing.UIManager;
061    import javax.swing.UnsupportedLookAndFeelException;
062    import javax.swing.event.ChangeEvent;
063    import javax.swing.event.ChangeListener;
064    import javax.swing.filechooser.FileFilter;
065    import javax.swing.table.TableColumn;
066    
067    import sears.Version;
068    import sears.file.SrtFile;
069    import sears.file.Subtitle;
070    import sears.file.SubtitleFile;
071    import sears.gui.resources.SearsResources;
072    import sears.tools.SearsAction;
073    import sears.tools.SearsProperties;
074    import sears.tools.SearsResourceBundle;
075    import sears.tools.Trace;
076    import sears.tools.Updater;
077    import sears.tools.Utils;
078    import sears.tools.player.DefaultPlayer;
079    import sears.tools.player.PlayerException;
080    import sears.tools.player.PlayerInterface;
081    import sears.tools.player.VLCPlayer;
082    
083    /**
084     * Class MainWindow.
085     * <br><b>Summary:</b><br>
086     * This is the main Interface of the sears project.
087     * It contains a JTable to displays the subtitles, and menus to interact and launch actions.
088     */
089    public class MainWindow extends JFrame implements MouseListener, WindowListener {
090        private static final long serialVersionUID = -6575979899788187615L;
091    
092        /** The instance of the mainWindow */
093        public static MainWindow instance;
094    
095        /**The menuBar*/
096        private JMenuBar jMenuBar;
097    
098        /**The subtitle file that is currently opened by Sears*/
099        private SubtitleFile subtitleFile;
100    
101        /**The main panel.*/
102        private JPanel mainPanel;
103    
104        /**The JTable that contains the subtitles.*/
105        private JTable table;
106    
107        /**The JTableModel that is used by the JTable.*/
108        private SubtitleTableModel tableModel;
109    
110        /**The list of subtitles actually in the subtitle file opened*/
111        private ArrayList<Subtitle> subtitleList;
112    
113        /**The application icon*/
114        private ImageIcon searsIcon;
115    
116        /**The Actions hashMap where key is (String) actionId and Value is corresponding SearsAction.*/
117        private HashMap<String, SearsAction> actions;
118    
119        /**The JToolBar*/
120        private JToolBar jToolBar;
121    
122        /**The JPopupMenu*/
123        private JPopupMenu jPopupMenu;
124    
125        /**The Player*/
126        protected PlayerInterface player = null;
127    
128        /**The JPanelVideoControler that controls the video.*/
129        private JPanelVideoControler jPanelVideoControler;
130    
131        /**The full path to the player executable.*/
132        private static String playerVideoFile = null;
133        
134        /** (<b>ArrayList<File></b>) recentFiles: The recentFiles */
135        private ArrayList<File> recentFiles = null;
136        
137        /** (<b>JMenu</b>) recentFileMenu: The recentFileMenu */
138        private JMenu recentFileMenu;
139    
140        /** (<b>int</b>) RECENT_FILE_NUMBER: The max number of recent files to be displayed.*/
141        private static final int RECENT_FILE_NUMBER = 10;
142        
143        /**The Actions Keys*/
144        /**Open action key, to open a subtitle file*/
145        protected static String ACTION_KEY_OPEN = "open";
146    
147        /**Save action key, to save current file.*/
148        protected static String ACTION_KEY_SAVE = "save";
149    
150        /**SaveAs action key, to save current file.*/
151        protected static String ACTION_KEY_SAVE_AS = "saveAs";
152    
153        /**Quit action key, to save current file.*/
154        protected static String ACTION_KEY_QUIT = "quit";
155    
156        /**Delay action key, to delay ST.*/
157        protected static String ACTION_KEY_DELAY = "delay";
158    
159        /**Resynch action key, to resynch ST.*/
160        protected static String ACTION_KEY_RESYNCH = "resynch";
161    
162        /**Split action key, to split ST.*/
163        protected static String ACTION_KEY_SPLIT = "split";
164    
165        /**Append action key, to append ST.*/
166        protected static String ACTION_KEY_APPEND = "append";
167    
168        /**Accent action key, to Accent repair.*/
169        protected static String ACTION_KEY_ACCENT_REPAIR = "accentRepair";
170    
171        /**Chain action key, to chain repair.*/
172        protected static String ACTION_KEY_CHAIN_REPAIR = "chainRepair";
173    
174        /**HTML action key, to html repair.*/
175        protected static String ACTION_KEY_HTML_REPAIR = "htmlRepair";
176    
177        /**Order action key, to order repair.*/
178        protected static String ACTION_KEY_ORDER_REPAIR = "orderRepair";
179    
180        /**Time action key, to time repair.*/
181        protected static String ACTION_KEY_TIME_REPAIR = "timeRepair";
182        
183        /**NormalizeDuration action key, to time repair.*/
184        protected static String ACTION_KEY_NORMALIZE_DURATION = "normalizeDuration";
185    
186        /**Select video action key.*/
187        protected static String ACTION_KEY_SELECT_VIDEO = "selectVideo";
188    
189        /**Options action key.*/
190        protected static String ACTION_KEY_OPTIONS = "options";
191    
192        /**Play action key.*/
193        protected static String ACTION_KEY_PLAY = "play";
194    
195        /**Stop action key.*/
196        protected static String ACTION_KEY_STOP = "stop";
197    
198        /**Pause action key.*/
199        protected static String ACTION_KEY_PAUSE = "pause";
200    
201        /**Previous action key.*/
202        protected static String ACTION_KEY_PREVIOUS = "previous";
203    
204        /**Next action key.*/
205        protected static String ACTION_KEY_NEXT = "next";
206    
207        /**TrackTrigger action key.*/
208        protected static String ACTION_KEY_TRACKER_TRIGGER = "trackTrigger";
209        
210        /**gotoSub action key.*/
211        protected static String ACTION_KEY_GOTO_SUB = "gotoSub";
212    
213        /**magicResynchro action key.*/
214        protected static String ACTION_KEY_MAGIC_RESYNCHRO = "magicResynchro";
215        
216        /**remoaveAllAnchors action key.*/
217        protected static String ACTION_KEY_REMOVE_ALL_ANCHORS = "removeAllAnchors";
218        
219        /**about action key.*/
220            protected static String ACTION_KEY_ABOUT = "about";
221        
222        /** (<b>String[]</b>) FILE_OPEN_DEPENDANT_ACTIONS: The Actions that depend on a file open status*/
223        private static final String[] FILE_OPEN_DEPENDANT_ACTIONS = new String[] {
224                              ACTION_KEY_ACCENT_REPAIR
225                            , ACTION_KEY_APPEND
226                            , ACTION_KEY_CHAIN_REPAIR
227                            , ACTION_KEY_DELAY
228                            , ACTION_KEY_HTML_REPAIR
229                            , ACTION_KEY_MAGIC_RESYNCHRO
230                            , ACTION_KEY_NORMALIZE_DURATION
231                            , ACTION_KEY_ORDER_REPAIR
232                            , ACTION_KEY_RESYNCH, ACTION_KEY_SAVE
233                            , ACTION_KEY_SAVE_AS
234                            , ACTION_KEY_SPLIT
235                            , ACTION_KEY_TIME_REPAIR };
236    
237        /** (<b>String[]</b>) FILE_CHANGED_DEPENDANT_ACTIONS: The actions that depend on a file change status*/
238        private static final String[] FILE_CHANGED_DEPENDANT_ACTIONS = new String[]{
239            ACTION_KEY_SAVE};
240        
241            /**
242         * 
243         * Constructor MainWindow.
244         * <br><b>Summary:</b><br>
245         * Constructor of the class.
246         * construct the GUI.
247         */
248        public MainWindow() {
249            super();
250            instance = this;
251            //restore recent files
252            recentFiles = new ArrayList<File>();
253            String recentFileProperty = SearsProperties.getProperty(SearsProperties.RECENT_FILES);
254            StringTokenizer stk = new StringTokenizer(recentFileProperty, ";");
255            while(stk.hasMoreTokens()){
256                    String recentFile = stk.nextToken();
257                    recentFiles.add(new File(recentFile));
258            }
259            //try to get the locale from properties
260            String lang = SearsProperties.getProperty(SearsProperties.LANGUAGE, "en");
261            String count = SearsProperties.getProperty(SearsProperties.COUNTRY, "US");
262            Locale locale = new Locale(lang, count);
263            new SearsResourceBundle();
264            setSelectedLocale(locale);
265            //set the look and feel on the current OS.
266            try {
267                String selectedLookAndFeel = SearsProperties.getProperty(SearsProperties.LOOK_AND_FEEL, UIManager
268                        .getSystemLookAndFeelClassName());
269                UIManager.setLookAndFeel(selectedLookAndFeel);
270                //SwingUtilities.updateComponentTreeUI(instance);
271                SearsProperties.setProperty(SearsProperties.LOOK_AND_FEEL, selectedLookAndFeel);
272                //MainWindow.instance.repaint();
273            } catch (ClassNotFoundException e1) {
274                // TODO Auto-generated catch block
275                e1.printStackTrace();
276            } catch (InstantiationException e1) {
277                // TODO Auto-generated catch block
278                e1.printStackTrace();
279            } catch (IllegalAccessException e1) {
280                // TODO Auto-generated catch block
281                e1.printStackTrace();
282            } catch (UnsupportedLookAndFeelException e1) {
283                // TODO Auto-generated catch block
284                e1.printStackTrace();
285            }
286            //Set the size.
287            int width = Integer.parseInt(SearsProperties.getProperty("MainWindow" + SearsProperties.SUFFIX_WIDTH, "600"));
288            int heigth = Integer.parseInt(SearsProperties.getProperty("MainWindow" + SearsProperties.SUFFIX_HEIGTH, "400"));
289            setSize(width, heigth);
290            searsIcon = SearsResources.getIcon("SearsGUIIcon");
291            setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
292            //Set the frame's icon
293            setIconImage(searsIcon.getImage());
294            updateWindowsName();
295            centerFrame(instance);
296            addWindowListener(this);
297            //Construct the actions.
298            constructActions();
299            mainPanel = new JPanel();
300            mainPanel.setLayout(new GridLayout(1, 1));
301            getContentPane().setLayout(new BorderLayout());
302            getContentPane().add(mainPanel, BorderLayout.CENTER);
303            constructJMenuBar();
304            setJMenuBar(jMenuBar);
305            constructJToolBar();
306            getContentPane().add(jToolBar, BorderLayout.NORTH);
307            getContentPane().add(getJPanelVideoControler(), BorderLayout.SOUTH);
308            subtitleList = new ArrayList<Subtitle>();
309            tableModel = new SubtitleTableModel(subtitleList);
310            table = new JTable(tableModel);
311            table.setRowSelectionAllowed(true);
312            table.addMouseListener(this);
313            //  Add the Transfer handler, which will handle the files Drag and drops.
314            SubtitleFileTransfertHandler searsTransferHandler = new SubtitleFileTransfertHandler();
315            getRootPane().setTransferHandler(searsTransferHandler);
316            table.setTransferHandler(searsTransferHandler);
317            //create table renderer.
318            //setting the table column sizes and renderer.
319            TableColumn column = null;
320            for (int i = 0; i < SubtitleTableModel.COLUMN_NUMBER; i++) {
321                column = table.getColumnModel().getColumn(i);
322                column.setCellRenderer(new SubtitleTableCellRenderer(subtitleList));
323                column.setHeaderRenderer(new SubtitleAnchorHeaderTableCellRenderer(
324                                            table.getTableHeader().getDefaultRenderer()));
325                switch (i) {
326                            case SubtitleTableModel.ANCHOR_COLUMN: {
327                                    column.setPreferredWidth(18);
328                                    column.setMaxWidth(18);
329                                    break;
330                            }
331                            case SubtitleTableModel.ID_COLUMN: {
332                                    column.setPreferredWidth(40);
333                                    column.setMaxWidth(60);
334                                    break;
335                            }
336                            case SubtitleTableModel.START_DATE_COLUMN: {
337                                    column.setPreferredWidth(100);
338                                    column.setMaxWidth(100);
339                                    break;
340                            }
341                            case SubtitleTableModel.END_DATE_COLUMN: {
342                                    column.setPreferredWidth(100);
343                                    column.setMaxWidth(100);
344                                    break;
345                            }
346                            }
347            }
348            mainPanel.add(new JScrollPane(table));
349            
350            //
351            // Create the thread of video time handler
352            PlayerTimeHandler playerTimeHandler = new PlayerTimeHandler();
353            Thread playerTimeHandlerThread = new Thread(playerTimeHandler, "playerTimeHandler");
354            playerTimeHandlerThread.start();
355            jPanelVideoControler.setJSliderSearchListener(playerTimeHandler);
356            
357            lookForUpdate();
358        }
359    
360        /**
361         * Method getJPanelVideoControler.
362         * <br><b>Summary:</b><br>
363         * Return the JPanel that control the video.
364         * @return The JPanel video controler.
365         */
366        private JPanel getJPanelVideoControler() {
367            if (jPanelVideoControler == null) {
368                jPanelVideoControler = new JPanelVideoControler();
369            }
370            return jPanelVideoControler;
371        }
372    
373        /**
374         * Method lookForUpdate.
375         * <br><b>Summary:</b><br>
376         * This method look for an update, if the SearsProperties.UPDATE_ADDRESS is not equals to "".
377         * and will display a message to the user if a new Sears version is available.
378         */
379        private void lookForUpdate() {
380            //If an address update has been set, then update.
381            String updateAddress = SearsProperties.getProperty(SearsProperties.UPDATE_ADDRESS, "firstLaunch");
382            if (!updateAddress.equals("")) {
383                //If it is the first launch
384                if (updateAddress.equals("firstLaunch")) {
385                    setUpdateAddress(Updater.DEFAULT_UPDATE_ADDRESS);
386                    updateAddress = Updater.DEFAULT_UPDATE_ADDRESS;
387                }
388                //look for update.
389                new Updater(this, updateAddress, Double.parseDouble(Version.VERSION));
390            }
391        }
392    
393        /**
394         * Method setUpdateAddress.
395         * <br><b>Summary:</b><br>
396         * This method set the new update address.
397         * @param address   The new update address to set.
398         */
399        private void setUpdateAddress(String address) {
400            SearsProperties.setProperty(SearsProperties.UPDATE_ADDRESS, address);
401        }
402    
403        /**
404         * Method constructActions.
405         * <br><b>Summary:</b><br>
406         * This method construct the actions.
407         */
408        private void constructActions() {
409            actions = new HashMap<String, SearsAction>();
410            //construct Open action
411            actions.put(ACTION_KEY_OPEN, new OpenAction());
412            //construct Save action
413            actions.put(ACTION_KEY_SAVE, new SaveAction());
414            //construct SaveAs action
415            actions.put(ACTION_KEY_SAVE_AS, new SaveAsAction());
416            //construct quit action
417            actions.put(ACTION_KEY_QUIT, new QuitAction());
418            //construct Delay action
419            actions.put(ACTION_KEY_DELAY, new DelayAction());
420            //construct Resynch action
421            actions.put(ACTION_KEY_RESYNCH, new ResynchAction());
422            //construct Split action
423            actions.put(ACTION_KEY_SPLIT, new SplitAction());
424            //construct Append action
425            actions.put(ACTION_KEY_APPEND, new AppendAction());
426            //construct AccentRepairAction
427            actions.put(ACTION_KEY_ACCENT_REPAIR, new AccentRepairAction());
428            //construct ChainRepairAction
429            actions.put(ACTION_KEY_CHAIN_REPAIR, new ChainRepairAction());
430            //construct HtmlRepairAction
431            actions.put(ACTION_KEY_HTML_REPAIR, new HtmlRepairAction());
432            //construct OrderRepairAction
433            actions.put(ACTION_KEY_ORDER_REPAIR, new OrderRepairAction());
434            //construct TimeRepairAction
435            actions.put(ACTION_KEY_TIME_REPAIR, new TimeRepairAction());
436            //construct NormalizeDurationAction
437            actions.put(ACTION_KEY_NORMALIZE_DURATION, new NormalizeDurationAction());
438            //construct SelectVideoAction
439            actions.put(ACTION_KEY_SELECT_VIDEO, new SelectVideoAction());
440            //construct optionsAction
441            actions.put(ACTION_KEY_OPTIONS, new OptionsAction());
442            //construct playAction
443            actions.put(ACTION_KEY_PLAY, new PlayAction());
444            //construct stopAction
445            actions.put(ACTION_KEY_STOP, new StopAction());
446            //construct pauseAction
447            actions.put(ACTION_KEY_PAUSE, new PauseAction());
448            //construct previousAction
449            actions.put(ACTION_KEY_PREVIOUS, new PreviousAction());
450            //construct nextAction
451            actions.put(ACTION_KEY_NEXT, new NextAction());
452            //construct trackerTriggerAction
453            actions.put(ACTION_KEY_TRACKER_TRIGGER, new TrackerTriggerAction());
454            //construct gotoSubAction
455            actions.put(ACTION_KEY_GOTO_SUB, new GotoSubAction());
456            //construct magicResynchroAction
457            actions.put(ACTION_KEY_MAGIC_RESYNCHRO, new MagicResynchroAction());
458            //Construct removeAllAnchorsAction
459            actions.put(ACTION_KEY_REMOVE_ALL_ANCHORS, new RemoveAllAnchorsAction());
460            //Construct aboutAction
461                    actions.put(ACTION_KEY_ABOUT, new AboutAction());
462            //update the actions status
463            updateActionStatus();
464        }
465    
466        /**
467         * Method constructJMenuBar.
468         * <br><b>Summary:</b><br>
469         * This method construct the JMenuBar
470         */
471        private void constructJMenuBar() {
472            jMenuBar = new JMenuBar();
473            /////////////
474            //FILE MENU
475            /////////////
476            JMenu jMenuFile = new JMenu(SearsResourceBundle.getResource("menuBar_file"));
477            jMenuBar.add(jMenuFile);
478            //OPEN ITEM
479            jMenuFile.add(new JMenuItem(getAction(ACTION_KEY_OPEN)));
480            //OPEN RECENT FILE
481            recentFileMenu = new JMenu(SearsResourceBundle.getResource("RecentFileName"));
482            recentFileMenu.setToolTipText(SearsResourceBundle.getResource("RecentFileTip"));
483            recentFileMenu.setIcon(SearsResources.getIcon("RecentFileIcon"));
484            jMenuFile.add(recentFileMenu);
485            updateRecentFileMenu();
486            //SAVE ITEM
487            jMenuFile.add(new JMenuItem(getAction(ACTION_KEY_SAVE)));
488            //SAVE AS ITEM
489            jMenuFile.add(new JMenuItem(getAction(ACTION_KEY_SAVE_AS)));
490            //QUIT ITEM
491            //if platform isn't a Mac platform:
492            if(System.getProperty("mrj.version") == null){
493                    jMenuFile.add(new JMenuItem(getAction(ACTION_KEY_QUIT)));
494            }
495            //////////////////
496            //ACTION MENU
497            /////////////////
498            JMenu jMenuAction = new JMenu(SearsResourceBundle.getResource("menuBar_action"));
499            jMenuBar.add(jMenuAction);
500            //DELAY ITEM
501            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_DELAY)));
502            //RESYNCHRO ITEM
503            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_RESYNCH)));
504            //MAGIC RESYNCHRO ITEM
505            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_MAGIC_RESYNCHRO)));
506            //REMOVE ALL ANCHORS ITEM
507            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_REMOVE_ALL_ANCHORS)));
508            //Add a separator
509            jMenuAction.addSeparator();
510            //SPLIT FILE ITEM
511            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_SPLIT)));
512            //SPLIT FILE ITEM
513            jMenuAction.add(new JMenuItem(getAction(ACTION_KEY_APPEND)));
514            //////////////////
515            //REPAIR MENU
516            /////////////////
517            JMenu jMenuRepair = new JMenu(SearsResourceBundle.getResource("menuBar_repair"));
518            jMenuBar.add(jMenuRepair);
519            //CHAIN REPAIR
520            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_CHAIN_REPAIR)));
521            //Add a separator
522            jMenuRepair.addSeparator();
523            //ACCENT REPAIR
524            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_ACCENT_REPAIR)));
525            //HTML REPAIR
526            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_HTML_REPAIR)));
527            //Add a separator
528            jMenuRepair.addSeparator();
529            //ORDER REPAIR
530            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_ORDER_REPAIR)));
531            //TIME REPAIR
532            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_TIME_REPAIR)));
533            //TIME REPAIR
534            jMenuRepair.add(new JMenuItem(getAction(ACTION_KEY_NORMALIZE_DURATION)));
535            //////////////////
536            //PLAYER MENU
537            /////////////////
538            JMenu jMenuPlayer = new JMenu(SearsResourceBundle.getResource("menuBar_player"));
539            jMenuBar.add(jMenuPlayer);
540            //SELECT VIDEO
541            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_SELECT_VIDEO)));
542            //LAUNCH PLAYER
543            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_PLAY)));
544            //PAUSE PLAYER
545            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_PAUSE)));
546            //STOP PLAYER
547            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_STOP)));
548            //PREVIOUS PLAYER
549            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_PREVIOUS)));
550            //NEXT PLAYER
551            jMenuPlayer.add(new JMenuItem(getAction(ACTION_KEY_NEXT)));
552            //////////////////
553            //OPTIONS MENU
554            /////////////////
555            JMenu jMenuOptions = new JMenu(SearsResourceBundle.getResource("menuBar_options"));
556            jMenuBar.add(jMenuOptions);
557            //OPTIONS
558            jMenuOptions.add(new JMenuItem(getAction(ACTION_KEY_OPTIONS)));
559            //////////////////
560            //HELP MENU
561            /////////////////
562            JMenu helpMenu = new JMenu(SearsResourceBundle.getResource("menuBar_help"));
563            jMenuBar.add(helpMenu);
564            JMenuItem helpItem = new JMenuItem(SearsResourceBundle.getResource("menuBar_help"));
565            helpItem.setIcon(SearsResources.getIcon("HelpIcon"));
566            helpItem.addActionListener(new ActionListener() {
567                public void actionPerformed(ActionEvent arg0) {
568                    //default title and icon
569                    JOptionPane
570                            .showMessageDialog(
571                                    instance,
572                                    "You can find help on Sears\n by going to sears sourceforge page:\nhttp://sears.sourceforge.net/",
573                                    "Sears Help", JOptionPane.INFORMATION_MESSAGE, searsIcon);
574                }
575            });
576            helpMenu.add(helpItem);
577            helpMenu.add(new JMenuItem(getAction(ACTION_KEY_ABOUT)));
578        }
579    
580        /**
581         * Method constructJMenuBar.
582         * <br><b>Summary:</b><br>
583         * This method construct the JMenuBar
584         */
585        private void constructJToolBar() {
586            jToolBar = new JToolBar();
587            /////////////
588            //FILE TOOLS
589            /////////////
590            //OPEN ITEM
591            JButton button = new JButton(getAction(ACTION_KEY_OPEN));
592            button.setText(null);
593            jToolBar.add(button);
594            //SAVE  ITEM
595            button = new JButton(getAction(ACTION_KEY_SAVE));
596            button.setText(null);
597            jToolBar.add(button);
598            //add separator
599            jToolBar.addSeparator();
600            //////////////////
601            //ACTION MENU
602            /////////////////
603            //DELAY ITEM
604            button = new JButton(getAction(ACTION_KEY_DELAY));
605            button.setText(null);
606            jToolBar.add(button);
607            //MAGIC RESYNCHRO ITEM
608            button = new JButton(getAction(ACTION_KEY_MAGIC_RESYNCHRO));
609            button.setText(null);
610            jToolBar.add(button);
611            //REMOVE ALL ANCHORS ITEM
612            button = new JButton(getAction(ACTION_KEY_REMOVE_ALL_ANCHORS));
613            button.setText(null);
614            jToolBar.add(button);
615            //SPLIT ITEM
616            button = new JButton(getAction(ACTION_KEY_SPLIT));
617            button.setText(null);
618            jToolBar.add(button);
619            //APPEND ITEM
620            button = new JButton(getAction(ACTION_KEY_APPEND));
621            button.setText(null);
622            jToolBar.add(button);
623            //add separator
624            jToolBar.addSeparator();
625            //////////////////
626            //REPAIR MENU
627            /////////////////
628            //CHAIN REPAIR ITEM
629            button = new JButton(getAction(ACTION_KEY_CHAIN_REPAIR));
630            button.setText(null);
631            jToolBar.add(button);
632            
633            //ACCENT REPAIR ITEM
634            button = new JButton(getAction(ACTION_KEY_ACCENT_REPAIR));
635            button.setText(null);
636            jToolBar.add(button);
637            //HTML REPAIR ITEM
638            button = new JButton(getAction(ACTION_KEY_HTML_REPAIR));
639            button.setText(null);
640            jToolBar.add(button);
641            jToolBar.addSeparator();
642            //////////////////
643            //TOOLS MENU
644            /////////////////
645            //OPTIONS BUTTON
646            button = new JButton(getAction(ACTION_KEY_OPTIONS));
647            button.setText(null);
648            jToolBar.add(button);
649        }
650    
651        /**
652         * Method getAction.
653         * <br><b>Summary:</b><br>
654         * Return the SearsActions that correspond to the given key.
655         * @param   key             The <b>String</b> Action key to search for.
656         * @return  SearsAction     The <b>SearsAction</b> that correspond to the key, or null if not found.
657         */
658        protected SearsAction getAction(String key) {
659            //Search and return the action in the actions map.
660            return (SearsAction) actions.get(key);
661        }
662    
663        /**
664         * Method delayAction.
665         * <br><b>Summary:</b><br>
666         * Use this method to set a delay on subtitles.
667         * It will show a dialog to enter a delay to the selected subtitles.
668         */
669        protected void delayAction() {
670            //Must have opened a subtitle file to apply a delay.
671            if (subtitleFile != null) {
672                //get the selected rows to delay.
673                int[] indexToDelay = table.getSelectedRows();
674                //ask the user for delay to apply
675                DelayDialog dd = new DelayDialog();
676                dd.setVisible(true);
677                //if user choose a delay.
678                if (dd.hasBeenValidated()) {
679                    double delay = dd.getDelay();
680                    //delay the subtitle with the given delay.
681                    //multiply by 1000, because delay is indicated in second, with possible milliseconds.
682                    subtitleFile.delay(indexToDelay, (int) (delay * 1000));
683                    //indicate table that data have changed
684                    tableModel.fireTableDataChanged();
685                }
686                updateWindowsName();
687                updateActionStatus();
688            } else {
689                //Must have opened a subtitle file to apply a delay.
690                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeDelay"), Trace.WARNING_PRIORITY);
691            }
692        }
693    
694        /**
695         * Method resynchroAction.
696         * <br><b>Summary:</b><br>
697         * Use this method to resynchro subtitles.
698         * If user has selected the Two source point, the dialog shown will be pre-filled with good values.
699         */
700        protected void resynchroAction() {
701            //Must have opened a subtitle file to apply a resynchro.
702            if (subtitleFile != null) {
703                //check wether user choose two source subtitles.
704                int[] selectedIndex = table.getSelectedRows();
705                ResynchroDialog rd;
706                //If user choose sources, use them in shown dialog.
707                if (selectedIndex.length > 1) {
708                    //Keep only 2 subtitles. interpolation of level n will come after :)
709                    int[] selectedIndex2 = new int[2];
710                    selectedIndex2[0] = selectedIndex[0];
711                    selectedIndex2[1] = selectedIndex[1];
712                    //TODO: create n-level interpolation in SubtitleFile!
713                    //construct the dialog.
714                    rd = new ResynchroDialog(subtitleList, selectedIndex2);
715                } else {
716                    //If no subtitles has been selected, construct empty resynchro dialog.
717                    rd = new ResynchroDialog(subtitleList);
718                }
719                //show the constructed dialog.
720                rd.setVisible(true);
721                //check user's response.
722                if (rd.hasBeenValidated()) {
723                    //if user has validated the dialog, get the result.
724                    int[] result = rd.getResult();
725                    //and resynchro the subtitles.
726                    subtitleFile.resynchro(result);
727                    //indicate the table that data have changed.
728                    tableModel.fireTableDataChanged();
729                }
730                updateWindowsName();
731                updateActionStatus();
732            } else {
733                //Must have opened a subtitle file to apply a resynchro.
734                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeResynch"), Trace.WARNING_PRIORITY);
735            }
736        }
737        
738        /**
739         * Method magicResynchroAction.
740         * <br><b>Summary:</b><br>
741         * Apply a magic resynchro
742         */
743        protected void magicResynchroAction(){
744            if (subtitleFile != null) {
745                    subtitleFile.magicResynchro();
746                    //After magic resynchro, remove all anchors.
747                    removeAllAnchorsAction();
748                updateWindowsName();
749                updateActionStatus();
750            } else {
751                //Must have opened a subtitle file to apply a resynchro.
752                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeResynch"), Trace.WARNING_PRIORITY);
753            }
754        }
755    
756        /**
757         * Method saveAsAction.
758         * <br><b>Summary:</b><br>
759         * Method called when want to save the subtitle file.
760         * open a JFolderChooser to chooser where to save the file.
761         */
762        protected void saveAsAction() {
763            Trace.trace("SaveAsAction", Trace.ALGO_PRIORITY);
764            JFileChooser fc = null;
765            //If have already opened a subtitle file, choose content directory.
766            if (subtitleFile != null) {
767                fc = new JFileChooser(subtitleFile.getContentDirectory());
768                fc.setSelectedFile(subtitleFile.getFile());
769            } else {
770                fc = new JFileChooser();
771            }
772            fc.setApproveButtonText(SearsResourceBundle.getResource("button_saveAs"));
773            int returnVal = fc.showOpenDialog(instance);
774            fc.setFileFilter(new FileFilter() {
775                public String getDescription() {
776                    return SearsResourceBundle.getResource("various_subtitleFileDescription");
777                }
778    
779                public boolean accept(File f) {
780                    return f.isDirectory() || Utils.getExtension(f).equals("srt");
781                }
782            });
783            if (returnVal == 0) {
784                //if user has choosed a file, save in it.
785                File file = fc.getSelectedFile();
786                Trace.trace(SearsResourceBundle.getResource("various_saveAs") + " " + file.getName() + ".");
787                subtitleFile.writeToFile(file);
788                openFile(file);
789            } else {
790                //User caceled the save action.
791                Trace.trace(SearsResourceBundle.getResource("various_openCommandCanceled"));
792            }
793            updateWindowsName();
794            updateActionStatus();
795        }
796    
797        /**
798         * Method saveAction.
799         * <br><b>Summary:</b><br>
800         * Method called when want to save the subtitle file.
801         */
802        protected void saveAction() {
803            Trace.trace("SaveAction", Trace.ALGO_PRIORITY);
804            if (subtitleFile != null) {
805                subtitleFile.writeToFile(subtitleFile.getFile());
806                updateWindowsName();
807                updateActionStatus();
808            }
809        }
810    
811        /**
812         * Method quitAction.
813         * <br><b>Summary:</b><br>
814         * Use this method to quit Sears.
815         */
816        protected void quitAction() {
817            //check current file before quit.
818            if (checkUnsavedFileBeforeAction()) {
819                Trace.trace(SearsResourceBundle.getResource("various_quittingSears"), Trace.ALGO_PRIORITY);
820                if (player != null) {
821                    player.quit();
822                }
823                saveMainWindowProperties();
824                SearsProperties.saveProperties();
825                System.exit(0);
826            }
827        }
828    
829        /**
830         * Method saveMainWindowProperties.
831         * <br><b>Summary:</b><br>
832         * This method saves the MainWindow properties of Sears.
833         */
834        private void saveMainWindowProperties() {
835            //save windows properties.
836            SearsProperties.setProperty("MainWindow" + SearsProperties.SUFFIX_WIDTH, "" + this.getWidth());
837            SearsProperties.setProperty("MainWindow" + SearsProperties.SUFFIX_HEIGTH, "" + this.getHeight());
838            //save the folder property.
839            if (subtitleFile != null) {
840                SearsProperties.setProperty(SearsProperties.LAST_FOLDER, "" + subtitleFile.getContentDirectory());
841            }
842            //Save the locale.
843            SearsProperties.setProperty(SearsProperties.LANGUAGE, getLocale().getLanguage().toString());
844            SearsProperties.setProperty(SearsProperties.COUNTRY, getLocale().getCountry().toString());
845            //Save the recent files list.
846            String recentFilesProperty = "";
847            for(File file: recentFiles){
848                    recentFilesProperty+=file.getAbsolutePath()+";";
849            }
850            SearsProperties.setProperty(SearsProperties.RECENT_FILES, recentFilesProperty);
851            
852        }
853    
854        /**
855         * Method openAction.
856         * <br><b>Summary:</b><br>
857         * Use this method to open a subtitle file.
858         * Displays a dialog to choose the file to open.
859         */
860        protected void openAction() {
861            Trace.trace("OpenAction", Trace.ALGO_PRIORITY);
862            File file = showSRTBrowser();
863            if (file != null) {
864                //If user has choose a file to open, open it.
865                Trace.trace(SearsResourceBundle.getResource("various_opening") + " " + file.getName() + ".");
866                // Close the video file
867                if (subtitleFile != null) {
868                    playerVideoFile = null;
869                }
870                openFile(file);
871                // Check if the player is running
872                if(player != null) {
873                    stopAction();
874                    playAction();
875                }
876            } else {
877                //if user canceled opening action, do nothing.
878                Trace.trace(SearsResourceBundle.getResource("various_openCommandCanceled"));
879            }
880        }
881    
882        /**
883         * Method showBrowser.
884         * <br><b>Summary:</b><br>
885         * This method shows a browser, to permits subtitle file selection.
886         * Open the browser at the current subtitle file location, if any.
887         * @param filter      The file filter
888         * @return  <b>File</b>  
889         */
890        public File showBrowser(FileFilter filter) {
891            //The result of the method.
892            File result = null;
893            if(subtitleFile != null){
894                result = showBrowser(filter, subtitleFile.getFile());
895            }else{
896                result = showBrowser(filter, null);
897            }
898            //return the result.
899            return result;
900        }
901        
902        /**
903         * Method showBrowser.
904         * <br><b>Summary:</b><br>
905         * This method shows a browser, to permits subtitle file selection.
906         * Open the browser at the given file location.
907         * @param filter       The file filter.
908         * @param sourceFile   Open the browser in the file's parent path.
909         * @return  <b>File</b>  
910         */
911        public File showBrowser(FileFilter filter, File sourceFile) {
912            //The result of the method
913            File result = null;
914            //create the file chooser.
915            JFileChooser fc = null;
916            if (sourceFile != null) {
917                fc = new JFileChooser(sourceFile.getParent());
918            } else {
919                fc = new JFileChooser(SearsProperties.getProperty(SearsProperties.LAST_FOLDER, ""));
920            }
921            //Set a filter on extension file.
922            if (filter != null){
923                fc.setFileFilter(filter);
924            }
925            //show the browser
926            if (fc.showOpenDialog(instance) == JFileChooser.APPROVE_OPTION) {
927                //set result if user approved selection.
928                result = fc.getSelectedFile();
929            }
930            return result;
931        }
932    
933        /**
934         * Method showSRTBrowser.
935         * <br><b>Summary:</b><br>
936         * This method shows a browser, to permits subtitle file selection.
937         * @return  <b>File</b>  
938         */
939        public File showSRTBrowser() {
940            return showBrowser(new FileFilter() {
941                public String getDescription() {
942                    return SearsResourceBundle.getResource("various_subtitleFileDescription");
943                }
944    
945                public boolean accept(File f) {
946                    return f.isDirectory() || Utils.getExtension(f).equals("srt");
947                }
948            });
949        }
950    
951        /**
952         * Method showVideoBrowser.
953         * <br><b>Summary:</b><br>
954         * This method shows a browser, to permits video selection.
955         * @return  <b>File</b>  
956         */
957        public File showVideoBrowser() {
958            return showBrowser(new FileFilter() {
959                public String getDescription() {
960                    return SearsResourceBundle.getResource("various_videoFileDescription");
961                }
962    
963                public boolean accept(File f) {
964                    return f.isDirectory() || Utils.getExtension(f).equals("avi") || Utils.getExtension(f).equals("mkv")
965                            || Utils.getExtension(f).equals("divx") || Utils.getExtension(f).equals("mov")
966                            || Utils.getExtension(f).equals("ogm") || Utils.getExtension(f).equals("wmv")
967                            || Utils.getExtension(f).equals("mpg") || Utils.getExtension(f).equals("mpeg");
968                }
969            });
970        }
971    
972        /**
973         * Method openFile. <br>
974         * <b>Summary:</b><br>
975         * This method open a subtitle file, and put it in the table.
976         * 
977         * @param file
978         *            The subtitle file to open
979         */
980        public void openFile(File file) {
981            //check current file before open.
982            if (checkUnsavedFileBeforeAction()) {
983                //Empty the subtitle list.
984                subtitleList.clear();
985                //create the new subtitleFile.
986                subtitleFile = new SrtFile(file, subtitleList);
987                //indicate table that datas have changed.
988                tableModel.fireTableDataChanged();
989                updateWindowsName();
990                addToRecentFileList(file);
991                updateActionStatus();
992            }
993        }
994    
995            /**
996             * Method addToRecentFileList.
997             * <br><b>Summary:</b><br>
998             * This method permits to add a new file to recent file list.
999             * @param file          The file that has been opened.
1000             */
1001            private void addToRecentFileList(File file) {
1002                    //remove if previously added.
1003                    recentFiles.remove(file);
1004                    //save the newly opened file under recent file list.
1005                    recentFiles.add(file);
1006                    //Remove oldest one, if list is too long.
1007                    if(recentFiles.size() > RECENT_FILE_NUMBER){
1008                            recentFiles.remove(0);
1009                    }
1010                    //Update the recentFile menu.
1011                    updateRecentFileMenu();
1012            }
1013    
1014        /**
1015         * Method updateRecentFileMenu.
1016         * <br><b>Summary:</b><br>
1017         * Permit to update the recent file menu.
1018         */
1019        private void updateRecentFileMenu() {
1020                    //Remove all previous menu items.
1021            recentFileMenu.removeAll();
1022            //And parse all the recent file adding a menuItem for each file.
1023            //parse in reverse order, to put older opened files at the end.
1024            for(int i= recentFiles.size()-1; i >= 0 ;i --){
1025                    final File finalFile = recentFiles.get(i);
1026                    JMenuItem jMenuItem = new JMenuItem(finalFile.getName());
1027                    jMenuItem.addActionListener(new ActionListener() {
1028                                    public void actionPerformed(ActionEvent e) {
1029                                            openFile(finalFile);
1030                                    }
1031                            });
1032                    recentFileMenu.add(jMenuItem);
1033            }
1034            }
1035    
1036            /**
1037         * Method centerFrame.
1038         * <br><b>Summary:</b><br>
1039         * center frame on screen.
1040         * @param window    The window to center.
1041         */
1042        public static void centerFrame(Window window) {
1043            Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
1044            int w = window.getSize().width;
1045            int h = window.getSize().height;
1046            int x = (dim.width - w) / 2;
1047            int y = (dim.height - h) / 2;
1048            window.setLocation(x, y);
1049        }
1050    
1051        /**
1052         * Class DelayAction.
1053         * <br><b>Summary:</b><br>
1054         * The delay action.
1055         */
1056        class DelayAction extends SearsAction {
1057            private static final long serialVersionUID = -1004418974524773725L;
1058    
1059            public DelayAction() {
1060                super("Delay");
1061            }
1062    
1063            /* (non-Javadoc)
1064             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1065             */
1066            public void actionPerformed(ActionEvent e) {
1067                delayAction();
1068           }
1069        }
1070    
1071        /**
1072         * Class OpenAction.
1073         * <br><b>Summary:</b><br>
1074         * This is the open action, call when user wants to open a file.
1075         */
1076        class OpenAction extends SearsAction {
1077            private static final long serialVersionUID = -6340643915376767725L;
1078    
1079            public OpenAction() {
1080                super("Open");
1081            }
1082    
1083            /* (non-Javadoc)
1084             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1085             */
1086            public void actionPerformed(ActionEvent e) {
1087                openAction();
1088            }
1089        }
1090    
1091        /**
1092         * Class SaveAction.
1093         * <br><b>Summary:</b><br>
1094         * The Save Action
1095         */
1096        class SaveAction extends SearsAction {
1097            private static final long serialVersionUID = 4144500453750320579L;
1098    
1099            public SaveAction() {
1100                super("Save");
1101            }
1102    
1103            /* (non-Javadoc)
1104             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1105             */
1106            public void actionPerformed(ActionEvent e) {
1107                saveAction();
1108            }
1109        }
1110    
1111        /**
1112         * Class SaveAsAction.
1113         * <br><b>Summary:</b><br>
1114         * The SaveAs Action
1115         */
1116        class SaveAsAction extends SearsAction {
1117            private static final long serialVersionUID = 8310536551059661660L;
1118    
1119            public SaveAsAction() {
1120                super("SaveAs");
1121            }
1122    
1123            /* (non-Javadoc)
1124             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1125             */
1126            public void actionPerformed(ActionEvent e) {
1127                saveAsAction();
1128            }
1129        }
1130    
1131        /**
1132         * Class ResynchAction.
1133         * <br><b>Summary:</b><br>
1134         * The Resynch Action
1135         */
1136        class ResynchAction extends SearsAction {
1137            private static final long serialVersionUID = -3381309933952740831L;
1138    
1139            public ResynchAction() {
1140                super("Resynch");
1141            }
1142    
1143            /* (non-Javadoc)
1144             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1145             */
1146            public void actionPerformed(ActionEvent e) {
1147                resynchroAction();
1148            }
1149        }
1150    
1151        /**
1152         * Class SplitAction.
1153         * <br><b>Summary:</b><br>
1154         * The Split Action
1155         */
1156        class SplitAction extends SearsAction {
1157            private static final long serialVersionUID = 4890494133812108855L;
1158    
1159            public SplitAction() {
1160                super("Split");
1161            }
1162    
1163            /* (non-Javadoc)
1164             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1165             */
1166            public void actionPerformed(ActionEvent e) {
1167                splitAction();
1168            }
1169        }
1170    
1171        /**
1172         * Class AppendAction.
1173         * <br><b>Summary:</b><br>
1174         * The Append Action
1175         */
1176        class AppendAction extends SearsAction {
1177            private static final long serialVersionUID = 307293671858946138L;
1178    
1179            public AppendAction() {
1180                super("Append");
1181            }
1182    
1183            /* (non-Javadoc)
1184             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1185             */
1186            public void actionPerformed(ActionEvent e) {
1187                appendAction();
1188            }
1189        }
1190    
1191        /**
1192         * Class QuitAction.
1193         * <br><b>Summary:</b><br>
1194         * The Quit Action
1195         */
1196        class QuitAction extends SearsAction {
1197            private static final long serialVersionUID = -7251685640412615636L;
1198    
1199            public QuitAction() {
1200                super("Quit");
1201            }
1202    
1203            /* (non-Javadoc)
1204             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1205             */
1206            public void actionPerformed(ActionEvent e) {
1207                quitAction();
1208            }
1209        }
1210    
1211        /**
1212         * Class OrderRepairAction.
1213         * <br><b>Summary:</b><br>
1214         * The OrderRepair Action
1215         */
1216        class OrderRepairAction extends SearsAction {
1217            private static final long serialVersionUID = 8302695242689945983L;
1218    
1219            public OrderRepairAction() {
1220                super("OrderRepair");
1221            }
1222    
1223            /* (non-Javadoc)
1224             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1225             */
1226            public void actionPerformed(ActionEvent e) {
1227                orderRepairAction();
1228            }
1229        }
1230    
1231        /**
1232         * Class PlayAction.
1233         * <br><b>Summary:</b><br>
1234         * The Play Action
1235         */
1236        class PlayAction extends SearsAction {
1237            private static final long serialVersionUID = -8943325412755437272L;
1238    
1239            public PlayAction() {
1240                super("Play");
1241            }
1242    
1243            /* (non-Javadoc)
1244             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1245             */
1246            public void actionPerformed(ActionEvent e) {
1247                playAction();
1248            }
1249        }
1250    
1251        /**
1252         * Class StopAction.
1253         * <br><b>Summary:</b><br>
1254         * The Stop Action
1255         */
1256        class StopAction extends SearsAction {
1257            private static final long serialVersionUID = -5754273483777981782L;
1258    
1259            public StopAction() {
1260                super("Stop");
1261            }
1262    
1263            /* (non-Javadoc)
1264             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1265             */
1266            public void actionPerformed(ActionEvent e) {
1267                stopAction();
1268            }
1269        }
1270    
1271        /**
1272         * Class PauseAction.
1273         * <br><b>Summary:</b><br>
1274         * The Pause Action
1275         */
1276        class PauseAction extends SearsAction {
1277            private static final long serialVersionUID = -4954880612379971754L;
1278    
1279            public PauseAction() {
1280                super("Pause");
1281            }
1282    
1283            /* (non-Javadoc)
1284             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1285             */
1286            public void actionPerformed(ActionEvent e) {
1287                pauseAction();
1288            }
1289        }
1290    
1291        /**
1292         * Class PreviousAction.
1293         * <br><b>Summary:</b><br>
1294         * The Previous Action
1295         */
1296        class PreviousAction extends SearsAction {
1297            private static final long serialVersionUID = 7512470475148460128L;
1298    
1299            public PreviousAction() {
1300                super("Previous");
1301            }
1302    
1303            /* (non-Javadoc)
1304             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1305             */
1306            public void actionPerformed(ActionEvent e) {
1307                previousAction();
1308            }
1309        }
1310    
1311        /**
1312         * Class NextAction.
1313         * <br><b>Summary:</b><br>
1314         * The Next Action
1315         */
1316        class NextAction extends SearsAction {
1317            private static final long serialVersionUID = -4498355509337819003L;
1318    
1319            public NextAction() {
1320                super("Next");
1321            }
1322    
1323            /* (non-Javadoc)
1324             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1325             */
1326            public void actionPerformed(ActionEvent e) {
1327                nextAction();
1328            }
1329        }
1330    
1331        /**
1332         * Class TrackerTriggerAction.
1333         * <br><b>Summary:</b><br>
1334         * The TrackerTrigger Action
1335         */
1336        class TrackerTriggerAction extends SearsAction {
1337            private static final long serialVersionUID = 5091927415403379314L;
1338    
1339            public TrackerTriggerAction() {
1340                super("TrackerTrigger");
1341            }
1342    
1343            /* (non-Javadoc)
1344             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1345             */
1346            public void actionPerformed(ActionEvent e) {
1347                trackerTriggerAction();
1348            }
1349        }
1350    
1351        /**
1352         * Method orderRepairAction.
1353         * <br><b>Summary:</b><br>
1354         * This method is called when user wants to repair the order of the subtitle file.
1355         * It will check chronology, order ST's with their start time, and finally fix ST's numbers.
1356         */
1357        public void orderRepairAction() {
1358            //Must have opened a subtitle file to repair.
1359            if (subtitleFile != null) {
1360                //Apply order repair
1361                subtitleFile.orderRepair();
1362                //indicate table that data have changed
1363                tableModel.fireTableDataChanged();
1364                updateWindowsName();
1365                updateActionStatus();
1366            } else {
1367                //Must have opened a subtitle file to apply a delay.
1368                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeRepair"), Trace.WARNING_PRIORITY);
1369            }
1370        }
1371    
1372        /**
1373         * Method pauseAction.
1374         * <br><b>Summary:</b><br>
1375         * This method when user wants to pause the video
1376         */
1377        public void pauseAction() {
1378            try {
1379                    if(player != null) {
1380                            //Pause the video player
1381                            player.pause();
1382                    }
1383            } catch (PlayerException e) {
1384                //display an error message.
1385                Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
1386            }
1387        }
1388    
1389        /**
1390         * Method trackerTriggerAction.
1391         * <br><b>Summary:</b><br>
1392         * This method is called when user wants to change the tracker mode.
1393         */
1394        public void trackerTriggerAction() {
1395            // TODO change the tracker mode.
1396            Trace.trace("TriggerAction");
1397        }
1398    
1399        /**
1400         * Method nextAction.
1401         * <br><b>Summary:</b><br>
1402         * This method is called when user wants to move forward in the video.
1403         */
1404        public void nextAction() {
1405            try {
1406                    if(player != null) {
1407                            int currentOffset = player.getPosition();
1408                            if(currentOffset != -1) {
1409                                    //Go forward using the time step.
1410                                    int timeStep = jPanelVideoControler.getTimeStep();
1411                                    player.setPosition(currentOffset + timeStep);
1412                            }
1413                    }
1414            } catch (PlayerException e) {
1415                //display an error message.
1416                Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
1417            }
1418        }
1419    
1420        /**
1421         * Method previousAction.
1422         * <br><b>Summary:</b><br>
1423         * This method is called when user wants to go backward in the video.
1424         */
1425        public void previousAction() {
1426            try {
1427                    if(player != null) {
1428                            int currentOffset = player.getPosition();
1429                            if(currentOffset != -1) {                       
1430                                    //Go forward using the time step.
1431                                    int timeStep = jPanelVideoControler.getTimeStep();
1432                                    player.setPosition(currentOffset - timeStep);
1433                            }
1434                    }
1435            } catch (PlayerException e) {
1436                //display an error message.
1437                Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
1438            }
1439        }
1440    
1441        /**
1442         * Method stopAction.
1443         * <br><b>Summary:</b><br>
1444         * This method is called when user wants to stop the video.
1445         */
1446        public void stopAction() {
1447            if(player != null) {
1448                    try {
1449                            player.stop();
1450                    } catch (PlayerException e) {
1451                            //display an error message.
1452                    Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
1453                            }
1454            }
1455        }
1456    
1457        /**
1458         * Method playAction.
1459         * <br><b>Summary:</b><br>
1460         * This method is called when user wants to play the video.
1461         */
1462        public void playAction() {
1463            String playerFile = SearsProperties.getProperty(SearsProperties.PLAYER_FULL_PATH);
1464            if (playerFile != "") {
1465                // Save the subtitle in tempory file
1466                SubtitleFile subFile = subtitleFile;
1467                if (subFile == null) {
1468                    // Create a fake subtitle file if no file exists
1469                    subFile = new SrtFile();
1470                    subFile.addFakeSub();
1471                }
1472                subFile.writeToTemporaryFile();
1473                String subtitleFileName = subFile.getTemporaryFile().getAbsolutePath();
1474    
1475                // Launch VLC player
1476                try {
1477                    if (player == null) {
1478                        player = constructPlayer();
1479                    }
1480                    // Select the video file
1481                    if (playerVideoFile == null) {
1482                        selectVideoAction();
1483                    } else {
1484                            //Verify that user has chosen a file.
1485                            if(playerVideoFile != null) {
1486                                    //Launch the video player
1487                                    player.play(playerVideoFile, subtitleFileName);
1488                            }
1489                    }
1490                } catch (PlayerException e) {
1491                    //display an error message.
1492                    Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
1493                }
1494            } else {
1495                JOptionPane.showOptionDialog(instance, 
1496                        SearsResourceBundle.getResource("error_playerNotDefined"), 
1497                        SearsResourceBundle.getResource("error_message"), 
1498                        JOptionPane.CLOSED_OPTION,
1499                        JOptionPane.ERROR_MESSAGE, null, null, null);
1500            }
1501        }
1502    
1503        /**
1504         * Class AccentRepairAction.
1505         * <br><b>Summary:</b><br>
1506         * The AccentRepair Action
1507         */
1508        class AccentRepairAction extends SearsAction {
1509            private static final long serialVersionUID = 1881880058709461107L;
1510    
1511            public AccentRepairAction() {
1512                super("AccentRepair");
1513            }
1514    
1515            /* (non-Javadoc)
1516             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1517             */
1518            public void actionPerformed(ActionEvent e) {
1519                accentRepairAction();
1520            }
1521        }
1522    
1523        /**
1524         * Method accentRepairAction.
1525         * <br><b>Summary:</b><br>
1526         * This method is called when user wants to remove 
1527         * the accents and special characters from the selected subtitle file.
1528         * 
1529         */
1530        public void accentRepairAction() {
1531            //Must have opened a subtitle file to repair accents.
1532            if (subtitleFile != null) {
1533                //get the selected rows.
1534                int[] selectedIndex = table.getSelectedRows();
1535                //Remove accent from them.
1536                subtitleFile.accentRepair(selectedIndex);
1537                //indicate table that data have changed
1538                tableModel.fireTableDataChanged();
1539                updateWindowsName();
1540                updateActionStatus();
1541            } else {
1542                //Must have opened a subtitle file to apply a delay.
1543                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeAccentRepair"), Trace.WARNING_PRIORITY);
1544            }
1545        }
1546    
1547        /**
1548         * Class HtmlRepairAction.
1549         * <br><b>Summary:</b><br>
1550         * The HtmlRepair Action
1551         */
1552        class HtmlRepairAction extends SearsAction {
1553            private static final long serialVersionUID = 2595376205493266568L;
1554    
1555            public HtmlRepairAction() {
1556                super("HtmlRepair");
1557            }
1558    
1559            /* (non-Javadoc)
1560             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1561             */
1562            public void actionPerformed(ActionEvent e) {
1563                htmlRepairAction();
1564            }
1565        }
1566    
1567        /**
1568         * Method htmlRepairAction.
1569         * <br><b>Summary:</b><br>
1570         * This method is called when user wants to remove 
1571         * the htmls.
1572         */
1573        public void htmlRepairAction() {
1574            //Must have opened a subtitle file to repair.
1575            if (subtitleFile != null) {
1576                //get the selected rows.
1577                int[] selectedIndex = table.getSelectedRows();
1578                //Remove html tags from them.
1579                subtitleFile.htmlRepair(selectedIndex);
1580                //indicate table that data have changed
1581                tableModel.fireTableDataChanged();
1582                updateWindowsName();
1583                updateActionStatus();
1584            } else {
1585                //Must have opened a subtitle file to apply a delay.
1586                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeHTMLRepair"), Trace.WARNING_PRIORITY);
1587            }
1588        }
1589    
1590        /**
1591         * Class SelectVideoAction.
1592         * <br><b>Summary:</b><br>
1593         * The SelectVideo Action
1594         */
1595        class SelectVideoAction extends SearsAction {
1596            private static final long serialVersionUID = 3081640943800843335L;
1597    
1598            public SelectVideoAction() {
1599                super("SelectVideo");
1600            }
1601    
1602            /* (non-Javadoc)
1603             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1604             */
1605            public void actionPerformed(ActionEvent e) {
1606                selectVideoAction();
1607            }
1608        }
1609    
1610        /**
1611         * Method selectVideoAction.
1612         * <br><b>Summary:</b><br>
1613         * This method is called when user wants to select the video file. 
1614         */
1615        public void selectVideoAction() {
1616            File file = showVideoBrowser();
1617            if (file != null) {
1618                playerVideoFile = file.getAbsolutePath();
1619                SearsProperties.setProperty(SearsProperties.LAST_FOLDER, file.getParent());
1620                stopAction();
1621                playAction();
1622            }
1623        }
1624    
1625        /**
1626         * Method constructPlayer.
1627         * <br><b>Summary:</b><br>
1628         * This method construct a new player, in function of the one selected in the properties.
1629         * @return
1630         */
1631        private PlayerInterface constructPlayer() throws PlayerException {
1632            //The result of the method.
1633            PlayerInterface result = null;
1634            //First is to get the selected player.
1635            try {
1636                int selectedPlayer = Integer.parseInt(SearsProperties.getProperty(SearsProperties.PLAYER_SELECTED));
1637                //Construct the player that correspond to the new selected one.
1638                switch (selectedPlayer) {
1639                    case SearsProperties.KEY_VLC:
1640                        result = new VLCPlayer();
1641                        break;
1642                    case SearsProperties.KEY_OTHER:
1643                        result = new DefaultPlayer();
1644                        break;
1645                }
1646            } catch (NumberFormatException e) {
1647                //There is an error in the player selected.
1648                throw new PlayerException("Selected player type is invalid:"
1649                        + SearsProperties.getProperty(SearsProperties.PLAYER_SELECTED));
1650            }
1651            //return the result.
1652            return result;
1653        }
1654    
1655        /**
1656         * Class ChainRepairAction. <br>
1657         * <b>Summary:</b><br>
1658         * The ChainRepair Action
1659         */
1660        class ChainRepairAction extends SearsAction {
1661            private static final long serialVersionUID = 1973498050830247049L;
1662    
1663            public ChainRepairAction() {
1664                super("ChainRepair");
1665            }
1666    
1667            /* (non-Javadoc)
1668             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1669             */
1670            public void actionPerformed(ActionEvent e) {
1671                chainRepairAction();
1672            }
1673        }
1674    
1675        /**
1676         * Method chainRepairAction.
1677         * <br><b>Summary:</b><br>
1678         * This method is called when user wants to chain repair.
1679         * It will apply several repair action.
1680         */
1681        public void chainRepairAction() {
1682            //Must have opened a subtitle file to repair.
1683            if (subtitleFile != null) {
1684                //Show the chain repair dialog
1685                JDialogChainRepair cr = new JDialogChainRepair();
1686                cr.setVisible(true);
1687                //Get the result.
1688                boolean[] result = cr.getResult();
1689                //Apply repair action in function of the result.
1690                //The accent repair
1691                if (result[0]) {
1692                    subtitleFile.accentRepair(null);
1693                }
1694                //The HTML repair
1695                if (result[1]) {
1696                    subtitleFile.htmlRepair(null);
1697                }
1698                //The orderRepair
1699                if (result[2]) {
1700                    subtitleFile.orderRepair();
1701                }
1702                //The Time Repair
1703                if (result[3]) {
1704                    subtitleFile.timeRepair();
1705                }
1706                //indicate table that data have changed
1707                tableModel.fireTableDataChanged();
1708                updateWindowsName();
1709                updateActionStatus();
1710            } else {
1711                //Must have opened a subtitle file to apply a chain repair.
1712                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeChainRepair"), Trace.WARNING_PRIORITY);
1713            }
1714        }
1715    
1716        /**
1717         * Class TimeRepairAction.
1718         * <br><b>Summary:</b><br>
1719         * The TimeRepair Action
1720         */
1721        class TimeRepairAction extends SearsAction {
1722            private static final long serialVersionUID = 1635491769577703287L;
1723    
1724            public TimeRepairAction() {
1725                super("TimeRepair");
1726            }
1727    
1728            /* (non-Javadoc)
1729             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1730             */
1731            public void actionPerformed(ActionEvent e) {
1732                timeRepairAction();
1733             }
1734        }
1735    
1736        /**
1737         * Class NormalizeDurationAction.
1738         * <br><b>Summary:</b><br>
1739         * The NormalizeDuration Action
1740         */
1741        class NormalizeDurationAction extends SearsAction {
1742       
1743            /** (<b>long</b>) serialVersionUID: The serialVersionUID */
1744                    private static final long serialVersionUID = -7636895212998797145L;
1745    
1746                    public NormalizeDurationAction() {
1747                super("NormalizeDuration");
1748            }
1749    
1750            /* (non-Javadoc)
1751             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1752             */
1753            public void actionPerformed(ActionEvent e) {
1754                NormalizeDurationAction();
1755            }
1756        }
1757        
1758        
1759        /**
1760         * Method NormalizeDurationAction.
1761         * <br><b>Summary:</b><br>
1762         * This permit to normalize the ST duration.
1763         */
1764        private void NormalizeDurationAction() {
1765            //Must have opened a subtitle file to apply a delay.
1766            if (subtitleFile != null) {
1767                //get the selected rows to delay.
1768                int[] indexToNormalize = table.getSelectedRows();
1769                //ask the user for delay to apply
1770                JDialogNormalizeAction jd = new JDialogNormalizeAction();
1771                jd.setVisible(true);
1772                //if user choose a delay.
1773                if (jd.hasBeenValidated()) {
1774                    double[] times = jd.getTimes();
1775                    //delay the subtitle with the given delay.
1776                    //multiply by 1000, because time is indicated in second, with possible milliseconds.
1777                    subtitleFile.normalizeDuration(indexToNormalize, (int) (times[0] * 1000), (int) (times[1] * 1000));
1778                    //indicate table that data have changed
1779                    tableModel.fireTableDataChanged();
1780                }
1781                updateWindowsName();
1782                updateActionStatus();
1783            } else {
1784                //Must have opened a subtitle file to apply a delay.
1785                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeNormalize"), Trace.WARNING_PRIORITY);
1786            }
1787            }
1788        
1789        /**
1790         * Class OptionsAction.
1791         * <br><b>Summary:</b><br>
1792         * The Options Action
1793         */
1794        class OptionsAction extends SearsAction {
1795    
1796    
1797            /** (<b>long</b>) serialVersionUID: The serialVersionUID */
1798                    private static final long serialVersionUID = -4333319058198937067L;
1799    
1800                    public OptionsAction() {
1801                super("Options");
1802            }
1803    
1804            /* (non-Javadoc)
1805             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1806             */
1807            public void actionPerformed(ActionEvent e) {
1808                optionsAction();
1809            }
1810        }
1811    
1812        /**
1813         * Method optionsAction.
1814         * <br><b>Summary:</b><br>
1815         * This method is called when user wants to change sears options.
1816         * It will display the options dialog, and set the sears properties.
1817         */
1818        private void optionsAction() {
1819            //Show ans options dialog.
1820            JDialogOptions jDialogOptions = new JDialogOptions();
1821            jDialogOptions.setVisible(true);
1822            //If options has been validated, set the new options.
1823            if (jDialogOptions.hasBeenValidated()) {
1824                //A boolean to know if a message has to be displayed.
1825                boolean displayRebootMessage = false;
1826                //set the selected locale, if changed.
1827                Locale selectedLocale = jDialogOptions.getSelectedLocale();
1828                if (!SearsProperties.getProperty(SearsProperties.LANGUAGE, "").equals(
1829                        selectedLocale.getLanguage().toString())
1830                        || !SearsProperties.getProperty(SearsProperties.COUNTRY, "").equals(
1831                                selectedLocale.getCountry().toString())) {
1832                    setSelectedLocale(selectedLocale);
1833                    //To see changes, you have to reboot.
1834                    displayRebootMessage = true;
1835                }
1836                //set the look and feel if changed.
1837                String selectedLookAndFeel = jDialogOptions.getSelectedLookAndFeel();
1838                if (!selectedLookAndFeel.equals(SearsProperties.getProperty(SearsProperties.LOOK_AND_FEEL, ""))) {
1839                    setLookAndFeel(selectedLookAndFeel);
1840                    //To see changes, you have to reboot.
1841                    displayRebootMessage = true;
1842                }
1843                //set the player if changed.
1844                String selectedPlayerPath = jDialogOptions.getConfiguredPlayerPath();
1845                int selectedPlayer = jDialogOptions.getSelectedPlayer();
1846                if (!selectedPlayerPath.equals(SearsProperties.getProperty(SearsProperties.PLAYER_FULL_PATH, ""))
1847                        || !("" + selectedPlayer).equals(SearsProperties.getProperty(SearsProperties.PLAYER_SELECTED, ""))) {
1848                    setPlayer(selectedPlayerPath, selectedPlayer);
1849                }
1850                //Set the DOS line separator options, if changed.
1851                String selectedDOSLineSeparator = jDialogOptions.getSelectedDOSLineSeparator();
1852                if (!selectedDOSLineSeparator.equals(SearsProperties.getProperty(SearsProperties.DOS_LINE_SEPARATOR, ""))) {
1853                    SearsProperties.setProperty(SearsProperties.DOS_LINE_SEPARATOR, selectedDOSLineSeparator);
1854                }
1855                //Set the check for update option, if changed.
1856                boolean selectedCheckForUpdates = jDialogOptions.getSelectedCheckForUpdates();
1857                if (selectedCheckForUpdates
1858                        && SearsProperties.getProperty(SearsProperties.UPDATE_ADDRESS, "firstLaunch").equals("")) {
1859                    //If user checks, and no address has been set in properties.
1860                    //then set the default one.
1861                    SearsProperties.setProperty(SearsProperties.UPDATE_ADDRESS, Updater.DEFAULT_UPDATE_ADDRESS);
1862                } else if (!selectedCheckForUpdates) {
1863                    //Else if user does not want to check for updates, set to "".
1864                    SearsProperties.setProperty(SearsProperties.UPDATE_ADDRESS, "");
1865                }
1866                //Set the VLC restart options, if changed.
1867                String selectedVlcRestart = jDialogOptions.getSelectedVlcRestart();
1868                if (!selectedVlcRestart.equals(SearsProperties.getProperty(SearsProperties.VLC_RESTART,
1869                        VLCPlayer.DEFAULT_VLC_RESTART))) {
1870                    SearsProperties.setProperty(SearsProperties.VLC_RESTART, selectedVlcRestart);
1871                }
1872                //Set the icon set option, if changed.
1873                String selectedIconSet = jDialogOptions.getSelectedIconSet();
1874                if (selectedIconSet != null && !selectedIconSet.equals(SearsProperties.getProperty(SearsProperties.ICON_SET))) {
1875                    SearsProperties.setProperty(SearsProperties.ICON_SET, selectedIconSet);
1876                    displayRebootMessage = true;
1877                }
1878                //Now display warning message about reboot.
1879                if (displayRebootMessage) {
1880                    JOptionPane.showConfirmDialog(instance, SearsResourceBundle.getResource("warning_rebootToSeeChanges"),
1881                            SearsResourceBundle.getResource("warning_rebootToSeeChangesTitle"), JOptionPane.DEFAULT_OPTION,
1882                            JOptionPane.WARNING_MESSAGE);
1883                }
1884            }
1885        }
1886        
1887            /**
1888             * Class AboutAction.
1889             * <br><b>Summary:</b><br>
1890             * The About Action
1891             */
1892            class AboutAction extends SearsAction {
1893                    /** (<b>long</b>) serialVersionUID: The serialVersionUID */
1894                    private static final long serialVersionUID = 2988713944030176952L;
1895    
1896                    public AboutAction() {
1897                            super("About");
1898                    }
1899    
1900                    /* (non-Javadoc)
1901                     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1902                     */
1903                    public void actionPerformed(ActionEvent e) {
1904                            aboutAction();
1905                    }
1906            }
1907    
1908            /**
1909             * Method dialogAction.
1910             * <br><b>Summary:</b><br>
1911             * This method is called when user wants to see the about dialog window.
1912             * It will display the about dialog..
1913             */
1914            private void aboutAction() {
1915                    // we create a new JDialogAbout and set it visible:
1916                    JDialogAbout jDialogAbout = new JDialogAbout("about");
1917                    jDialogAbout.configureSize();
1918                    jDialogAbout.setVisible(true);
1919            }
1920    
1921        /**
1922         * Class GotoSubAction.
1923         * <br><b>Summary:</b><br>
1924         * The GotoSub Action
1925         */
1926        class GotoSubAction extends SearsAction {
1927            private static final long serialVersionUID = 4897317673745710990L;
1928    
1929                    public GotoSubAction() {
1930                super("GotoSub");
1931            }
1932    
1933            /* (non-Javadoc)
1934             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1935             */
1936            public void actionPerformed(ActionEvent e) {
1937                gotoSubAction();
1938            }
1939        }
1940        
1941        /**
1942         * Class MagicResynchroAction.
1943         * <br><b>Summary:</b><br>
1944         * The MagicResynchro
1945         */
1946        class MagicResynchroAction extends SearsAction {
1947            
1948                    /** (<b>long</b>) serialVersionUID: The serialVersionUID */
1949                    private static final long serialVersionUID = 7188578370344829732L;
1950    
1951                    public MagicResynchroAction() {
1952                super("MagicResynchro");
1953            }
1954    
1955            /* (non-Javadoc)
1956             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1957             */
1958            public void actionPerformed(ActionEvent e) {
1959                magicResynchroAction();
1960            }
1961        }
1962        
1963        /**
1964         * Class RemoveAllAnchorsAction.
1965         * <br><b>Summary:</b><br>
1966         * The RemoveAllAnchorsAction
1967         */
1968        class RemoveAllAnchorsAction extends SearsAction {
1969            
1970                    /** (<b>long</b>) serialVersionUID: The serialVersionUID */
1971                    private static final long serialVersionUID = 2484034561626602996L;
1972    
1973                    public RemoveAllAnchorsAction() {
1974                super("RemoveAllAnchors");
1975            }
1976    
1977            /* (non-Javadoc)
1978             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
1979             */
1980            public void actionPerformed(ActionEvent e) {
1981                removeAllAnchorsAction();
1982            }
1983        }
1984        
1985        /**
1986         * Method removeAllAnchorsAction.
1987         * <br><b>Summary:</b><br>
1988         * Call this method to remove all the anchors from subtitles.
1989         */
1990        private void removeAllAnchorsAction() {
1991            if(subtitleList != null){
1992                    //Parse all the subtitles, and remove anchor from them.
1993                    for(Subtitle subtitle : subtitleList){
1994                            subtitle.unanchor();
1995                    }
1996                updateActionStatus();
1997                    //update table.
1998                    tableModel.fireTableDataChanged();
1999            }
2000        }
2001            
2002        
2003        
2004        /**
2005         * Method gotoSubAction.
2006         * <br><b>Summary:</b><br>
2007         * This method permits to set the video position to the selected subtitle.
2008         */
2009        public void gotoSubAction() {
2010            //First is to get the selected subtitle.
2011            int selectedRow = table.getSelectedRow();
2012            //If selected and player is not null, go to this subtitle.
2013            if(player != null && selectedRow != -1){
2014                Subtitle selectedSubtitle = (Subtitle) subtitleList.get(selectedRow);
2015                if(selectedSubtitle != null){
2016                    try {
2017                        //Divide by 1000, because start date is in milliseconds.
2018                        player.setPosition(selectedSubtitle.getStartDate()/1000);
2019                    } catch (PlayerException e) {
2020                        Trace.trace(e.getMessage(), Trace.ERROR_PRIORITY);
2021                    }
2022                }
2023            }
2024        }
2025    
2026        
2027        
2028    
2029            /**
2030         * Method setPlayer.
2031         * <br><b>Summary:</b><br>
2032         * Set the player.
2033         * @param selectedPlayerPath    The player full path to set.
2034         * @param selectedPlayer        The player to set.
2035         */
2036        private void setPlayer(String selectedPlayerPath, int selectedPlayer) {
2037            SearsProperties.setProperty(SearsProperties.PLAYER_FULL_PATH, selectedPlayerPath);
2038            SearsProperties.setProperty(SearsProperties.PLAYER_SELECTED, "" + selectedPlayer);
2039            if (player != null) {
2040                //quit previous player if exist.
2041                player.quit();
2042                //disable the player
2043                player = null;
2044            }
2045            //construct the new player
2046            try {
2047                player = constructPlayer();
2048            } catch (PlayerException e) {
2049                e.printStackTrace();
2050            }
2051        }
2052    
2053    
2054        /**
2055         * Method setSelectedLocale.
2056         * <br><b>Summary:</b><br>
2057         * Use this to set the locale.
2058         * @param selectedLocale        The locale to set.
2059         */
2060        private void setSelectedLocale(Locale selectedLocale) {
2061            SearsResourceBundle.setLocale(selectedLocale);
2062            SearsProperties.setProperty(SearsProperties.LANGUAGE, selectedLocale.getLanguage());
2063            SearsProperties.setProperty(SearsProperties.COUNTRY, selectedLocale.getCountry());
2064            setLocale(selectedLocale);
2065        }
2066    
2067        /**
2068         * Method setLookAndFeel.
2069         * <br><b>Summary:</b><br>
2070         * Use this method to sets the look and feel.
2071         * @param selectedLookAndFeel       The look and feel to set.
2072         */
2073        private void setLookAndFeel(String selectedLookAndFeel) {
2074            SearsProperties.setProperty(SearsProperties.LOOK_AND_FEEL, selectedLookAndFeel);
2075        }
2076    
2077        /**
2078         * Method timeRepairAction.
2079         * <br><b>Summary:</b><br>
2080         * This method is called when user wants to time repair.
2081         * It will correct the time superposition problem.
2082         * When subtitle ends after next ST start time.
2083         */
2084        public void timeRepairAction() {
2085            //Must have opened a subtitle file to repair.
2086            if (subtitleFile != null) {
2087                //Apply Time repair
2088                subtitleFile.timeRepair();
2089                //indicate table that data have changed
2090                tableModel.fireTableDataChanged();
2091                updateWindowsName();
2092                updateActionStatus();
2093            } else {
2094                //Must have opened a subtitle file to apply a delay.
2095                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeTimeRepair"), Trace.WARNING_PRIORITY);
2096            }
2097        }
2098    
2099        /**
2100         * Method splitAction.
2101         * <br><b>Summary:</b><br>
2102         * This method is called when user wants to split STs file in two files.
2103         * Must have selected the ST to split from.
2104         */
2105        public void splitAction() {
2106            //Must have opened a subtitle.
2107            if (subtitleFile != null) {
2108                int selectedIndex = table.getSelectedRow();
2109                if (selectedIndex == -1) {
2110                    selectedIndex = 0;
2111                }
2112                //show Split dialog.
2113                JDialogSplit splitDialog = new JDialogSplit(subtitleList, selectedIndex, subtitleFile);
2114                splitDialog.setLocationRelativeTo(this);
2115                splitDialog.setVisible(true);
2116                if (splitDialog.hasBeenValidated()) {
2117                    //Get the destination files.
2118                    File[] destinationFiles = splitDialog.getDestinationsFiles();
2119                    //The the second part delay
2120                    double secondPartDelay = splitDialog.getSecondPartDelay();
2121                    //Look for open second part option.
2122                    boolean openSecondPart = splitDialog.needToOpenSecondPart();
2123                    //Get the choosen subtitle for split.
2124                    int subtitleIndex = splitDialog.getChoosenSubtitle();
2125                    //Apply spliting to subtitleFile.
2126                    //multiply by 1000, because delay is indicated in second, with possible milliseconds.
2127                    SubtitleFile[] parts = subtitleFile.split(destinationFiles, subtitleIndex,
2128                            (int) (secondPartDelay * 1000));
2129                    //At the end save the two files.
2130                    Trace.trace("Saving splitted files", Trace.ALGO_PRIORITY);
2131                    parts[0].writeToFile(parts[0].getFile());
2132                    parts[1].writeToFile(parts[1].getFile());
2133                    //If need to open second part, open it !
2134                    if (openSecondPart) {
2135                        openFile(parts[1].getFile());
2136                    }
2137                }
2138                updateWindowsName();
2139                updateActionStatus();
2140            } else {
2141                //Must have opened a subtitle file to apply a split.
2142                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeSplit"), Trace.WARNING_PRIORITY);
2143            }
2144        }
2145    
2146        /**
2147         * Method appendAction.
2148         * <br><b>Summary:</b><br>
2149         * This method is called when user wants to make an append Action.
2150         */
2151        public void appendAction() {
2152            //Must have opened a subtitle.
2153            if (subtitleFile != null) {
2154                //show Append dialog.
2155                JDialogAppend appendDialog = new JDialogAppend();
2156                appendDialog.setVisible(true);
2157                if (appendDialog.hasBeenValidated()) {
2158                    File fileToAppend = appendDialog.getFileToAppend();
2159                    double delay = appendDialog.getDelay();
2160                    if (fileToAppend != null) {
2161                        SrtFile srtFileToAppend = new SrtFile(fileToAppend, new ArrayList<Subtitle>());
2162                        //Append file to current one.
2163                        //multiply by 1000, because delay is indicated in second, with possible milliseconds.
2164                        subtitleFile.append(srtFileToAppend, (int) (delay * 1000));
2165                    }
2166                    //indicate table that data have changed
2167                    tableModel.fireTableDataChanged();
2168                }
2169                updateWindowsName();
2170                updateActionStatus();
2171            } else {
2172                //Must have opened a subtitle file to apply an append.
2173                Trace.trace(SearsResourceBundle.getResource("warning_mustOpenBeforeAppend"), Trace.WARNING_PRIORITY);
2174            }
2175        }
2176    
2177        /**
2178         * Method <b>watchForUnsavedFile</b>
2179         * <br><b>Summary:</b><br>
2180         * This method check that current file (if open) does not have unsaved modifications.
2181         * If it has, ask for user to "save", "don't save" or "cancel".
2182         * If user click on save, current file will be saved, method returns true.
2183         * If user click on save, current file is not saved, method returns true.
2184         * if user click on cancel, file is not saved, method returns false.
2185         * Parameters:
2186         * @return  <b>boolean</b>      True if user wants to continue (click on "save" or "don't save")
2187         *                              , or if file has no modifications. false if user clicked on "cancel"
2188         * 
2189         */
2190        private boolean checkUnsavedFileBeforeAction() {
2191            //The result of the method
2192            boolean result = true;
2193            //If there already is an unsaved open file, warn the user before opening.
2194            if (subtitleFile != null && subtitleFile.isFileChanged()) {
2195                result = false;
2196                //File is not saved, ask for saving.
2197                String message = SearsResourceBundle.getResource("warning_fileNotSaved");
2198                //Custom button text
2199                Object[] options = { SearsResourceBundle.getResource("button_save"),
2200                        SearsResourceBundle.getResource("button_dontSave"),
2201                        SearsResourceBundle.getResource("button_cancel") };
2202                int userChoice = JOptionPane.showOptionDialog(instance, message, SearsResourceBundle
2203                        .getResource("warning_fileNotSaved"), JOptionPane.YES_NO_CANCEL_OPTION,
2204                        JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
2205                //If user choose to save file, save it, and return true.
2206                if (userChoice == 0) {
2207                    saveAction();
2208                    result = true;
2209                } else if (userChoice == 1) {
2210                    //if user choosed don't save, just return true
2211                    result = true;
2212                }//else return false.
2213            }
2214            //return the result
2215            return result;
2216        }
2217    
2218        /**
2219         * Method updateWindowsName.
2220         * <br><b>Summary:</b><br>
2221         * Update the main windows name.
2222         */
2223        private void updateWindowsName() {
2224            String title = SearsResources.getString("SearsGUITitle")+Version.VERSION;
2225            if (subtitleFile != null) {
2226                title += " - " + subtitleFile.getFile().getName();
2227                if (subtitleFile.isFileChanged()) {
2228                    title += "*";
2229                }
2230            }
2231            setTitle(title);
2232        }
2233    
2234        /* (non-Javadoc)
2235         * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
2236         */
2237        public void mouseClicked(MouseEvent e) {
2238            maybeShowPopup(e);
2239            mayBeAnchor(e);
2240        }
2241    
2242        /**
2243         * Method mayBeAnchor.
2244         * <br><b>Summary:</b><br>
2245         * This method is called whn a click is detected.
2246         * It permits to check an anchor event.
2247         * @param e         The mouseEvent that called for this mayBeAnchor.
2248         */
2249        private void mayBeAnchor(MouseEvent e) {
2250            if (e.getButton() == MouseEvent.BUTTON1 && e.getComponent() == table) {
2251                    //click must be on 4rth column.
2252                    Point clickPoint = new Point(e.getX(), e.getY());
2253                    if(table.columnAtPoint(clickPoint) == SubtitleTableModel.ANCHOR_COLUMN){
2254                            //retrieve the clicked row.
2255                            int row = table.rowAtPoint(clickPoint);
2256                            anchorSubtitle(row);
2257                    }
2258            }
2259                    
2260            }
2261    
2262            /**
2263             * Method anchorSubtitle.
2264             * <br><b>Summary:</b><br>
2265             * This method permits to anchor/un-anchor the given index subtitle.
2266             * @param subtitleIndex         The subtitle index to anchor.
2267             */
2268            private void anchorSubtitle(int row) {
2269                    //Retrieve current playing time.
2270                    if (player != null) {
2271                            try {
2272                                    // Retrieve the given subtitle.
2273                                    Subtitle subtitle = subtitleList.get(row);
2274                                    if (subtitle.isAnchored()) {
2275                                            subtitle.unanchor();
2276                                    } else {
2277                                            int currentPosition = player.getPosition();
2278                                            subtitle.anchor(currentPosition);
2279                                    }
2280                                    //Indicate to table, that data changed.
2281                                    tableModel.fireTableDataChanged();
2282                                    //Update actions
2283                        updateActionStatus();
2284                            } catch (PlayerException e) {
2285                                    e.printStackTrace();
2286                            }
2287                    }
2288            }
2289    
2290            /* (non-Javadoc)
2291         * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
2292         */
2293        public void mouseEntered(MouseEvent e) {
2294        }
2295    
2296        /* (non-Javadoc)
2297         * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
2298         */
2299        public void mouseExited(MouseEvent e) {
2300        }
2301    
2302        /* (non-Javadoc)
2303         * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
2304         */
2305        public void mousePressed(MouseEvent e) {
2306            maybeShowPopup(e);
2307        }
2308    
2309        /**
2310         * Method maybeShowPopup.
2311         * <br><b>Summary:</b><br>
2312         * Show the popup menu if necessary
2313         * @param e The mouse event.
2314         */
2315        private void maybeShowPopup(MouseEvent e) {
2316            //Check wether it is a right button, or a system pop-up trigger.
2317            if (e.getButton() == MouseEvent.BUTTON3 || e.isPopupTrigger()) {
2318                getJPopUpMenu().show(e.getComponent(), e.getX(), e.getY());
2319            }
2320        }
2321    
2322        /**
2323         * Method getJPopUpMenu.
2324         * <br><b>Summary:</b><br>
2325         * Construct/return the JPopUpMenu.
2326         * @return  <b>JPopupMenu</b>   The Sears JPopupMenu.
2327         */
2328        private JPopupMenu getJPopUpMenu() {
2329            if (jPopupMenu == null) {
2330                jPopupMenu = new JPopupMenu("Sears");
2331                //////////////////
2332                //ACTION MENU
2333                /////////////////
2334                //Add a separator.
2335                jPopupMenu.addSeparator();
2336                //DELAY ITEM
2337                jPopupMenu.add(getAction(ACTION_KEY_DELAY));
2338                //RESYNCHRO ITEM
2339                jPopupMenu.add(getAction(ACTION_KEY_RESYNCH));
2340                //Add a separator.
2341                jPopupMenu.addSeparator();
2342                //SPLIT ITEM
2343                jPopupMenu.add(getAction(ACTION_KEY_SPLIT));
2344                //APPEND ITEM
2345                jPopupMenu.add(getAction(ACTION_KEY_APPEND));
2346                //Add a separator.
2347                jPopupMenu.addSeparator();
2348                //GOTO SUB
2349                jPopupMenu.add(getAction(ACTION_KEY_GOTO_SUB));
2350            }
2351            return jPopupMenu;
2352        }
2353    
2354        /**
2355         * Method <b>fileChanged</b>
2356         * <br><b>Summary:</b><br>
2357         * Set the fileChanged status flag to true.
2358         */
2359        public void fileChanged() {
2360            if (subtitleFile != null) {
2361                subtitleFile.fileChanged();
2362            }
2363            updateWindowsName();
2364        }
2365    
2366        /* (non-Javadoc)
2367         * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
2368         */
2369        public void mouseReleased(MouseEvent e) {
2370            // TODO Auto-generated method stub
2371        }
2372    
2373        /* (non-Javadoc)
2374         * @see java.awt.event.WindowListener#windowActivated(java.awt.event.WindowEvent)
2375         */
2376        public void windowActivated(WindowEvent e) {
2377            // TODO Auto-generated method stub
2378        }
2379    
2380        /* (non-Javadoc)
2381         * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent)
2382         */
2383        public void windowClosed(WindowEvent e) {
2384            quitAction();
2385        }
2386    
2387        /* (non-Javadoc)
2388         * @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
2389         */
2390        public void windowClosing(WindowEvent e) {
2391            quitAction();
2392        }
2393    
2394        /* (non-Javadoc)
2395         * @see java.awt.event.WindowListener#windowDeactivated(java.awt.event.WindowEvent)
2396         */
2397        public void windowDeactivated(WindowEvent e) {
2398            // TODO Auto-generated method stub
2399        }
2400    
2401        /* (non-Javadoc)
2402         * @see java.awt.event.WindowListener#windowDeiconified(java.awt.event.WindowEvent)
2403         */
2404        public void windowDeiconified(WindowEvent e) {
2405            // TODO Auto-generated method stub
2406        }
2407    
2408        /* (non-Javadoc)
2409         * @see java.awt.event.WindowListener#windowIconified(java.awt.event.WindowEvent)
2410         */
2411        public void windowIconified(WindowEvent e) {
2412            // TODO Auto-generated method stub
2413        }
2414    
2415        /* (non-Javadoc)
2416         * @see java.awt.event.WindowListener#windowOpened(java.awt.event.WindowEvent)
2417         */
2418        public void windowOpened(WindowEvent e) {
2419            // TODO Auto-generated method stub
2420        }
2421    
2422            
2423            /**
2424             * Method updateActionStatus.
2425             * <br><b>Summary:</b><br>
2426             * This method permits to update the action enabling status
2427             */
2428            private void updateActionStatus(){
2429                    //File dependant Actions.
2430                    boolean enableFileDependant = false;
2431                    if(subtitleFile != null){
2432                            enableFileDependant = true;             
2433                    }
2434                    for (int i = 0; i < FILE_OPEN_DEPENDANT_ACTIONS.length; i++) {
2435                            actions.get(FILE_OPEN_DEPENDANT_ACTIONS[i]).setEnabled(enableFileDependant);
2436                    }
2437                    //File changed dependant.
2438                    boolean enableFileChangedDependant = false;
2439                    if(subtitleFile != null && subtitleFile.isFileChanged()){
2440                            enableFileChangedDependant = true;
2441                    }
2442                    for (int i = 0; i < FILE_CHANGED_DEPENDANT_ACTIONS.length; i++) {
2443                            actions.get(FILE_CHANGED_DEPENDANT_ACTIONS[i]).setEnabled(enableFileChangedDependant);
2444                    }
2445                    //Then update actions, that depends on anchors number.
2446                    int anchorsNumber = getAnchorsNumber();
2447                    if(anchorsNumber == 0){
2448                            actions.get(ACTION_KEY_MAGIC_RESYNCHRO).setEnabled(false);
2449                            actions.get(ACTION_KEY_REMOVE_ALL_ANCHORS).setEnabled(false);
2450                    }else if (anchorsNumber < 2){
2451                            //need at least 2 anchors to apply a magic resynchro
2452                            actions.get(ACTION_KEY_MAGIC_RESYNCHRO).setEnabled(false);
2453                            actions.get(ACTION_KEY_REMOVE_ALL_ANCHORS).setEnabled(true);
2454                    }else {
2455                            actions.get(ACTION_KEY_MAGIC_RESYNCHRO).setEnabled(true);
2456                            actions.get(ACTION_KEY_REMOVE_ALL_ANCHORS).setEnabled(true);
2457                    }
2458            }
2459        
2460        /**
2461         * Method getAnchorsNumber.
2462         * <br><b>Summary:</b><br>
2463         * This method returns the number of defined anchors.
2464         * @return  (<b>int</b>)   The number of defined anchors.
2465         */
2466        private int getAnchorsNumber() {
2467                    //the result of the method.
2468            int result = 0;
2469            if(subtitleList != null){
2470                    for(Subtitle subtitle : subtitleList){
2471                            if(subtitle.isAnchored()){
2472                                    result ++;
2473                            }
2474                    }
2475            }
2476            //return the result.
2477            return result;
2478            }
2479    
2480            /**
2481         * Class SubtitleFileTransfertHandler.
2482         * Summary:
2483         * This TransferHanlder check for subtitle file drag and drop.
2484         * It permits to open Subtitle file that are draged from OS's location, such as 
2485         * Navigator.
2486         */
2487        protected class SubtitleFileTransfertHandler extends TransferHandler {
2488            private static final long serialVersionUID = 5833961095020955565L;
2489    
2490            public SubtitleFileTransfertHandler() {
2491                super();
2492            }
2493    
2494            /* (non-Javadoc)
2495             * @see javax.swing.TransferHandler#canImport(javax.swing.JComponent, java.awt.datatransfer.DataFlavor[])
2496             */
2497            public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
2498                //Check import available.
2499                //The result of the method.
2500                boolean result = false;
2501                //Parse the array.
2502                for (int i = 0; i < transferFlavors.length; i++) {
2503                    //retrieve current element.
2504                    DataFlavor dataFlavor = transferFlavors[i];
2505                    //We could only import FileList flavor.
2506                    //Which are files to be opened by Sears.
2507                    if (dataFlavor.isFlavorJavaFileListType()) {
2508                        result = true;
2509                    }
2510                }
2511                //return the result.
2512                return result;
2513            }
2514    
2515            /* (non-Javadoc)
2516             * @see javax.swing.TransferHandler#importData(javax.swing.JComponent, java.awt.datatransfer.Transferable)
2517             */
2518            public boolean importData(JComponent comp, Transferable t) {
2519                //the result of the method.
2520                boolean result = false;
2521                try {
2522                    Object list = t.getTransferData(DataFlavor.javaFileListFlavor);
2523                    if (list instanceof List && ((List) list).size() > 0) {
2524                        //Get the first file of the list, and open it.
2525                        File fileToOpen = (File) ((List) list).get(0);
2526                        instance.openFile(fileToOpen);
2527                        result = true;
2528                    }
2529                } catch (UnsupportedFlavorException exception) {
2530                    // TODO Auto-generated catch block
2531                    exception.printStackTrace();
2532                } catch (IOException exception) {
2533                    // TODO Auto-generated catch block
2534                    exception.printStackTrace();
2535                }
2536                //return the result
2537                return result;
2538            }
2539        }
2540    
2541        /**
2542         * Class PlayerTimeHandler.
2543         * Summary:
2544         * This PlayerTimeHandler check for video length and position.
2545         */
2546        private class PlayerTimeHandler implements Runnable, ChangeListener {
2547            private boolean jSliderIsMoving  = false;
2548            private boolean jSliderIsUpdating  = false;
2549            private long updateDate = 0;
2550            private int currentPosition = 0;
2551            private int oldPosition = 0;
2552            private int subtitleFocusIndex = -1;
2553            
2554            /* (non-Javadoc)
2555             * @see java.lang.Runnable#run()
2556             */
2557            public void run() {
2558                    int previousOffset = -1;
2559                    int previousLength = -1;
2560                    while (true) {
2561                            try {
2562                                    Thread.sleep(200);
2563                                    if(player != null) {
2564                                            // get the position and length of video
2565                                            int currentOffset = player.getPosition();
2566                                            if(currentOffset == -1) 
2567                                                    currentOffset = 0;
2568                                            int videoLength = player.getLength();
2569                                            if(videoLength == -1) 
2570                                                    videoLength = 0;
2571                                            // update only if it is not the same position and length
2572                                            if(videoLength != previousLength) {
2573                                                    previousLength = videoLength;
2574                                                    //
2575                                                    // Update the JSlider bar
2576                                                            jSliderIsUpdating = true;
2577                                                            jPanelVideoControler.setJSliderSearchLength(videoLength);
2578                                                            jSliderIsUpdating = false;
2579                                            }
2580                                            if(currentOffset != previousOffset) {
2581                                                    previousOffset = currentOffset;
2582                                                    //
2583                                                    // Update the video position label and slider bar.
2584                                                    if(jSliderIsMoving == false) {
2585                                                            jSliderIsUpdating = true;
2586                                                            jPanelVideoControler.setJSliderSearchPosition(currentOffset);
2587                                                            jSliderIsUpdating = false;
2588                                                    }
2589                                                    //
2590                                                    // Update the current subtitle focus
2591                                                    if((subtitleFile != null) && (jPanelVideoControler.jCheckBoxTrackModeIsSelected())) {
2592                                    Subtitle activeSubtitle = subtitleFile.getSubtitleAtDate((currentOffset+1)*1000);
2593                                                            final int row = activeSubtitle.getNumber() - 1;
2594                                                            if(subtitleFocusIndex !=  row) {
2595                                                                    subtitleFocusIndex = row;
2596                                                                    SwingUtilities.invokeLater(new Runnable(){
2597                                                                                    public void run() {
2598                                                                                            table.changeSelection(row, 0, false, false);
2599                                                                                            table.changeSelection(row+4, 0, true, true);
2600                                                                                    }
2601                                                                    });
2602                                                            }
2603                                                    }
2604                                            }
2605                                            // Update video according to the JSlider
2606                                            if(jSliderIsMoving == true) {
2607                                                    Date date = new Date();
2608                                                    if(((date.getTime() - updateDate) > 1000) && (oldPosition != currentPosition)) {
2609                                                            // set the new reference date
2610                                                            updateDate = date.getTime();
2611                                                            oldPosition = currentPosition;
2612                                                            // Set the new position
2613                                                            try {
2614                                                                    player.setPosition(oldPosition);
2615                                                            } catch (Exception e) {
2616                                                                    Trace.trace("PlayerTimeHandler failed cannot set video position:"+e.getMessage());
2617                                                            }
2618                                                    }
2619                                            }
2620                                    }
2621                            } catch (Exception e) {
2622                                    Trace.trace("PlayerTimeHandler failed:"+e.getMessage());
2623                            }
2624                    }
2625            }
2626            
2627            /* (non-Javadoc)
2628             * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
2629             */
2630            public void stateChanged(ChangeEvent changeEvent) {
2631                    JSlider source = (JSlider)changeEvent.getSource();
2632                    // get the current slider position
2633                    currentPosition = (int)source.getValue();
2634                    jSliderIsMoving = true;
2635                    // slider stops moving
2636                    if (!source.getValueIsAdjusting()) {
2637                            jSliderIsMoving = false;
2638                            if((jSliderIsUpdating == false) && (oldPosition != currentPosition)) {
2639                                    // Set the new position
2640                                    try {
2641                                            player.setPosition(currentPosition);
2642                                    } catch (Exception e) {
2643                                            Trace.trace("PlayerTimeHandler failed cannot set video position:"+e.getMessage());
2644                                    }
2645                            }
2646                    }
2647                    // update the video time
2648                    jPanelVideoControler.setJLabelTime(Utils.formatTime(currentPosition));
2649            }
2650        }
2651    }