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