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 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027 028package org.opends.quicksetup.ui; 029 030import java.awt.event.WindowAdapter; 031import java.awt.event.WindowEvent; 032import java.util.HashSet; 033import org.forgerock.i18n.LocalizableMessage; 034import org.forgerock.i18n.slf4j.LocalizedLogger; 035 036 037import javax.swing.JButton; 038import javax.swing.JFrame; 039import javax.swing.JPanel; 040import javax.swing.SwingUtilities; 041import javax.swing.WindowConstants; 042 043import org.opends.quicksetup.*; 044import org.opends.quicksetup.event.ButtonActionListener; 045import org.opends.quicksetup.event.ButtonEvent; 046import org.opends.quicksetup.event.MinimumSizeComponentListener; 047import org.opends.quicksetup.ProgressDescriptor; 048/** 049 * This class represents the dialog used by quicksetup applications. 050 * 051 * In its constructor it gets as parameters an object describing the current 052 * installation status and the default values to be proposed to the user 053 * in the panels. 054 * 055 * If we are installing Open DS and the server has already been installed it 056 * will display an error message. In the other cases it will display a wizard. 057 * 058 */ 059public class QuickSetupDialog 060{ 061 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 062 063 private JFrame frame; 064 private QuickSetupErrorPanel installedPanel; 065 private JPanel framePanel; 066 private StepsPanel stepsPanel; 067 private CurrentStepPanel currentStepPanel; 068 private ButtonsPanel buttonsPanel; 069 070 private WizardStep displayedStep; 071 072 private CurrentInstallStatus installStatus; 073 074 private HashSet<ButtonActionListener> buttonListeners = new HashSet<>(); 075 076 private GuiApplication application; 077 078 private QuickSetup quickSetup; 079 080 private boolean forceToDisplay; 081 082 /** 083 * Constructor of QuickSetupDialog. 084 * @param app Application to run in as a wizard 085 * @param installStatus of the current environment 086 * @param qs QuickSetup acting as controller 087 */ 088 public QuickSetupDialog(GuiApplication app, 089 CurrentInstallStatus installStatus, 090 QuickSetup qs) 091 { 092 if (app == null) { 093 throw new IllegalArgumentException("application cannot be null"); 094 } 095 this.application = app; 096 this.installStatus = installStatus; 097 this.quickSetup = qs; 098 frame = new JFrame(String.valueOf(application.getFrameTitle())); 099 frame.getContentPane().add(getFramePanel()); 100 frame.addWindowListener(new WindowAdapter() { 101 public void windowClosing(WindowEvent e) { 102 application.windowClosing(QuickSetupDialog.this, e); 103 } 104 }); 105 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 106 Utilities.setFrameIcon(frame); 107 } 108 109 /** 110 * Packs and displays this dialog. 111 * 112 */ 113 public void packAndShow() 114 { 115 frame.pack(); 116 int minWidth = (int) frame.getPreferredSize().getWidth(); 117 int minHeight = (int) frame.getPreferredSize().getHeight(); 118 Utilities.centerOnScreen(frame); 119 setFocusOnButton(application.getInitialFocusButtonName()); 120 frame.addComponentListener(new MinimumSizeComponentListener(frame, 121 minWidth, minHeight)); 122 123 frame.setVisible(true); 124 } 125 126 /** 127 * This method is called when we detected that there is something installed 128 * we inform of this to the user and the user wants to proceed with the 129 * installation destroying the contents of the data and the configuration 130 * in the current installation. 131 */ 132 public void forceToDisplay() 133 { 134 this.forceToDisplay = true; 135 framePanel = null; 136 frame.getContentPane().removeAll(); 137 frame.getContentPane().add(getFramePanel()); 138 frame.pack(); 139 Utilities.centerOnScreen(frame); 140 setFocusOnButton(ButtonName.NEXT); 141 } 142 143 /** 144 * Displays the panel corresponding to the provided step. The panel contents 145 * are updated with the contents of the UserData object. 146 * @param step the step that we want to display. 147 * @param userData the UserData object that must be used to populate 148 * the panels. 149 */ 150 public void setDisplayedStep(WizardStep step, UserData userData) 151 { 152 displayedStep = step; 153 // First call the panels to do the required updates on their layout 154 getButtonsPanel().updateButtons(step); 155 getStepsPanel().setDisplayedStep(step, userData); 156 getCurrentStepPanel().setDisplayedStep(step, userData); 157 } 158 159 /** 160 * Returns the currently displayed step. 161 * @return the currently displayed step. 162 */ 163 public WizardStep getDisplayedStep() 164 { 165 return displayedStep; 166 } 167 168 /** 169 * Forwards to the displayed panel the ProgressDescriptor so that they 170 * can update their contents accordingly. 171 * @param descriptor the descriptor of the Installation progress. 172 */ 173 public void displayProgress(ProgressDescriptor descriptor) 174 { 175 getCurrentStepPanel().displayProgress(descriptor); 176 ProgressStep status = descriptor.getProgressStep(); 177 if (status.isLast()) { 178 setButtonEnabled(ButtonName.CLOSE, true); 179 } 180 } 181 182 /** 183 * Displays an error message dialog. 184 * 185 * @param msg 186 * the error message. 187 * @param title 188 * the title for the dialog. 189 */ 190 public void displayError(LocalizableMessage msg, LocalizableMessage title) 191 { 192 Utilities.displayError(getFrame(), msg, title); 193 } 194 195 /** 196 * Displays a confirmation message dialog. 197 * 198 * @param msg 199 * the confirmation message. 200 * @param title 201 * the title of the dialog. 202 * @return <CODE>true</CODE> if the user confirms the message, or 203 * <CODE>false</CODE> if not. 204 */ 205 public boolean displayConfirmation(LocalizableMessage msg, LocalizableMessage title) 206 { 207 return Utilities.displayConfirmation(getFrame(), msg, title); 208 } 209 210 /** 211 * Returns the value corresponding to the provided FieldName. 212 * @param fieldName the FieldName for which we want to obtain the value. 213 * @return the value corresponding to the provided FieldName. 214 */ 215 public Object getFieldValue(FieldName fieldName) 216 { 217 return getCurrentStepPanel().getFieldValue(fieldName); 218 } 219 220 /** 221 * Marks as invalid (or valid depending on the value of the invalid parameter) 222 * a field corresponding to FieldName. This basically implies udpating the 223 * style of the JLabel associated with fieldName (the association is done 224 * using the LabelFieldDescriptor class). 225 * @param fieldName the FieldName to be marked as valid or invalid. 226 * @param invalid whether to mark the field as valid or invalid. 227 */ 228 public void displayFieldInvalid(FieldName fieldName, boolean invalid) 229 { 230 getCurrentStepPanel().displayFieldInvalid(fieldName, invalid); 231 } 232 233 /** 234 * Adds a button listener. All the button listeners will be notified when 235 * the buttons are clicked (by the user or programatically). 236 * @param l the ButtonActionListener to be added. 237 */ 238 public void addButtonActionListener(ButtonActionListener l) 239 { 240 getButtonsPanel().addButtonActionListener(l); 241 getInstalledPanel().addButtonActionListener(l); 242 getCurrentStepPanel().addButtonActionListener(l); 243 244 buttonListeners.add(l); 245 } 246 247 /** 248 * This method is called to inform that a worker has started (the QuickSetup 249 * is doing some data validation). The worker is doing its tasks outside 250 * the event thread to avoid blocking of the painting and this class is 251 * notified of this fact. The method basically simply the Next and Previous 252 * buttons. 253 * 254 * This method can be called from the event thread or outside the event 255 * thread. 256 * 257 */ 258 public void workerStarted() 259 { 260 Runnable r = new Runnable() 261 { 262 public void run() 263 { 264 displayWorkingProgressImage(true); 265 setButtonEnabled(ButtonName.NEXT, false); 266 setButtonEnabled(ButtonName.PREVIOUS, false); 267 setButtonEnabled(ButtonName.FINISH, false); 268 } 269 }; 270 runOnEventThread(r); 271 } 272 273 /** 274 * This method is called to inform that a worker has finished. The method just 275 * enables the Next and Previous buttons. 276 * 277 * This method can be called from the event thread or outside the event 278 * thread. 279 * 280 */ 281 public void workerFinished() 282 { 283 Runnable r = new Runnable() 284 { 285 public void run() 286 { 287 displayWorkingProgressImage(false); 288 setButtonEnabled(ButtonName.NEXT, true); 289 setButtonEnabled(ButtonName.PREVIOUS, true); 290 setButtonEnabled(ButtonName.FINISH, true); 291 } 292 }; 293 runOnEventThread(r); 294 } 295 296 /** 297 * Notification telling that the installation/uninstallation is finished. 298 * @param successful a boolean telling whether the setup was successful or 299 * not. 300 */ 301 public void finished(boolean successful) 302 { 303 setButtonEnabled(ButtonName.CLOSE, true); 304 if (!successful) 305 { 306 // Do nothing... all the error messages 307 } 308 } 309 310 /** 311 * Returns the frame containing the dialog. 312 * @return the frame containing the dialog. 313 */ 314 public JFrame getFrame() 315 { 316 return frame; 317 } 318 319 /** 320 * Enables a button associated with the given Button Name. 321 * @param buttonName the button name of the button. 322 * @param enable boolean indicating to enable or to disable the button. 323 */ 324 public void setButtonEnabled(ButtonName buttonName, boolean enable) 325 { 326 getButton(buttonName).setEnabled(enable); 327 } 328 329 /** 330 * Returns the panel of the dialog. 331 * @return the panel of the dialog. 332 */ 333 private JPanel getFramePanel() 334 { 335 if (framePanel == null) { 336 framePanel = application.createFramePanel(this); 337 } 338 return framePanel; 339 } 340 341 /** 342 * Returns the steps panel. 343 * @return the steps panel. 344 */ 345 public StepsPanel getStepsPanel() 346 { 347 if (stepsPanel == null) 348 { 349 stepsPanel = new StepsPanel(application); 350 stepsPanel.setQuickSetup(quickSetup); 351 } 352 return stepsPanel; 353 } 354 355 /** 356 * Returns the current step panel. 357 * @return the current step panel. 358 */ 359 public CurrentStepPanel getCurrentStepPanel() 360 { 361 if (currentStepPanel == null) 362 { 363 currentStepPanel = new CurrentStepPanel(application, quickSetup); 364 } 365 return currentStepPanel; 366 } 367 368 369 /** 370 * Returns the buttons panel. 371 * @return the buttons panel. 372 */ 373 public ButtonsPanel getButtonsPanel() 374 { 375 if (buttonsPanel == null) 376 { 377 buttonsPanel = new ButtonsPanel(application); 378 buttonsPanel.setQuickSetup(quickSetup); 379 } 380 return buttonsPanel; 381 } 382 383 /** 384 * Returns the button corresponding to the buttonName. 385 * @param buttonName the ButtonName for which we want to get the button. 386 * @return the button corresponding to the buttonName. 387 */ 388 private JButton getButton(ButtonName buttonName) 389 { 390 JButton button; 391 if (isInstalled() && !forceToDisplay) 392 { 393 if (buttonName == ButtonName.QUIT) 394 { 395 button = getInstalledPanel().getQuitButton(); 396 } else if (buttonName == ButtonName.CONTINUE_INSTALL) 397 { 398 button = getInstalledPanel().getContinueInstallButton(); 399 } else 400 { 401 button = getButtonsPanel().getButton(buttonName); 402 } 403 } else 404 { 405 button = getButtonsPanel().getButton(buttonName); 406 } 407 return button; 408 } 409 410 /** 411 * Sets the focus in the button associated with the ButtonName. 412 * @param buttonName the ButtonName associated with the button. 413 */ 414 public void setFocusOnButton(ButtonName buttonName) 415 { 416 JButton button = getButton(buttonName); 417 if (button != null) { 418 button.requestFocusInWindow(); 419 } else { 420 logger.info(LocalizableMessage.raw("Focus requested for unknown button '" + 421 buttonName + "'")); 422 } 423 } 424 425 /** 426 * Sets the default button for the frame. 427 * @param buttonName the ButtonName associated with the button. 428 */ 429 public void setDefaultButton(ButtonName buttonName) 430 { 431 getFrame().getRootPane().setDefaultButton(getButton(buttonName)); 432 } 433 434 /** 435 * Method used to execute a Runnable in the event thread. If we are in the 436 * event thread it will be called synchronously and if we are not it will 437 * be executed asynchronously. 438 * 439 * @param r the Runnable to be executed. 440 */ 441 private void runOnEventThread(Runnable r) 442 { 443 if (SwingUtilities.isEventDispatchThread()) 444 { 445 r.run(); 446 } else 447 { 448 SwingUtilities.invokeLater(r); 449 } 450 } 451 452 /** 453 * Returns <CODE>true</CODE> if the server is already installed and 454 * <CODE>false</CODE> otherwise. 455 * @return <CODE>true</CODE> if the server is already installed and 456 * <CODE>false</CODE> otherwise. 457 */ 458 private boolean isInstalled() 459 { 460 return installStatus.isInstalled(); 461 } 462 463 /** 464 * Returns (and creates if it is not already created) the panel that 465 * informs the user that the server is already installed when the 466 * installation has been launched. 467 * @return the panel that is used 468 * to inform the user that the server is already installed when the 469 * installation has been launched. 470 */ 471 public QuickSetupErrorPanel getInstalledPanel() 472 { 473 if (installedPanel == null) 474 { 475 installedPanel = new QuickSetupErrorPanel( 476 application, 477 installStatus); 478 installedPanel.setQuickSetup(quickSetup); 479 } 480 return installedPanel; 481 } 482 483 /** 484 * Notifies the ButtonActionListener objects that an ButtonEvent has occurred 485 * in the button associated with buttonName. 486 * @param buttonName the ButtonName associated with the button. 487 */ 488 public void notifyButtonEvent(ButtonName buttonName) 489 { 490 ButtonEvent be = new ButtonEvent(this, buttonName); 491 for (ButtonActionListener li : buttonListeners) 492 { 493 li.buttonActionPerformed(be); 494 } 495 } 496 497 private void displayWorkingProgressImage(boolean display) 498 { 499 getCurrentStepPanel().setCheckingVisible(display); 500 } 501}