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