/** * @param wizardModel The overall wizard data model containing the aggregate information of all * components in the wizard * @param isExiting True if the exit button should trigger an application shutdown * @param wizardParameter An optional parameter that can be referenced during construction * @param escapeIsCancel If true, ESC cancels the wizard, if false, it does nothing */ protected AbstractWizard( M wizardModel, boolean isExiting, Optional wizardParameter, boolean escapeIsCancel) { Preconditions.checkNotNull(wizardModel, "'model' must be present"); log.debug("Building wizard..."); this.wizardModel = wizardModel; this.exiting = isExiting; this.wizardParameter = wizardParameter; // Subscribe to events ViewEvents.subscribe(this); CoreEvents.subscribe(this); // Optionally bind the ESC key to a Cancel event (escape to safety) if (escapeIsCancel) { wizardScreenHolder .getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "quit"); wizardScreenHolder.getActionMap().put("quit", getCancelAction()); } // TODO Bind the ENTER key to a Next/Finish/Apply event to speed up data entry through keyboard // wizardPanel.getInputMap(JPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "next"); // wizardPanel.getActionMap().put("next", getNextAction(null)); log.debug("Populating view map and firing initial state view events..."); // Populate based on the current locale populateWizardViewMap(wizardViewMap); // Once all the views are created allow events to occur for (Map.Entry<String, AbstractWizardPanelView> entry : wizardViewMap.entrySet()) { // Ensure the panel is in the correct starting state entry.getValue().fireInitialStateViewEvents(); } wizardScreenHolder.setMinimumSize( new Dimension(MultiBitUI.WIZARD_MIN_WIDTH, MultiBitUI.WIZARD_MIN_HEIGHT)); wizardScreenHolder.setPreferredSize( new Dimension(MultiBitUI.WIZARD_MIN_WIDTH, MultiBitUI.WIZARD_MIN_HEIGHT)); wizardScreenHolder.setSize( new Dimension(MultiBitUI.WIZARD_MIN_WIDTH, MultiBitUI.WIZARD_MIN_HEIGHT)); // Show the panel specified by the initial state show(wizardModel.getPanelName()); }
/** * Hide the wizard * * <p>This method is guaranteed to run on the EDT * * @param panelName The panel name * @param isExitCancel True if this hide operation comes from an exit or cancel * @param wizardPanelView The wizard panel view from the wizard view map */ protected void handleHide( final String panelName, final boolean isExitCancel, AbstractWizardPanelView wizardPanelView) { log.debug("Handle hide starting: '{}' ExitCancel: {}", panelName, isExitCancel); // De-register wizardPanelView.deregisterDefaultButton(); // Ensure we unsubscribe the wizard from all further events getWizardModel().unsubscribe(); unsubscribe(); // Issue the wizard hide event before the hide takes place to give panel views time to update ViewEvents.fireWizardHideEvent(panelName, wizardModel, isExitCancel); // Required to run on a new thread since this may take some time to complete wizardHideExecutorService.submit( new Runnable() { @Override public void run() { log.debug("Hide and deregister wizard: '{}'", this.getClass().getSimpleName()); // Require some extra time to get the rest of the UI started for credentials wizard // There is no chance of the system showing a light box during this time so this // operation is safe if (CredentialsState.CREDENTIALS_ENTER_PASSWORD.name().equals(panelName)) { log.trace("Blocking to allow UI startup to complete"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); } // Work through the view map ensuring all components are deregistered from UI events log.trace("Deregister {} views and their component(s)", wizardViewMap.size()); for (Map.Entry<String, AbstractWizardPanelView> entry : wizardViewMap.entrySet()) { AbstractWizardPanelView panelView = entry.getValue(); // Ensure we deregister the wizard panel view (and model if present) for events try { // Unsubscribe from events panelView.unsubscribe(); log.trace( "Deregistered wizard panel view '{}' from UI events", panelView.getPanelName()); if (panelView.getPanelModel().isPresent()) { Object panelModel = panelView.getPanelModel().get(); // May get some false positives from this approach CoreEvents.unsubscribe(panelModel); log.trace( "Deregistered wizard panel model '{}' from UI events", panelView.getPanelName()); } } catch (NullPointerException | IllegalArgumentException e) { log.warn( "Wizard panel model/view '{}' was not registered", panelView.getPanelName(), e); } // Deregister all components @SuppressWarnings("unchecked") List<ModelAndView> mavs = panelView.getComponents(); for (ModelAndView mav : mavs) { mav.unsubscribe(); } log.trace( "Closed {} registered component(s) from wizard panel view '{}'", mavs.size(), panelView.getPanelName()); // Remove the references mavs.clear(); } // Depopulate the map to ensure non-AWT references are removed wizardViewMap.clear(); // Hiding the light box must be on the EDT SwingUtilities.invokeLater( new Runnable() { @Override public void run() { log.trace("Handle hide remove light box: '{}'", panelName); // This removes the reference to the wizard allowing for garbage collection Panels.hideLightBoxIfPresent(); // Clear the deferred hide Panels.setDeferredHideEventInProgress(false); } }); } }); }
/** This wizard is about to close */ public void unsubscribe() { ViewEvents.unsubscribe(this); CoreEvents.unsubscribe(this); // Further events are handled by subclasses (e.g. HardwareWallet) }
/** * Update the view with any required view events to create a clean initial state (all * initialisation will have completed) * * <p>Default implementation is to disable the "next" button */ public void fireInitialStateViewEvents() { // Default is to disable the Next button ViewEvents.fireWizardButtonEnabledEvent(getPanelName(), WizardButton.NEXT, false); }
@Subscribe public void onVerificationStatusChangedEvent(VerificationStatusChangedEvent event) { ViewEvents.fireWizardButtonEnabledEvent(event.getPanelName(), WizardButton.NEXT, event.isOK()); }
/** Handle the process of restoring a wallet */ private void handleRestoreWallet() { WelcomeWizardModel model = getWizardModel(); log.debug("The select wallet choice is {}", model.getSelectWalletChoice()); log.debug("The restore method is {}", model.getRestoreMethod()); // There are two sorts of restore wallet method: // RESTORE_WALLET_SEED_PHRASE = restore from a seed phrase and timestamp (MBHD soft wallet or // Trezor soft wallet) // RESTORE_WALLET_BACKUP = restore from a seed phrase and wallet backup final boolean backupLocationStatus = handleBackupLocation(); SwingUtilities.invokeLater( new Runnable() { @Override public void run() { LabelDecorator.applyStatusLabel( backupLocationStatusLabel, Optional.of(backupLocationStatus)); backupLocationStatusLabel.setVisible(true); // Hide the header view (switching back on is done in // MainController#onBitcoinNetworkChangedEvent ViewEvents.fireViewChangedEvent(ViewKey.HEADER, false); } }); // Give the user the impression of work being done Uninterruptibles.sleepUninterruptibly(250, TimeUnit.MILLISECONDS); final boolean walletCreatedStatus = handleCreateWalletStatus(model); log.debug("Wallet created status: {}", walletCreatedStatus); // Update created wallet status SwingUtilities.invokeLater( new Runnable() { @Override public void run() { LabelDecorator.applyStatusLabel( walletCreatedStatusLabel, Optional.of(walletCreatedStatus)); walletCreatedStatusLabel.setVisible(true); } }); // Give the user the impression of work being done Uninterruptibles.sleepUninterruptibly(250, TimeUnit.MILLISECONDS); final boolean caCertificatesStatus = handleCACertificateStatus(); // Update the UI SwingUtilities.invokeLater( new Runnable() { @Override public void run() { LabelDecorator.applyStatusLabel( caCertificateStatusLabel, Optional.of(caCertificatesStatus)); caCertificateStatusLabel.setVisible(true); } }); // Give the user the impression of work being done Uninterruptibles.sleepUninterruptibly(250, TimeUnit.MILLISECONDS); // Allow the Finish button at this point since the Bitcoin network may fail and the user will be // trapped ViewEvents.fireWizardButtonEnabledEvent( WelcomeWizardState.RESTORE_WALLET_REPORT.name(), WizardButton.FINISH, true); final boolean walletSynchronizationStatus = handleSynchronizationStatus(); // Update the UI SwingUtilities.invokeLater( new Runnable() { @Override public void run() { LabelDecorator.applyStatusLabel( synchronizationStatusLabel, Optional.of(walletSynchronizationStatus)); synchronizationStatusLabel.setVisible(true); } }); }
@Override public void fireInitialStateViewEvents() { // Enable the finish button ViewEvents.fireWizardButtonEnabledEvent(getPanelName(), WizardButton.FINISH, false); }