Remove compact style from CustomizeIDEWizardDialog
[idea/community.git] / platform / platform-impl / src / com / intellij / ide / customize / CustomizeIDEWizardDialog.java
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;
3
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;
20
21 import javax.swing.*;
22 import java.awt.*;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import static com.intellij.openapi.util.text.HtmlChunk.*;
29
30 public class CustomizeIDEWizardDialog extends DialogWrapper implements CommonCustomizeIDEWizardDialog {
31   protected static final String BUTTONS = "BUTTONS";
32   protected static final String NO_BUTTONS = "NO_BUTTONS";
33
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"));
37
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;
48
49   public CustomizeIDEWizardDialog(@NotNull CustomizeIDEWizardStepsProvider stepsProvider) {
50     this(stepsProvider, null, true, true);
51   }
52
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();
58
59     if (beforeSplash) stepsProvider.initSteps(this, mySteps);
60     if (afterSplash) stepsProvider.initStepsAfterSplash(this, mySteps);
61
62     if (appStarter != null) {
63       int newIndex = appStarter.customizeIdeWizardDialog(mySteps);
64       if (newIndex != -1) {
65         myIndex = newIndex;
66       }
67     }
68
69     myHideSkipButton = (mySteps.size() <= 1) || stepsProvider.hideSkipButton();
70
71     if (mySteps.isEmpty()) {
72       close(CANCEL_EXIT_CODE);
73       return;
74     }
75
76     mySkipButton.addActionListener(this);
77     myBackButton.addActionListener(this);
78     myNextButton.addActionListener(this);
79     AbstractCustomizeWizardStep.applyHeaderFooterStyle(myNavigationLabel);
80     AbstractCustomizeWizardStep.applyHeaderFooterStyle(myHeaderLabel);
81     AbstractCustomizeWizardStep.applyHeaderFooterStyle(myFooterLabel);
82     init();
83     initCurrentStep(true);
84     setSize(400, 300);
85     System.setProperty(StartupActionScriptManager.STARTUP_WIZARD_MODE, "true");
86   }
87
88   @Override
89   public final void show() {
90     if (mySteps.isEmpty()) {
91       throw new IllegalStateException("no steps provided");  // use showIfNeeded() instead
92     }
93     CustomizeIDEWizardInteractions.INSTANCE.record(CustomizeIDEWizardInteractionType.WizardDisplayed);
94     SplashManager.executeWithHiddenSplash(getWindow(), () -> super.show());
95   }
96
97   @Override
98   public final boolean showIfNeeded() {
99     boolean willBeShown = !mySteps.isEmpty() && !isDisposed();
100     if (willBeShown) {
101       show();
102     }
103     return willBeShown;
104   }
105
106   @Override
107   protected void dispose() {
108     System.clearProperty(StartupActionScriptManager.STARTUP_WIZARD_MODE);
109     super.dispose();
110   }
111
112   @Override
113   protected JComponent createCenterPanel() {
114     JPanel result = new JPanel(new BorderLayout(5, 5));
115     myContentPanel = new JPanel(myCardLayout);
116     for (AbstractCustomizeWizardStep step : mySteps) {
117       myContentPanel.add(step, step.getTitle());
118     }
119     JPanel topPanel = new JPanel(new BorderLayout(5, 5));
120     if (mySteps.size() > 1) {
121       topPanel.add(myNavigationLabel, BorderLayout.NORTH);
122     }
123     topPanel.add(myHeaderLabel, BorderLayout.CENTER);
124     result.add(topPanel, BorderLayout.NORTH);
125     result.add(myContentPanel, BorderLayout.CENTER);
126     result.add(myFooterLabel, BorderLayout.SOUTH);
127     result.setPreferredSize(JBUI.size(700, 600));
128     result.setBorder(AbstractCustomizeWizardStep.createSmallEmptyBorder());
129     return result;
130   }
131
132   @Override
133   protected JComponent createSouthPanel() {
134     final JPanel buttonPanel = new JPanel(new GridBagLayout());
135     GridBagConstraints gbc = new GridBagConstraints();
136     gbc.insets.right = 5;
137     gbc.fill = GridBagConstraints.BOTH;
138     gbc.gridx = 0;
139     gbc.gridy = 0;
140
141     if (!myHideSkipButton)
142       buttonPanel.add(mySkipButton, gbc);
143
144     gbc.gridx++;
145     buttonPanel.add(myBackButton, gbc);
146     gbc.gridx++;
147     gbc.weightx = 1;
148     buttonPanel.add(Box.createHorizontalGlue(), gbc);
149     gbc.gridx++;
150     gbc.weightx = 0;
151     buttonPanel.add(myNextButton, gbc);
152     buttonPanel.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0));
153     myButtonWrapper.add(buttonPanel, BUTTONS);
154     myButtonWrapper.add(new JLabel(), NO_BUTTONS);
155     myButtonWrapperLayout.show(myButtonWrapper, BUTTONS);
156     return myButtonWrapper;
157   }
158
159   void setButtonsVisible(boolean visible) {
160     myButtonWrapperLayout.show(myButtonWrapper, visible ? BUTTONS : NO_BUTTONS);
161   }
162
163   @Override
164   public void actionPerformed(@NotNull ActionEvent e) {
165     if (e.getSource() == mySkipButton) {
166       CustomizeIDEWizardInteractions.INSTANCE.setSkippedOnPage(myIndex);
167       doOKAction();
168       return;
169     }
170     if (e.getSource() == myBackButton) {
171       myIndex--;
172       initCurrentStep(false);
173       return;
174     }
175     if (e.getSource() == myNextButton) {
176       if (myIndex >= mySteps.size() - 1) {
177         doOKAction();
178         return;
179       }
180       myIndex++;
181       initCurrentStep(true);
182     }
183   }
184
185   @Nullable
186   @Override
187   protected ActionListener createCancelAction() {
188     return null;//Prevent closing by <Esc>
189   }
190
191   @Override
192   public void doCancelAction() {
193     doOKAction();
194   }
195
196   @Override
197   protected void doOKAction() {
198     for (AbstractCustomizeWizardStep step : mySteps) {
199       if (!step.beforeOkAction()) {
200         int index = mySteps.indexOf(step);
201         if (myIndex != index) {
202           myIndex = index;
203           initCurrentStep(true);
204         }
205         return;
206       }
207     }
208     super.doOKAction();
209   }
210
211   @Override
212   protected boolean canRecordDialogId() {
213     return false;
214   }
215
216   protected void initCurrentStep(boolean forward) {
217     final AbstractCustomizeWizardStep myCurrentStep = mySteps.get(myIndex);
218     myCurrentStep.beforeShown(forward);
219     myCardLayout.swipe(myContentPanel, myCurrentStep.getTitle(), JBCardLayout.SwipeDirection.AUTO, () -> {
220       Component component = myCurrentStep.getDefaultFocusedComponent();
221       if (component != null) {
222         IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown(() -> IdeFocusManager.getGlobalInstance().requestFocus(component, true));
223       }
224     });
225
226     myBackButton.setVisible(myIndex > 0);
227     if (myIndex > 0) {
228       myBackButton.setText(IdeBundle.message("button.back.to.0", mySteps.get(myIndex - 1).getTitle()));
229     }
230
231     myNextButton.setText(myIndex < mySteps.size() - 1
232                          ? IdeBundle.message("button.next.0", mySteps.get(myIndex + 1).getTitle())
233                          : IdeBundle.message("button.start.using.0", ApplicationNamesInfo.getInstance().getFullProductName()));
234     myHeaderLabel.setText(ensureHTML(myCurrentStep.getHTMLHeader()));
235     myFooterLabel.setText(ensureHTML(myCurrentStep.getHTMLFooter()));
236     if (mySteps.size() > 1) {
237       HtmlChunk.Element body = HtmlChunk.body();
238       String arrow = myNavigationLabel.getFont().canDisplay(0x2192) ? "&#8594;" : "&gt;";
239       for (int i = 0; i < mySteps.size(); i++) {
240         if (i > 0) {
241           body = body.children(nbsp(), raw(arrow), nbsp());
242         }
243         if (i == myIndex) {
244           body = body.children(
245             tag("b").addText(mySteps.get(i).getTitle()));
246         } else {
247           body = body.addText(mySteps.get(i).getTitle());
248         }
249       }
250       String navHtml = new HtmlBuilder().append(HtmlChunk.html().child(body)).toString();
251
252       myNavigationLabel.setText(navHtml);
253     }
254   }
255
256   @Contract(value = "!null->!null" ,pure = true)
257   private static String ensureHTML(@Nullable String s) {
258     return s == null ? null : s.startsWith("<html>") ? s : "<html>" + StringUtil.escapeXmlEntities(s) + "</html>";
259   }
260 }