001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2015-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.ui; 018 019import static org.opends.messages.AdminToolMessages.*; 020import static org.opends.messages.QuickSetupMessages.INFO_CLOSE_BUTTON_LABEL; 021 022import java.awt.Component; 023import java.awt.Dimension; 024import java.awt.GridBagConstraints; 025import java.awt.GridBagLayout; 026import java.awt.Insets; 027import java.awt.Window; 028import java.awt.event.ActionEvent; 029import java.awt.event.ActionListener; 030 031import javax.swing.BorderFactory; 032import javax.swing.Box; 033import javax.swing.JButton; 034import javax.swing.JCheckBox; 035import javax.swing.JEditorPane; 036import javax.swing.JFrame; 037import javax.swing.JPanel; 038import javax.swing.JProgressBar; 039import javax.swing.JScrollPane; 040import javax.swing.SwingUtilities; 041import javax.swing.text.html.HTMLDocument; 042 043import org.forgerock.i18n.LocalizableMessage; 044import org.opends.guitools.controlpanel.datamodel.ControlPanelInfo; 045import org.opends.guitools.controlpanel.event.ConfigurationChangeEvent; 046import org.opends.guitools.controlpanel.event.PrintStreamListener; 047import org.opends.guitools.controlpanel.ui.components.BasicExpander; 048import org.opends.guitools.controlpanel.util.ApplicationPrintStream; 049import org.opends.guitools.controlpanel.util.Utilities; 050 051/** The dialog that is used to display progress in a task. */ 052public class ProgressDialog extends GenericDialog 053{ 054 private static final long serialVersionUID = -6462866257463062629L; 055 private ProgressPanel progressPanel; 056 057 /** 058 * Constructor of the dialog. 059 * @param parentFrame the parent frame. 060 * @param relativeTo the component to use as reference to set the position 061 * of this dialog. 062 * @param title the title of the dialog. 063 * @param info the control panel information. 064 */ 065 public ProgressDialog(JFrame parentFrame, Component relativeTo, 066 LocalizableMessage title, ControlPanelInfo info) 067 { 068 super(parentFrame, getPanel(info)); 069 Utilities.centerGoldenMean(this, relativeTo); 070 setTitle(title.toString()); 071 progressPanel = (ProgressPanel)panel; 072 getRootPane().setDefaultButton(progressPanel.closeButton); 073 } 074 075 /** 076 * Creates the panel that will be contained in the dialog. 077 * @param info the control panel information. 078 * @return the panel that will be contained in the dialog. 079 */ 080 private static StatusGenericPanel getPanel(ControlPanelInfo info) 081 { 082 ProgressPanel panel = new ProgressPanel(); 083 panel.setInfo(info); 084 return panel; 085 } 086 087 /** 088 * Adds two print stream listeners. 089 * @param outPrintStream the output stream listener. 090 * @param errorPrintStream the error stream listener. 091 */ 092 public void addPrintStreamListeners(ApplicationPrintStream outPrintStream, 093 ApplicationPrintStream errorPrintStream) 094 { 095 errorPrintStream.addListener(new PrintStreamListener() 096 { 097 @Override 098 public void newLine(final String msg) 099 { 100 SwingUtilities.invokeLater(new Runnable() 101 { 102 @Override 103 public void run() 104 { 105 progressPanel.appendErrorLine(msg); 106 } 107 }); 108 } 109 }); 110 outPrintStream.addListener(new PrintStreamListener() 111 { 112 @Override 113 public void newLine(final String msg) 114 { 115 SwingUtilities.invokeLater(new Runnable() 116 { 117 @Override 118 public void run() 119 { 120 progressPanel.appendOutputLine(msg); 121 } 122 }); 123 } 124 }); 125 } 126 127 /** 128 * Returns the progress bar of the dialog. 129 * @return the progress bar of the dialog. 130 */ 131 public JProgressBar getProgressBar() 132 { 133 return progressPanel.getProgressBar(); 134 } 135 136 /** 137 * Appends some text in HTML format to the 'Details' section of the dialog. 138 * @param text the text in HTML format to be appended. 139 */ 140 public void appendProgressHtml(String text) 141 { 142 progressPanel.appendHtml(text); 143 } 144 145 /** Resets the contents of the 'Details' section of the dialog. */ 146 public void resetProgressLogs() 147 { 148 progressPanel.resetLogs(); 149 } 150 151 /** 152 * Sets the text to be displayed in the summary area of the progress 153 * dialog. 154 * @param text the text to be displayed. 155 */ 156 public void setSummary(LocalizableMessage text) 157 { 158 progressPanel.setSummary(text); 159 } 160 161 @Override 162 public void setEnabledClose(boolean enable) 163 { 164 progressPanel.closeButton.setEnabled(enable); 165 } 166 167 /** 168 * Note: this will make the dialog to be closed asynchronously. So that 169 * sequential calls to setTaskIsOver(true) and setTaskIsOver(false) on the 170 * event thread are guaranteed not to close the dialog. 171 * @param taskIsOver whether the task is finished or not. 172 */ 173 public void setTaskIsOver(boolean taskIsOver) 174 { 175 progressPanel.taskIsOver = taskIsOver; 176 progressPanel.closeWhenOverClicked(); 177 } 178 179 /** The panel contained in the progress dialog. */ 180 private static class ProgressPanel extends StatusGenericPanel 181 { 182 private static final long serialVersionUID = -364496083928260306L; 183 private BasicExpander details; 184 private JEditorPane logs; 185 private JScrollPane scroll; 186 private JCheckBox closeWhenOver; 187 private final String LASTID = "lastid"; 188 private final String INIT_TEXT = "<span id=\""+LASTID+ 189 "\" style=\"bold\"> </span>"; 190 private JProgressBar progressBar; 191 private Component extraStrut; 192 private JButton closeButton; 193 private static final String FAKE_PROGRESS_TEXT = 194 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ 195 "<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>"+ 196 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; 197 private int heightDiff; 198 private int lastCollapsedHeight = -1; 199 private int lastExpandedHeight = -1; 200 201 private static boolean lastShowDetails; 202 private static boolean lastCloseWhenOver; 203 204 private boolean taskIsOver; 205 206 /** Default constructor. */ 207 public ProgressPanel() 208 { 209 super(); 210 createLayout(); 211 } 212 213 @Override 214 public LocalizableMessage getTitle() 215 { 216 return null; 217 } 218 219 @Override 220 public boolean requiresScroll() 221 { 222 return false; 223 } 224 225 @Override 226 public boolean requiresBorder() 227 { 228 return false; 229 } 230 231 @Override 232 public boolean isDisposeOnClose() 233 { 234 return true; 235 } 236 237 /** 238 * Appends a line to the logs (Details are) section of the panel. The text 239 * will have a new-line char at the end (is similar to println()). 240 * @param msg the HTML formatted text to be appended. 241 */ 242 private void appendErrorLine(String msg) 243 { 244 msg = filterForBugID4988885(msg+"<br>"); 245 msg = Utilities.applyFont(msg, ColorAndFontConstants.progressFont); 246 appendHtml(msg); 247 } 248 249 /** 250 * Sets the text to be displayed in the summary area of the progress 251 * dialog. 252 * @param msg the text to be displayed. 253 */ 254 public void setSummary(LocalizableMessage msg) 255 { 256 errorPane.setText(msg.toString()); 257 258 if (!details.isSelected() && isVisible()) 259 { 260 LocalizableMessage wrappedText = Utilities.wrapHTML(msg, 70); 261 JEditorPane pane = new JEditorPane(); 262 pane.setContentType("text/html"); 263 pane.setText(wrappedText.toString()); 264 ProgressDialog dlg = (ProgressDialog)Utilities.getParentDialog(this); 265 int width = Math.max(pane.getPreferredSize().width + 40, 266 dlg.getWidth()); 267 int height = Math.max(pane.getPreferredSize().height + 40 + 268 extraStrut.getHeight() + details.getPreferredSize().height, 269 dlg.getHeight()); 270 // We might want to resize things. 271 if (width > dlg.getWidth() || height > dlg.getHeight()) 272 { 273 Dimension newDim = new Dimension(width, height); 274 dlg.setSize(newDim); 275 } 276 } 277 } 278 279 /** 280 * Appends a line to the logs (Details are) section of the panel. The text 281 * will be preceded by a new line (is similar to println()). 282 * @param msg the HTML formatted text to be appended. 283 */ 284 private void appendOutputLine(String msg) 285 { 286 appendErrorLine(msg); 287 } 288 289 /** 290 * Appends text to the logs (Details are) section of the panel. The text 291 * will be appended as it is (is similar to print()). 292 * @param msg the HTML formatted text to be appended. 293 */ 294 private void appendHtml(String msg) 295 { 296 HTMLDocument doc = (HTMLDocument)logs.getDocument(); 297 298 try 299 { 300 msg = filterForBugID4988885(msg); 301 doc.insertBeforeStart(doc.getElement(LASTID), msg); 302 } 303 catch (Throwable t) 304 { 305 // Bug 306 t.printStackTrace(); 307 } 308 } 309 310 /** Resets the contents of the logs (Details) section. */ 311 private void resetLogs() 312 { 313 logs.setText(INIT_TEXT); 314 } 315 316 /** Creates the layout of the panel (but the contents are not populated here). */ 317 private void createLayout() 318 { 319 GridBagConstraints gbc = new GridBagConstraints(); 320 addErrorPane(gbc); 321 322 errorPane.setVisible(true); 323 errorPane.setText(Utilities.applyFont( 324 INFO_CTRL_PANEL_PLEASE_WAIT_SUMMARY.get(), 325 ColorAndFontConstants.defaultFont)); 326 327 gbc.anchor = GridBagConstraints.WEST; 328 gbc.gridwidth = 1; 329 gbc.gridx = 0; 330 gbc.gridy = 1; 331 332 progressBar = new JProgressBar(); 333 progressBar.setMaximum(100); 334 gbc.weightx = 1.0; 335 gbc.fill = GridBagConstraints.HORIZONTAL; 336 gbc.insets = new Insets(10, 20, 0, 30); 337 add(progressBar, gbc); 338 339 gbc.insets.top = 10; 340 gbc.insets.bottom = 5; 341 details = 342 new BasicExpander(INFO_CTRL_PANEL_PROGRESS_DIALOG_DETAILS_LABEL.get()); 343 gbc.gridy ++; 344 add(details, gbc); 345 346 logs = Utilities.makeHtmlPane(FAKE_PROGRESS_TEXT, 347 ColorAndFontConstants.progressFont); 348 gbc.gridy ++; 349 gbc.weighty = 1.0; 350 gbc.fill = GridBagConstraints.BOTH; 351 gbc.insets.top = 5; 352 gbc.insets.right = 20; 353 gbc.insets.bottom = 5; 354 scroll = Utilities.createScrollPane(logs); 355 scroll.setOpaque(false); 356 scroll.getViewport().setOpaque(false); 357 add(scroll, gbc); 358 Dimension scrollDim = scroll.getPreferredSize(); 359 360 gbc.weighty = 1.0; 361 extraStrut = Box.createRigidArea(new Dimension(scrollDim.width, 50)); 362 add(extraStrut, gbc); 363 gbc.gridy ++; 364 gbc.weighty = 0.0; 365 add(Box.createHorizontalStrut(scrollDim.width), gbc); 366 367 heightDiff = scrollDim.height - extraStrut.getHeight(); 368 369 logs.setText(INIT_TEXT); 370 371 scroll.setPreferredSize(scrollDim); 372 373 updateVisibility(lastShowDetails); 374 details.addActionListener(new ActionListener() 375 { 376 @Override 377 public void actionPerformed(ActionEvent ev) 378 { 379 lastShowDetails = details.isSelected(); 380 updateVisibility(lastShowDetails); 381 } 382 }); 383 384 // The button panel 385 gbc.gridy ++; 386 gbc.weighty = 0.0; 387 gbc.insets = new Insets(0, 0, 0, 0); 388 add(createButtonsPanel(), gbc); 389 } 390 391 private JPanel createButtonsPanel() 392 { 393 JPanel buttonsPanel = new JPanel(new GridBagLayout()); 394 GridBagConstraints gbc = new GridBagConstraints(); 395 gbc.gridx = 0; 396 gbc.gridy = 0; 397 gbc.anchor = GridBagConstraints.WEST; 398 gbc.fill = GridBagConstraints.HORIZONTAL; 399 gbc.gridwidth = 1; 400 gbc.gridy = 0; 401 closeWhenOver = Utilities.createCheckBox( 402 INFO_CTRL_PANEL_CLOSE_WINDOW_WHEN_OPERATION_COMPLETES_LABEL.get()); 403 closeWhenOver.setOpaque(false); 404 closeWhenOver.addActionListener(new ActionListener() 405 { 406 @Override 407 public void actionPerformed(ActionEvent ev) 408 { 409 closeWhenOverClicked(); 410 } 411 }); 412 closeWhenOver.setSelected(lastCloseWhenOver); 413 gbc.insets = new Insets(10, 10, 10, 10); 414 buttonsPanel.add(closeWhenOver, gbc); 415 gbc.weightx = 1.0; 416 gbc.gridx ++; 417 buttonsPanel.add(Box.createHorizontalStrut(150)); 418 buttonsPanel.add(Box.createHorizontalGlue(), gbc); 419 buttonsPanel.setOpaque(true); 420 buttonsPanel.setBackground(ColorAndFontConstants.greyBackground); 421 gbc.gridx ++; 422 gbc.weightx = 0.0; 423 buttonsPanel.add(Box.createHorizontalStrut(100)); 424 gbc.gridx ++; 425 closeButton = Utilities.createButton(INFO_CLOSE_BUTTON_LABEL.get()); 426 closeButton.setOpaque(false); 427 gbc.gridx ++; 428 gbc.insets.left = 5; 429 gbc.insets.right = 10; 430 buttonsPanel.add(closeButton, gbc); 431 closeButton.addActionListener(new ActionListener() 432 { 433 @Override 434 public void actionPerformed(ActionEvent ev) 435 { 436 closeClicked(); 437 } 438 }); 439 440 buttonsPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, 441 ColorAndFontConstants.defaultBorderColor)); 442 443 return buttonsPanel; 444 } 445 446 private void updateVisibility(boolean showDetails) 447 { 448 scroll.setVisible(showDetails); 449 extraStrut.setVisible(!showDetails); 450 details.setSelected(showDetails); 451 452 final Window dialog = Utilities.getParentDialog(this); 453 if (dialog != null) 454 { 455 final Runnable repaint = new Runnable() 456 { 457 @Override 458 public void run() 459 { 460 invalidate(); 461 dialog.invalidate(); 462 dialog.repaint(); 463 } 464 }; 465 466 final Dimension dialogSize = dialog.getSize(); 467 if (showDetails) 468 { 469 lastCollapsedHeight = dialogSize.height; 470 if (lastExpandedHeight == -1) 471 { 472 dialog.setSize(new Dimension(dialogSize.width, dialogSize.height + heightDiff)); 473 } 474 else 475 { 476 dialog.setSize(new Dimension(dialogSize.width, lastExpandedHeight)); 477 } 478 SwingUtilities.invokeLater(repaint); 479 } 480 else 481 { 482 lastExpandedHeight = dialogSize.height; 483 if (lastCollapsedHeight == -1) 484 { 485 packParentDialog(); 486 } 487 else 488 { 489 dialog.setSize(new Dimension(dialogSize.width, lastCollapsedHeight)); 490 SwingUtilities.invokeLater(repaint); 491 } 492 } 493 } 494 } 495 496 @Override 497 public GenericDialog.ButtonType getButtonType() 498 { 499 return GenericDialog.ButtonType.NO_BUTTON; 500 } 501 502 @Override 503 public void configurationChanged(ConfigurationChangeEvent ev) 504 { 505 } 506 507 @Override 508 public Component getPreferredFocusComponent() 509 { 510 return details; 511 } 512 513 @Override 514 public void okClicked() 515 { 516 Utilities.getParentDialog(this).setVisible(false); 517 } 518 519 /** 520 * Returns the progress bar of the dialog. 521 * @return the progress bar of the dialog. 522 */ 523 public JProgressBar getProgressBar() 524 { 525 return progressBar; 526 } 527 528 /** 529 * Checks if the 'Close when over' check box is selected and if it is the 530 * case, closes the dialog after waiting for 2 seconds (so that the user 531 * can see the result, or cancel the automatic closing of the dialog). 532 */ 533 private void closeWhenOverClicked() 534 { 535 lastCloseWhenOver = closeWhenOver.isSelected(); 536 if (lastCloseWhenOver && taskIsOver) 537 { 538 Thread t = new Thread(new Runnable() 539 { 540 @Override 541 public void run() 542 { 543 try 544 { 545 Thread.sleep(2000); 546 SwingUtilities.invokeLater(new Runnable() 547 { 548 @Override 549 public void run() 550 { 551 if (closeWhenOver.isSelected() && taskIsOver) 552 { 553 closeClicked(); 554 } 555 } 556 }); 557 } 558 catch (Throwable t) 559 { 560 } 561 } 562 }); 563 t.start(); 564 } 565 } 566 } 567 568 /** 569 * This is necessary because of bug 4988885. 570 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4988885 571 * @param msg the message. 572 * @return the message filtered. 573 */ 574 private static String filterForBugID4988885(String msg) 575 { 576 return msg.replaceAll("<br>", " <br>"); 577 } 578}