/*
* 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.welcome;
import com.android.tools.idea.wizard.DynamicWizard;
import com.android.tools.idea.wizard.DynamicWizardHost;
import com.android.tools.idea.wizard.WizardConstants;
import com.google.common.collect.Maps;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.ui.OptionAction;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.WelcomeScreen;
import com.intellij.openapi.wm.impl.welcomeScreen.NewWelcomeScreen;
import com.intellij.ui.IdeBorderFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Hosts dynamic wizards in the welcome frame.
*/
public class WelcomeScreenHost extends JPanel implements WelcomeScreen, DynamicWizardHost {
private static final Insets BUTTON_MARGINS = new Insets(2, 16, 2, 16);
// Action References. myCancelAction and myHelpAction are inherited
private Action myPreviousAction = new PreviousAction();
private Action myNextAction = new NextAction();
private Action myFinishAction = new FinishAction();
private DynamicWizard myWizard;
private JFrame myFrame;
private String myTitle;
private Dimension myPreferredWindowSize = new Dimension(800, 600);
private Map<Action, JButton> myActionToButtonMap = Maps.newHashMap();
public WelcomeScreenHost() {
super(new BorderLayout());
add(createSouthPanel(), BorderLayout.SOUTH);
}
private static void setMargin(JButton button) {
// Aqua LnF does a good job of setting proper margin between buttons. Setting them specifically causes them be 'square' style instead of
// 'rounded', which is expected by apple users.
if (!SystemInfo.isMac) {
if (BUTTON_MARGINS == null) {
return;
}
button.setMargin(BUTTON_MARGINS);
}
}
@Override
public JComponent getWelcomePanel() {
if (myWizard == null) {
setupWizard();
}
assert myWizard != null;
return this;
}
private void setupWizard() {
DynamicWizard wizard = new FirstRunWizard(this);
wizard.init();
add(wizard.getContentPane(), BorderLayout.CENTER);
}
@Override
public void setupFrame(JFrame frame) {
myFrame = frame;
if (myTitle != null) {
frame.setTitle(myTitle);
}
frame.setSize(myPreferredWindowSize);
Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
int x = (size.width - myPreferredWindowSize.width) / 2;
int y = (size.height - myPreferredWindowSize.height) / 2;
frame.setLocation(x, y);
JButton defaultButton = myActionToButtonMap.get(myFinishAction);
if (!defaultButton.isEnabled()) {
defaultButton = myActionToButtonMap.get(myNextAction);
}
getRootPane().setDefaultButton(defaultButton);
}
@Override
public void dispose() {
// Nothing
}
@NotNull
@Override
public Disposable getDisposable() {
return this;
}
@Override
public void init(@NotNull DynamicWizard wizard) {
myWizard = wizard;
}
@Override
public boolean showAndGet() {
return false;
}
@Override
public void close(boolean canceled) {
}
@Override
public void shakeWindow() {
}
@Override
public void updateButtons(boolean canGoPrev, boolean canGoNext, boolean canFinish) {
setButtonEnabled(myPreviousAction, canGoPrev);
setButtonEnabled(myNextAction, canGoNext);
setButtonEnabled(myFinishAction, canFinish);
if (myFrame != null) {
getRootPane().setDefaultButton(myActionToButtonMap.get(canFinish ? myFinishAction : myNextAction));
}
}
private void setButtonEnabled(Action action, boolean enabled) {
JButton button = myActionToButtonMap.get(action);
if (button != null) {
button.setEnabled(enabled);
}
}
@Override
public void setTitle(String title) {
myTitle = title;
if (myFrame != null) {
myFrame.setTitle(title);
}
}
@Override
public void setIcon(@Nullable Icon icon) {
}
/**
* Creates panel located at the south of the content pane. By default that
* panel contains dialog's buttons. This default implementation uses <code>createActions()</code>
* and <code>createJButtonForAction(Action)</code> methods to construct the panel.
*
* @return south panel
*/
@NotNull
private JComponent createSouthPanel() {
Action[] actions = createActions();
List<JButton> buttons = new ArrayList<JButton>();
JPanel panel = new JPanel(new BorderLayout());
final JPanel lrButtonsPanel = new JPanel(new GridBagLayout());
final Insets insets = SystemInfo.isMacOSLeopard ? new Insets(0, 0, 0, 0) : new Insets(8, 0, 0, 0);
if (actions.length > 0) {
int gridX = 0;
lrButtonsPanel.add(Box.createHorizontalGlue(), // left strut
new GridBagConstraints(gridX++, 0, 1, 1, 1, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, insets, 0,
0));
if (actions.length > 0) {
JPanel buttonsPanel = createButtons(actions, buttons);
lrButtonsPanel.add(buttonsPanel,
new GridBagConstraints(gridX, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE, insets, 0, 0));
}
}
panel.add(lrButtonsPanel, BorderLayout.CENTER);
panel.setBorder(IdeBorderFactory.createEmptyBorder(WizardConstants.STUDIO_WIZARD_INSETS));
return panel;
}
private Action[] createActions() {
return new Action[]{myPreviousAction, myNextAction, myFinishAction};
}
@NotNull
private JPanel createButtons(@NotNull Action[] actions, @NotNull List<JButton> buttons) {
if (!UISettings.getShadowInstance().ALLOW_MERGE_BUTTONS) {
final List<Action> actionList = new ArrayList<Action>();
for (Action action : actions) {
actionList.add(action);
if (action instanceof OptionAction) {
final Action[] options = ((OptionAction)action).getOptions();
actionList.addAll(Arrays.asList(options));
}
}
if (actionList.size() != actions.length) {
actions = actionList.toArray(actionList.toArray(new Action[actionList.size()]));
}
}
JPanel buttonsPanel = new JPanel(new GridLayout(1, actions.length, SystemInfo.isMacOSLeopard ? 0 : 5, 0));
for (final Action action : actions) {
JButton button = createJButtonForAction(action);
final Object value = action.getValue(Action.MNEMONIC_KEY);
if (value instanceof Integer) {
final int mnemonic = ((Integer)value).intValue();
button.setMnemonic(mnemonic);
}
buttons.add(button);
buttonsPanel.add(button);
}
return buttonsPanel;
}
/**
* Creates <code>JButton</code> for the specified action. If the button has not <code>null</code>
* value for <code>DialogWrapper.DEFAULT_ACTION</code> key then the created button will be the
* default one for the dialog.
*
* @param action action for the button
* @return button with action specified
* @see com.intellij.openapi.ui.DialogWrapper#DEFAULT_ACTION
*/
protected JButton createJButtonForAction(Action action) {
JButton button = new JButton(action);
String text = button.getText();
if (SystemInfo.isMac) {
button.putClientProperty("JButton.buttonType", "text");
}
if (text != null) {
int mnemonic = 0;
StringBuilder plainText = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch == '_' || ch == '&') {
if (i >= text.length()) {
break;
}
ch = text.charAt(i);
if (ch != '_' && ch != '&') {
// Mnemonic is case insensitive.
int vk = ch;
if (vk >= 'a' && vk <= 'z') {
vk -= 'a' - 'A';
}
mnemonic = vk;
}
}
plainText.append(ch);
}
button.setText(plainText.toString());
button.setMnemonic(mnemonic);
setMargin(button);
}
myActionToButtonMap.put(action, button);
return button;
}
@Override
public void setPreferredWindowSize(Dimension preferredWindowSize) {
myPreferredWindowSize = preferredWindowSize;
if (myFrame != null) {
myFrame.setSize(preferredWindowSize);
}
}
protected class NextAction extends AbstractAction {
protected NextAction() {
putValue(NAME, IdeBundle.message("button.wizard.next"));
}
@Override
public void actionPerformed(ActionEvent e) {
myWizard.doNextAction();
}
}
protected class PreviousAction extends AbstractAction {
protected PreviousAction() {
putValue(NAME, IdeBundle.message("button.wizard.previous"));
}
@Override
public void actionPerformed(ActionEvent e) {
myWizard.doPreviousAction();
}
}
protected class FinishAction extends AbstractAction {
protected FinishAction() {
putValue(NAME, IdeBundle.message("button.finish"));
}
@Override
public void actionPerformed(ActionEvent e) {
myWizard.doFinishAction();
NewWelcomeScreen welcomeScreen = new NewWelcomeScreen();
Disposer.register(getDisposable(), welcomeScreen);
getRootPane().setDefaultButton(null);
welcomeScreen.setupFrame(myFrame);
myFrame.setContentPane(welcomeScreen.getWelcomePanel());
}
}
}