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