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