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 }