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