1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.ide.customize;
4 import com.intellij.ide.IdeBundle;
5 import com.intellij.ide.startup.StartupActionScriptManager;
6 import com.intellij.idea.SplashManager;
7 import com.intellij.idea.StartupUtil;
8 import com.intellij.openapi.application.ApplicationNamesInfo;
9 import com.intellij.openapi.ui.DialogWrapper;
10 import com.intellij.openapi.util.text.HtmlBuilder;
11 import com.intellij.openapi.util.text.HtmlChunk;
12 import com.intellij.openapi.util.text.StringUtil;
13 import com.intellij.openapi.wm.IdeFocusManager;
14 import com.intellij.ui.JBCardLayout;
15 import com.intellij.ui.components.JBLabel;
16 import com.intellij.util.ui.JBUI;
17 import org.jetbrains.annotations.Contract;
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.util.ArrayList;
26 import java.util.List;
28 import static com.intellij.openapi.util.text.HtmlChunk.*;
30 public class CustomizeIDEWizardDialog extends DialogWrapper implements CommonCustomizeIDEWizardDialog {
31 protected static final String BUTTONS = "BUTTONS";
32 protected static final String NO_BUTTONS = "NO_BUTTONS";
34 protected final JButton mySkipButton = new JButton(IdeBundle.message("button.skip.remaining.and.set.defaults"));
35 protected final JButton myBackButton = new JButton(IdeBundle.message("button.back"));
36 protected final JButton myNextButton = new JButton(IdeBundle.message("button.next"));
38 protected final JBCardLayout myCardLayout = new JBCardLayout();
39 protected final List<AbstractCustomizeWizardStep> mySteps = new ArrayList<>();
40 protected int myIndex = 0;
41 protected final JBLabel myNavigationLabel = new JBLabel();
42 protected final JBLabel myHeaderLabel = new JBLabel();
43 protected final JBLabel myFooterLabel = new JBLabel();
44 protected final CardLayout myButtonWrapperLayout = new CardLayout();
45 protected final JPanel myButtonWrapper = new JPanel(myButtonWrapperLayout);
46 protected JPanel myContentPanel;
47 protected final boolean myHideSkipButton;
49 public CustomizeIDEWizardDialog(@NotNull CustomizeIDEWizardStepsProvider stepsProvider) {
50 this(stepsProvider, null, true, true);
53 public CustomizeIDEWizardDialog(@NotNull CustomizeIDEWizardStepsProvider stepsProvider, @Nullable StartupUtil.AppStarter appStarter,
54 boolean beforeSplash, boolean afterSplash) {
55 super(null, true, true);
56 setTitle(IdeBundle.message("dialog.title.customize.0", ApplicationNamesInfo.getInstance().getFullProductName()));
57 getPeer().setAppIcons();
59 if (beforeSplash) stepsProvider.initSteps(this, mySteps);
60 if (afterSplash) stepsProvider.initStepsAfterSplash(this, mySteps);
62 if (appStarter != null) {
63 int newIndex = appStarter.customizeIdeWizardDialog(mySteps);
69 myHideSkipButton = (mySteps.size() <= 1) || stepsProvider.hideSkipButton();
71 if (mySteps.isEmpty()) {
72 close(CANCEL_EXIT_CODE);
76 mySkipButton.addActionListener(this);
77 myBackButton.addActionListener(this);
78 myNextButton.addActionListener(this);
79 AbstractCustomizeWizardStep.applyHeaderFooterStyle(myNavigationLabel);
80 AbstractCustomizeWizardStep.applyHeaderFooterStyle(myHeaderLabel);
81 AbstractCustomizeWizardStep.applyHeaderFooterStyle(myFooterLabel);
83 initCurrentStep(true);
85 System.setProperty(StartupActionScriptManager.STARTUP_WIZARD_MODE, "true");
89 public final void show() {
90 if (mySteps.isEmpty()) {
91 throw new IllegalStateException("no steps provided"); // use showIfNeeded() instead
93 CustomizeIDEWizardInteractions.INSTANCE.record(CustomizeIDEWizardInteractionType.WizardDisplayed);
94 SplashManager.executeWithHiddenSplash(getWindow(), () -> super.show());
98 public final boolean showIfNeeded() {
99 boolean willBeShown = !mySteps.isEmpty() && !isDisposed();
107 protected @NotNull DialogStyle getStyle() {
108 return DialogStyle.COMPACT;
112 protected void dispose() {
113 System.clearProperty(StartupActionScriptManager.STARTUP_WIZARD_MODE);
118 protected JComponent createCenterPanel() {
119 JPanel result = new JPanel(new BorderLayout(5, 5));
120 myContentPanel = new JPanel(myCardLayout);
121 for (AbstractCustomizeWizardStep step : mySteps) {
122 myContentPanel.add(step, step.getTitle());
124 JPanel topPanel = new JPanel(new BorderLayout(5, 5));
125 if (mySteps.size() > 1) {
126 topPanel.add(myNavigationLabel, BorderLayout.NORTH);
128 topPanel.add(myHeaderLabel, BorderLayout.CENTER);
129 result.add(topPanel, BorderLayout.NORTH);
130 result.add(myContentPanel, BorderLayout.CENTER);
131 result.add(myFooterLabel, BorderLayout.SOUTH);
132 result.setPreferredSize(JBUI.size(700, 600));
133 result.setBorder(AbstractCustomizeWizardStep.createSmallEmptyBorder());
138 protected JComponent createSouthPanel() {
139 final JPanel buttonPanel = new JPanel(new GridBagLayout());
140 GridBagConstraints gbc = new GridBagConstraints();
141 gbc.insets.right = 5;
142 gbc.fill = GridBagConstraints.BOTH;
146 if (!myHideSkipButton)
147 buttonPanel.add(mySkipButton, gbc);
150 buttonPanel.add(myBackButton, gbc);
153 buttonPanel.add(Box.createHorizontalGlue(), gbc);
156 buttonPanel.add(myNextButton, gbc);
157 buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
158 myButtonWrapper.add(buttonPanel, BUTTONS);
159 myButtonWrapper.add(new JLabel(), NO_BUTTONS);
160 myButtonWrapperLayout.show(myButtonWrapper, BUTTONS);
161 return myButtonWrapper;
164 void setButtonsVisible(boolean visible) {
165 myButtonWrapperLayout.show(myButtonWrapper, visible ? BUTTONS : NO_BUTTONS);
169 public void actionPerformed(@NotNull ActionEvent e) {
170 if (e.getSource() == mySkipButton) {
171 CustomizeIDEWizardInteractions.INSTANCE.setSkippedOnPage(myIndex);
175 if (e.getSource() == myBackButton) {
177 initCurrentStep(false);
180 if (e.getSource() == myNextButton) {
181 if (myIndex >= mySteps.size() - 1) {
186 initCurrentStep(true);
192 protected ActionListener createCancelAction() {
193 return null;//Prevent closing by <Esc>
197 public void doCancelAction() {
202 protected void doOKAction() {
203 for (AbstractCustomizeWizardStep step : mySteps) {
204 if (!step.beforeOkAction()) {
205 int index = mySteps.indexOf(step);
206 if (myIndex != index) {
208 initCurrentStep(true);
217 protected boolean canRecordDialogId() {
221 protected void initCurrentStep(boolean forward) {
222 final AbstractCustomizeWizardStep myCurrentStep = mySteps.get(myIndex);
223 myCurrentStep.beforeShown(forward);
224 myCardLayout.swipe(myContentPanel, myCurrentStep.getTitle(), JBCardLayout.SwipeDirection.AUTO, () -> {
225 Component component = myCurrentStep.getDefaultFocusedComponent();
226 if (component != null) {
227 IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(component, true));
231 myBackButton.setVisible(myIndex > 0);
233 myBackButton.setText(IdeBundle.message("button.back.to.0", mySteps.get(myIndex - 1).getTitle()));
236 myNextButton.setText(myIndex < mySteps.size() - 1
237 ? IdeBundle.message("button.next.0", mySteps.get(myIndex + 1).getTitle())
238 : IdeBundle.message("button.start.using.0", ApplicationNamesInfo.getInstance().getFullProductName()));
239 myHeaderLabel.setText(ensureHTML(myCurrentStep.getHTMLHeader()));
240 myFooterLabel.setText(ensureHTML(myCurrentStep.getHTMLFooter()));
241 if (mySteps.size() > 1) {
242 HtmlChunk.Element body = HtmlChunk.body();
243 String arrow = myNavigationLabel.getFont().canDisplay(0x2192) ? "→" : ">";
244 for (int i = 0; i < mySteps.size(); i++) {
246 body = body.children(nbsp(), raw(arrow), nbsp());
249 body = body.children(
250 tag("b").addText(mySteps.get(i).getTitle()));
252 body = body.addText(mySteps.get(i).getTitle());
255 String navHtml = new HtmlBuilder().append(HtmlChunk.html().child(body)).toString();
257 myNavigationLabel.setText(navHtml);
261 @Contract(value = "!null->!null" ,pure = true)
262 private static String ensureHTML(@Nullable String s) {
263 return s == null ? null : s.startsWith("<html>") ? s : "<html>" + StringUtil.escapeXmlEntities(s) + "</html>";