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 //some suggestions about this class: floriaen@gmail.com 019 020 package sears.gui; 021 022 import java.awt.BorderLayout; 023 import java.awt.Container; 024 import java.awt.Dimension; 025 import java.awt.Font; 026 import java.awt.GridLayout; 027 import java.awt.event.ActionEvent; 028 import java.awt.event.ActionListener; 029 import java.awt.event.WindowAdapter; 030 import java.awt.event.WindowEvent; 031 import java.nio.charset.Charset; 032 import java.util.Iterator; 033 034 import javax.swing.BorderFactory; 035 import javax.swing.BoxLayout; 036 import javax.swing.Icon; 037 import javax.swing.JButton; 038 import javax.swing.JComboBox; 039 import javax.swing.JLabel; 040 import javax.swing.JPanel; 041 import javax.swing.JScrollPane; 042 import javax.swing.JTextArea; 043 import javax.swing.SwingConstants; 044 import javax.swing.SwingUtilities; 045 import javax.swing.UIManager; 046 047 import sears.file.SubtitleFile; 048 import sears.gui.resources.SearsResources; 049 import sears.tools.Utils; 050 051 /** 052 * <p> 053 * <tt>ErrorDialog</tt> allows to display 3 things: 054 * <ul> 055 * <li>A title to display a general message to alert user</li> 056 * <li>A message error which describes the error</li> 057 * <li>A text area where is printed error's details</li> 058 * <li>A panel with a combo box to let user choose another encoding</li> 059 * </ul> 060 * </p> 061 * <p> 062 * <tt>ErrorDialog</tt> provides static method for access to generics error dialog: 063 * <br><tt># showOpenErrorDialog(String, String, boolean)</tt> 064 * <br><tt># showSaveErrorDialog(String, String)</tt> 065 * 066 */ 067 public class ErrorDialog extends SearsJDialog { 068 private static final long serialVersionUID = -8640938815129486410L; 069 070 /** this font is used for all the dialog's components */ 071 public static final Font DEFAULT_FONT = UIManager.getFont("Label.font"); 072 /** derived font of <tt>DEFAULT_FONT</tt> */ 073 public static final Font BIG_FONT = DEFAULT_FONT.deriveFont(13f); 074 /** derived font of <tt>DEFAULT_FONT</tt> */ 075 public static final Font MIDDLE_FONT = DEFAULT_FONT.deriveFont(12f); 076 /** derived font of <tt>DEFAULT_FONT</tt> */ 077 public static final Font SMALL_FONT = DEFAULT_FONT.deriveFont(11f); 078 079 private static final int MINIMUM_HEIGHT = 180; 080 private static final int MINIMUM_WIDTH = 300; 081 082 // MESSAGE PANEL 083 private JPanel messagePanel; 084 private JLabel mainTitle; 085 private JLabel errorMessageLabel; 086 private ArrowButton arrowButton; 087 088 // DETAIL AREA 089 private JPanel detailPanel; 090 private JScrollPane scrollDetailPane; 091 private JTextArea errorDetailArea; 092 093 // CHARSET PANEL 094 private JPanel charsetPanel; 095 private JTextArea charsetMessageArea; 096 private JComboBox charsetComboBox; 097 098 // an instance used by the static method 099 private static ErrorDialog openErrorDialog; 100 101 /** 102 * Constructs a new instance of <tt>ErrorDialog</tt> 103 * <br>A preferred size is set as default 104 */ 105 public ErrorDialog() { 106 super("Error"); 107 //this.setPreferredSize(new Dimension(200, 300)); 108 } 109 110 /** 111 * Redefines super method, add components to the dialog before 112 * compute the it size :D 113 */ 114 protected void configureSize() { 115 // DEVELOPER: 116 // the super method #configureSize() calls the pack() method if there's no saved sizes. 117 // The problem resides in the fact that there's not all the components added when this method 118 // is called, so pack() method result is not good for the view... 119 // 120 //this.setMinimumSize(new Dimension(372,332)); 121 initComponents(); 122 setSize(MINIMUM_WIDTH, MINIMUM_HEIGHT); 123 setLocationRelativeTo(MainWindow.instance); 124 //super.configureSize(); 125 } 126 127 /* 128 * (non-Javadoc) 129 * @see java.awt.Component#setVisible(boolean) 130 */ 131 public void setVisible(boolean aFlag) { 132 if( aFlag == true ) { 133 setSize(MINIMUM_WIDTH, MINIMUM_HEIGHT); 134 } 135 super.setVisible(aFlag); 136 } 137 138 /** 139 * Initializes all the dialog's components 140 */ 141 private void initComponents() { 142 Container contentPane = getContentPane(); 143 contentPane.setLayout(new BorderLayout()); 144 contentPane.add(getMessagePanel(), BorderLayout.NORTH); 145 contentPane.add(getDetailPanel(), BorderLayout.CENTER); 146 contentPane.add(getCharsetPanel(), BorderLayout.SOUTH); 147 } 148 149 /** 150 * Constructs if needed and returns the message panel. 151 * <br> This panel contains two <tt>JLabel</tt> object: 152 * <ul> 153 * <li>one to describe to the user what happens</li> 154 * <li>the other to display which error occurs</li> 155 * </ul> 156 * @return the message panel 157 */ 158 private JPanel getMessagePanel() { 159 if( messagePanel == null ) { 160 messagePanel = new JPanel(); 161 messagePanel.setLayout(new GridLayout(2,0)); 162 messagePanel.setBorder(BorderFactory.createEmptyBorder(10, 4, 6, 4)); 163 // ERROR MESSAGE 164 errorMessageLabel = new JLabel(); 165 errorMessageLabel.setFont(MIDDLE_FONT); 166 errorMessageLabel.setHorizontalTextPosition(SwingConstants.LEFT); 167 // MAIN TITLE 168 mainTitle = new JLabel(); 169 Icon errorIcon = SearsResources.getIcon("ErrorIcon"); 170 mainTitle.setIconTextGap(5); 171 mainTitle.setIcon(errorIcon); 172 mainTitle.setFont(BIG_FONT); 173 // ADD LABELS TO PANEL 174 messagePanel.add(mainTitle); 175 //messagePanel.add(errorMessageLabel); 176 messagePanel.add(getArrowButton()); 177 } 178 return messagePanel; 179 } 180 181 /** 182 * Constructs if needed and returns the "detail panel" 183 * <br>It is composed by an area where is displayed text 184 * @return the panel 185 */ 186 private JPanel getDetailPanel() { 187 if( detailPanel == null ) { 188 detailPanel = new JPanel(); 189 detailPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 190 scrollDetailPane = new JScrollPane(getErrorDetailArea()); 191 scrollDetailPane.setPreferredSize(new Dimension(200,100)); 192 detailPanel.setLayout(new GridLayout(1,0)); 193 detailPanel.add(scrollDetailPane); 194 } 195 return detailPanel; 196 } 197 198 // get the arrow button 199 private JButton getArrowButton() { 200 arrowButton = new ArrowButton("details"); 201 arrowButton.setHorizontalAlignment(SwingConstants.LEFT); 202 arrowButton.setFont(MIDDLE_FONT); 203 arrowButton.setToolTipText("click to show or hide error details"); 204 arrowButton.addActionListener(new ActionListener() { 205 public void actionPerformed(ActionEvent e) { 206 setDetailPanelVisible(!arrowButton.isTurnOn()); 207 } 208 }); 209 return arrowButton; 210 } 211 212 /** 213 * Used by the arrow button to show or hide the detail panel 214 * @param visible true for show, false for hide 215 */ 216 protected void setDetailPanelVisible(boolean visible) { 217 if( visible && !detailPanel.isVisible() ) { 218 detailPanel.setVisible(true); 219 int detailPanelHeigth = detailPanel.getHeight(); 220 if( detailPanelHeigth == 0 ) { 221 detailPanelHeigth = 100; 222 } 223 this.setSize(this.getWidth(), this.getHeight() + detailPanelHeigth); 224 this.repaint(); 225 } else if( !visible && detailPanel.isVisible() ) { 226 detailPanel.setVisible(false); 227 this.setSize(this.getWidth(), this.getHeight() - detailPanel.getHeight()); 228 this.repaint(); 229 } 230 } 231 232 /** 233 * Constructs if needed and returns a non-editable <tt>JTextArea</tt> object. 234 * <br>Use for display error messages 235 * @return the error message area 236 */ 237 private JTextArea getErrorDetailArea() { 238 if( errorDetailArea == null ) { 239 errorDetailArea = new JTextArea(); 240 errorDetailArea.setEditable(false); 241 errorDetailArea.setBorder(BorderFactory.createEmptyBorder(2,4,2,4)); 242 errorDetailArea.setFont(SMALL_FONT); 243 } 244 return errorDetailArea; 245 } 246 247 /** 248 * Constructs if needed and returns the "charset panel" composed by labels and a combo box 249 * <br>to give tu user the possibility to choose another charset encoding/decoding 250 * @return the panel 251 */ 252 private JPanel getCharsetPanel() { 253 if( charsetPanel == null ) { 254 charsetPanel = new JPanel(); 255 charsetPanel.setBorder(BorderFactory.createEmptyBorder(6, 4, 6, 18)); 256 257 charsetMessageArea = new JTextArea(); 258 charsetMessageArea.setText( 259 "A wrong charset might be the error origin." 260 + Utils.LINE_SEPARATOR 261 + "You can try to reopen the file with the" 262 + Utils.LINE_SEPARATOR 263 + "charset of your choice:"); 264 265 charsetMessageArea.setFont(SMALL_FONT); 266 charsetMessageArea.setEditable(false); 267 charsetMessageArea.setBorder(BorderFactory.createEmptyBorder(2,4,2,4)); 268 charsetMessageArea.setBackground(charsetPanel.getBackground()); 269 270 charsetComboBox = new JComboBox(); 271 charsetComboBox.setFont(MIDDLE_FONT); 272 fillCharsetComboBox(); 273 charsetComboBox.addActionListener(new ActionListener() { 274 public void actionPerformed(ActionEvent e) { 275 String selectedCharset = (String) charsetComboBox.getSelectedItem(); 276 charset = selectedCharset; 277 // closed and dispose window dialog, 278 // call to super method from SearsJDialog 279 okAction(); 280 } 281 }); 282 283 charsetMessageArea.setAlignmentX(LEFT_ALIGNMENT); 284 charsetComboBox.setAlignmentX(LEFT_ALIGNMENT); 285 286 // adding components: 287 charsetPanel.setLayout(new BoxLayout(charsetPanel, BoxLayout.Y_AXIS)); 288 charsetPanel.add(charsetMessageArea); 289 charsetPanel.add(charsetComboBox); 290 } 291 return charsetPanel; 292 } 293 294 // NullPointerException if charsetComboBox is null 295 private void fillCharsetComboBox() { 296 String[] searsCharset = SubtitleFile.BASIC_CHARSETS; 297 Iterator<Charset> availableCharsetsIt = Charset.availableCharsets().values().iterator(); 298 while( availableCharsetsIt.hasNext() ) { 299 String charset = availableCharsetsIt.next().displayName(); 300 //charsetComboBox.addItem(charset); 301 for( int i=0;i<searsCharset.length;i++ ) { 302 if( charset.equals(searsCharset[i])) { 303 charset = null; 304 // exit the loop for: 305 i = searsCharset.length; 306 } 307 } 308 if( charset != null ) { 309 charsetComboBox.addItem(charset); 310 } 311 } 312 } 313 314 /** 315 * Indicates if the "charset panel" must be visible or not 316 * @param aFlag if true, "charset panel" will be visible. 317 */ 318 public void setCharsetSelectionVisible(boolean aFlag) { 319 charsetPanel.setVisible(aFlag); 320 } 321 322 /** 323 * Sets the main title 324 * @param title the main title to set 325 */ 326 public void setMainTitle(String title) { 327 mainTitle.setText(title); 328 } 329 330 /** 331 * Sets a message error 332 * @param message the message error to set 333 */ 334 public void setErrorMessage(String message) { 335 //errorMessageLabel.setText("> " + message); 336 arrowButton.setText(message); 337 } 338 339 /** 340 * Sets a detail error 341 * @param detail the detail error to set 342 */ 343 public void setErrorDetail(String detail) { 344 // use line separator for a good display of the JScollBar 345 errorDetailArea.setText(detail + Utils.LINE_SEPARATOR); 346 } 347 348 /** 349 * Appends text to the actual message error 350 * @param detail the text to append 351 */ 352 public void appendErrorDetail(String detail) { 353 String textArea = errorDetailArea.getText(); 354 errorDetailArea.setText(textArea + detail); 355 } 356 357 /** 358 * Clears the error detail 359 */ 360 public void clearErrorDetail() { 361 errorDetailArea.setText(null); 362 } 363 364 /* 365 * (non-Javadoc) 366 * @see sears.gui.SearsJDialog#getDialogName() 367 */ 368 protected String getDialogName() { 369 return "error"; 370 } 371 372 public void windowClosed(WindowEvent e) { 373 //super.windowClosed(e); 374 arrowButton.turnOff(); 375 } 376 377 // 378 // STATIC FACTORY METHODS: 379 // 380 381 // used to return result of user action on error dialog 382 private static String charset = SubtitleFile.DEFAULT_CHARSET; 383 384 /** 385 * Shows a generic error dialog when file is opened 386 * @param message the message 387 * @param detail the error detail 388 * @param reOpenChoice if there's a way to resolve the error with a different charset 389 * @return the charset choosed by the user or null if not 390 */ 391 public static String showOpenErrorDialog(String message, String detail, boolean reOpenChoice) { 392 if( openErrorDialog == null ) { 393 openErrorDialog = new ErrorDialog(); 394 } 395 openErrorDialog.setMainTitle("Sears could'nt open file"); 396 openErrorDialog.setErrorMessage(message); 397 openErrorDialog.setErrorDetail(detail); 398 openErrorDialog.setCharsetSelectionVisible(reOpenChoice); 399 openErrorDialog.setDetailPanelVisible(false); 400 401 if( SwingUtilities.isEventDispatchThread() ) { 402 openErrorDialog.addWindowListener(new WindowAdapter() { 403 public void windowClosing(WindowEvent e) { 404 charset = null; 405 } 406 }); 407 openErrorDialog.setVisible(true); 408 } else { 409 try { 410 SwingUtilities.invokeAndWait(new Runnable() { 411 public void run() { 412 openErrorDialog.addWindowListener(new WindowAdapter() { 413 public void windowClosing(WindowEvent e) { 414 charset = null; 415 } 416 }); 417 openErrorDialog.setVisible(true); 418 } 419 }); 420 } catch (java.lang.reflect.InvocationTargetException e) { 421 e.printStackTrace(); 422 } catch (java.lang.InterruptedException e) { 423 e.printStackTrace(); 424 } 425 } 426 return charset; 427 } 428 429 /** 430 * Shows a generic error dialog when file is saved 431 * @param message the message 432 * @param detail the error detail 433 */ 434 public static void showSaveErrorDialog(String message, String detail) { 435 if( openErrorDialog == null ) { 436 openErrorDialog = new ErrorDialog(); 437 } 438 439 openErrorDialog.setMainTitle("Sears could'nt save the file"); 440 openErrorDialog.setErrorMessage(message); 441 openErrorDialog.setErrorDetail(detail); 442 openErrorDialog.setCharsetSelectionVisible(false); 443 openErrorDialog.setDetailPanelVisible(false); 444 445 if( SwingUtilities.isEventDispatchThread() ) { 446 openErrorDialog.setVisible(true); 447 } else { 448 try { 449 SwingUtilities.invokeAndWait(new Runnable() { 450 public void run() { 451 openErrorDialog.setVisible(true); 452 } 453 }); 454 } catch (java.lang.reflect.InvocationTargetException e) { 455 e.printStackTrace(); 456 } catch (java.lang.InterruptedException e) { 457 e.printStackTrace(); 458 } 459 } 460 } 461 }