blob: 01851cb88e1989e31767dc6c0ea0efa68247dcdb [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.wizard.dynamic;
import com.android.tools.idea.wizard.WizardConstants;
import com.google.common.collect.Maps;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogEarthquakeShaker;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.components.panels.OpaquePanel;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
/**
* Uses {@link DialogWrapper} to display a wizard in a dialog
*/
public class DialogWrapperHost extends DialogWrapper implements DynamicWizardHost {
// Action References. myCancelAction and myHelpAction are inherited
protected Action myPreviousAction = new PreviousAction();
protected Action myNextAction = new NextAction();
protected Action myFinishAction = new FinishAction();
private DynamicWizard myWizard;
private TallImageComponent myIcon = new TallImageComponent(null);
private AtomicReference<ProgressIndicator> myCurrentProgressIndicator = new AtomicReference<ProgressIndicator>();
private JPanel myCenterPanel;
// UI references
private Map<Action, JButton> myActionToButtonMap = Maps.newHashMapWithExpectedSize(5);
public DialogWrapperHost(@Nullable Project project, IdeModalityType modalityType) {
super(project, true, modalityType);
}
public DialogWrapperHost(@Nullable Project project) {
super(project);
}
@Override
public void init(@NotNull DynamicWizard wizard) {
Window window = getWindow();
if (window == null) {
assert ApplicationManager.getApplication().isUnitTestMode();
} else {
window.setPreferredSize(WizardConstants.DEFAULT_WIZARD_WINDOW_SIZE);
}
myWizard = wizard;
super.init();
// Clear out the large border
Container centerRootPanel = myCenterPanel.getParent();
if (centerRootPanel != null) {
Container rootPanel = centerRootPanel.getParent();
if (rootPanel instanceof JPanel) {
// Clear out default borders, we'll set our own later
((JPanel)rootPanel).setBorder(new EmptyBorder(0, 0, 0, 0));
}
}
}
@Override
public void close(@NotNull CloseAction action) {
if (action == CloseAction.FINISH) {
doOKAction();
}
else if (action == CloseAction.CANCEL) {
dialogWrapperCancel();
}
}
private void dialogWrapperCancel() {
super.doCancelAction();
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
JComponent focusedComponent = myWizard.getPreferredFocusedComponent();
return focusedComponent == null ? getNextButton() : focusedComponent;
}
@Override
public void shakeWindow() {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
DialogEarthquakeShaker.shake((JDialog)getPeer().getWindow());
}
}
/**
* ACTIONS
*/
@NotNull
@Override
protected Action[] createActions() {
if (getHelpId() == null) {
if (SystemInfo.isMac) {
return new Action[]{getCancelAction(), myPreviousAction, myNextAction, myFinishAction};
}
return new Action[]{myPreviousAction, myNextAction, getCancelAction(), myFinishAction};
}
else {
if (SystemInfo.isMac) {
return new Action[]{getHelpAction(), getCancelAction(), myPreviousAction, myNextAction, myFinishAction};
}
return new Action[]{myPreviousAction, myNextAction, getCancelAction(), myFinishAction, getHelpAction()};
}
}
@Override
public void setIcon(@Nullable Icon icon) {
myIcon.setIcon(icon);
myIcon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 8));
}
@Override
public void runSensitiveOperation(@NotNull final ProgressIndicator progressIndicator,
boolean cancellable, @NotNull final Runnable operation) {
final Application application = ApplicationManager.getApplication();
application.assertIsDispatchThread();
if (!myCurrentProgressIndicator.compareAndSet(null, progressIndicator)) {
throw new IllegalStateException("Submitting an operation while another is in progress.");
}
final JRootPane rootPane = getRootPane();
rootPane.setDefaultButton(null);
updateButtons(false, false, true, false);
Task.Backgroundable task = new Task.Backgroundable(null, myWizard.getWizardActionDescription(), cancellable) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
operation.run();
// this can't be done in onSuccess because the ModalityState needs to be set
ApplicationManager.getApplication().invokeAndWait(new Runnable() {
@Override
public void run() {
updateButtons(false, false, false, true);
myCurrentProgressIndicator.set(null);
}
}, ModalityState.stateForComponent(myWizard.getContentPane()));
}
};
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, progressIndicator);
}
@Override
protected final JButton createJButtonForAction(Action action) {
// Save a reference to the created button so that we can access them
// later to enable/disable
JButton button = super.createJButtonForAction(action);
myActionToButtonMap.put(action, button);
return button;
}
@Override
protected void doHelpAction() {
// TODO: Implement
}
/**
* Create the south panel that serves as the container for the wizard buttons.
* The base class does all the heavy lifting already, we just adjust the margins here.
*/
@Nullable
@Override
protected JComponent createSouthPanel() {
JPanel southPanel = (JPanel)super.createSouthPanel();
assert southPanel != null;
southPanel.setBorder(new EmptyBorder(WizardConstants.STUDIO_WIZARD_INSETS));
return southPanel;
}
@Override
public final void doCancelAction() {
ProgressIndicator indicator = myCurrentProgressIndicator.get();
if (indicator != null) {
indicator.cancel();
} else {
myWizard.doCancelAction();
}
}
/**
* Update the buttons for the wizard
* @param canGoPrev whether the previous button is enabled
* @param canGoNext whether the next button is enabled
* @param canCancel whether the cancel button is enabled
* @param canFinish if this is set to true and the current path is the last non-optional path, the canFinish
*/
@Override
public void updateButtons(boolean canGoPrev, boolean canGoNext, boolean canCancel, boolean canFinish) {
JButton prev = getPreviousButton();
if (prev != null) {
prev.setEnabled(canGoPrev);
}
JButton next = getNextButton();
if (next != null) {
next.setEnabled(canGoNext);
}
getFinishButton().setEnabled(canFinish);
getCancelButton().setEnabled(canCancel);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
getRootPane().setDefaultButton(canFinish ? getFinishButton() : getNextButton());
}
}
/**
* Create the center panel that will serve as a container for the UI components of each step.
*/
@Override
@Nullable
protected final JComponent createCenterPanel() {
myCenterPanel = new JPanel(new BorderLayout());
myCenterPanel.add(myWizard.getContentPane(), BorderLayout.CENTER);
myCenterPanel.add(myIcon, BorderLayout.WEST);
return myCenterPanel;
}
@Nullable
protected JButton getNextButton() {
return myActionToButtonMap.get(myNextAction);
}
@Nullable
protected JButton getPreviousButton() {
return myActionToButtonMap.get(myPreviousAction);
}
@NotNull
protected JButton getFinishButton() {
return myActionToButtonMap.get(myFinishAction);
}
@NotNull
protected JButton getCancelButton() {
return myActionToButtonMap.get(myCancelAction);
}
@Override
public void setTitle(String title) {
super.setTitle(title);
}
@Override
public void setPreferredWindowSize(Dimension dimension) {
getContentPanel().setPreferredSize(dimension);
}
/**
* Taken from {@link com.intellij.ide.wizard.AbstractWizard}
*/
public static class TallImageComponent extends OpaquePanel {
@Nullable private Icon myIcon;
public TallImageComponent(@Nullable Icon icon) {
myIcon = icon;
}
@Override
protected void paintChildren(Graphics g) {
if (myIcon == null) return;
paintIcon(g);
}
public void paintIcon(Graphics g) {
if (myIcon == null) {
return;
}
final BufferedImage image = UIUtil.createImage(myIcon.getIconWidth(), myIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
final Graphics2D gg = image.createGraphics();
myIcon.paintIcon(this, gg, 0, 0);
final Rectangle bounds = g.getClipBounds();
int y = myIcon.getIconHeight() - 1;
while (y < bounds.y + bounds.height) {
g.drawImage(image, bounds.x, y, bounds.x + bounds.width, y + 1, 0, myIcon.getIconHeight() - 1, bounds.width, myIcon.getIconHeight(),
this);
y++;
}
g.drawImage(image, 0, 0, this);
}
public void setIcon(@Nullable Icon icon) {
myIcon = icon;
revalidate();
repaint();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(myIcon != null ? myIcon.getIconWidth() : 0, 0);
}
@Override
public Dimension getMinimumSize() {
return new Dimension(myIcon != null ? myIcon.getIconWidth() : 0, 0);
}
}
protected class NextAction extends DialogWrapperAction {
protected NextAction() {
super(IdeBundle.message("button.wizard.next"));
putValue(DEFAULT_ACTION, Boolean.TRUE);
}
@Override
protected void doAction(ActionEvent e) {
myWizard.doNextAction();
}
}
protected class PreviousAction extends DialogWrapperAction {
protected PreviousAction() {
super(IdeBundle.message("button.wizard.previous"));
}
@Override
protected void doAction(ActionEvent e) {
myWizard.doPreviousAction();
}
}
protected class FinishAction extends DialogWrapperAction {
protected FinishAction() {
super(IdeBundle.message("button.finish"));
}
@Override
protected void doAction(ActionEvent e) {
myWizard.doFinishAction();
}
}
}