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