2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.openapi.wm.impl.welcomeScreen;
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.DataManager;
20 import com.intellij.ide.RecentProjectsManager;
21 import com.intellij.openapi.Disposable;
22 import com.intellij.openapi.MnemonicHelper;
23 import com.intellij.openapi.actionSystem.*;
24 import com.intellij.openapi.application.Application;
25 import com.intellij.openapi.application.ApplicationManager;
26 import com.intellij.openapi.application.ApplicationNamesInfo;
27 import com.intellij.openapi.application.ModalityState;
28 import com.intellij.openapi.application.ex.ApplicationInfoEx;
29 import com.intellij.openapi.application.ex.ApplicationManagerEx;
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.project.DumbAwareRunnable;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.project.ProjectManager;
34 import com.intellij.openapi.project.ProjectManagerAdapter;
35 import com.intellij.openapi.ui.popup.JBPopupFactory;
36 import com.intellij.openapi.util.DimensionService;
37 import com.intellij.openapi.util.Disposer;
38 import com.intellij.openapi.util.IconLoader;
39 import com.intellij.openapi.util.Ref;
40 import com.intellij.openapi.wm.IdeFrame;
41 import com.intellij.openapi.wm.IdeRootPaneNorthExtension;
42 import com.intellij.openapi.wm.StatusBar;
43 import com.intellij.openapi.wm.WelcomeScreen;
44 import com.intellij.openapi.wm.impl.IdeGlassPaneImpl;
45 import com.intellij.ui.*;
46 import com.intellij.ui.border.CustomLineBorder;
47 import com.intellij.ui.components.labels.ActionLink;
48 import com.intellij.ui.components.panels.NonOpaquePanel;
49 import com.intellij.ui.popup.PopupFactoryImpl;
50 import com.intellij.util.ui.EmptyIcon;
51 import com.intellij.util.ui.UIUtil;
52 import org.jetbrains.annotations.NotNull;
53 import org.jetbrains.annotations.Nullable;
56 import javax.swing.border.EmptyBorder;
57 import javax.swing.event.ListDataEvent;
58 import javax.swing.event.ListDataListener;
60 import java.awt.event.*;
62 import java.io.InputStream;
66 * @author Konstantin Bulenkov
68 public class FlatWelcomeFrame extends JFrame implements IdeFrame {
69 private final BalloonLayout myBalloonLayout;
70 private final FlatWelcomeScreen myScreen;
72 public FlatWelcomeFrame() {
73 final JRootPane rootPane = getRootPane();
74 myScreen = new FlatWelcomeScreen();
76 final IdeGlassPaneImpl glassPane = new IdeGlassPaneImpl(rootPane) {
78 public void addNotify() {
80 rootPane.remove(getProxyComponent());
84 setGlassPane(glassPane);
85 glassPane.setVisible(false);
86 //setUndecorated(true);
87 setContentPane(myScreen.getWelcomePanel());
88 setTitle("Welcome to " + ApplicationNamesInfo.getInstance().getFullProductName());
89 AppUIUtil.updateWindowIcon(this);
90 //Rectangle bounds = ScreenUtil.getMainScreenBounds();
91 if (RecentProjectsManager.getInstance().getRecentProjectsActions(false).length > 0) {
97 //int x = bounds.x + (bounds.width - getWidth()) / 2;
98 //int y = bounds.y + (bounds.height - getHeight()) / 2;
99 Point location = DimensionService.getInstance().getLocation(WelcomeFrame.DIMENSION_KEY, null);
100 Rectangle screenBounds = ScreenUtil.getScreenRectangle(location != null ? location : new Point(0, 0));
101 setLocation(new Point(
102 screenBounds.x + (screenBounds.width - getWidth()) / 2,
103 screenBounds.y + (screenBounds.height - getHeight()) / 3
107 ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerAdapter() {
109 public void projectOpened(Project project) {
114 myBalloonLayout = new BalloonLayoutImpl(rootPane, new Insets(8, 8, 8, 8));
117 new MnemonicHelper().register(this);
118 Disposer.register(ApplicationManager.getApplication(), new Disposable() {
120 public void dispose() {
121 FlatWelcomeFrame.this.dispose();
127 public void dispose() {
128 saveLocation(getBounds());
130 Disposer.dispose(myScreen);
131 WelcomeFrame.resetInstance();
134 private static void saveLocation(Rectangle location) {
135 Point middle = new Point(location.x + location.width / 2, location.y = location.height / 2);
136 DimensionService.getInstance().setLocation(WelcomeFrame.DIMENSION_KEY, middle, null);
139 private void setupCloseAction() {
140 setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
142 new WindowAdapter() {
143 public void windowClosing(final WindowEvent e) {
146 final Application app = ApplicationManager.getApplication();
147 app.invokeLater(new DumbAwareRunnable() {
149 if (app.isDisposed()) {
150 ApplicationManagerEx.getApplicationEx().exit();
154 final Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
155 if (openProjects.length == 0) {
156 ApplicationManagerEx.getApplicationEx().exit();
159 }, ModalityState.NON_MODAL);
166 public StatusBar getStatusBar() {
170 public static Color getMainBackground() {
171 return new JBColor(0xf7f7f7, 0x45474a);
174 public static Color getProjectsBackground() {
175 return new JBColor(Gray.xFF, Gray.x39);
178 public static Color getLinkNormalColor() {
179 return new JBColor(Gray._0, Gray.xBB);
182 public static Color getListSelectionColor(boolean hasFocus) {
183 return hasFocus ? new JBColor(0x3875d6, 0x4b6eaf) : new JBColor(Gray.xDD, Gray.x45);
186 public static Color getActionLinkSelectionColor() {
187 return new JBColor(0xdbe5f5, 0x485875);
190 public static JBColor getSeparatorColor() {
191 return new JBColor(Gray.xEC, new Color(72, 75, 78));
194 private class FlatWelcomeScreen extends JPanel implements WelcomeScreen {
195 public FlatWelcomeScreen() {
196 super(new BorderLayout());
197 setBackground(getMainBackground());
198 if (RecentProjectsManager.getInstance().getRecentProjectsActions(false).length > 0) {
199 final JComponent recentProjects = createRecentProjects();
200 add(recentProjects, BorderLayout.WEST);
201 final JList projectsList = UIUtil.findComponentOfType(recentProjects, JList.class);
202 if (projectsList != null) {
203 projectsList.getModel().addListDataListener(new ListDataListener() {
205 public void intervalAdded(ListDataEvent e) {
209 public void intervalRemoved(ListDataEvent e) {
213 private void removeIfNeeded() {
214 if (RecentProjectsManager.getInstance().getRecentProjectsActions(false).length == 0) {
215 FlatWelcomeScreen.this.remove(recentProjects);
216 FlatWelcomeScreen.this.revalidate();
217 FlatWelcomeScreen.this.repaint();
222 public void contentsChanged(ListDataEvent e) {
226 projectsList.addFocusListener(new FocusListener() {
228 public void focusGained(FocusEvent e) {
229 projectsList.repaint();
233 public void focusLost(FocusEvent e) {
234 projectsList.repaint();
239 add(createBody(), BorderLayout.CENTER);
243 public JComponent getWelcomePanel() {
247 private JComponent createBody() {
248 NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
249 panel.add(createLogo(), BorderLayout.NORTH);
250 panel.add(createActionPanel(), BorderLayout.CENTER);
251 panel.add(createSettingsAndDocs(), BorderLayout.SOUTH);
255 private JComponent createSettingsAndDocs() {
256 JPanel panel = new NonOpaquePanel(new BorderLayout());
257 NonOpaquePanel toolbar = new NonOpaquePanel();
258 AnAction register = ActionManager.getInstance().getAction("Register");
259 boolean registeredVisible = false;
260 if (register != null) {
261 Presentation presentation = register.getTemplatePresentation();
262 register.update(new AnActionEvent(null, DataManager.getInstance().getDataContext(this),
263 ActionPlaces.WELCOME_SCREEN, presentation, ActionManager.getInstance(), 0));
264 if (presentation.isEnabled()) {
265 ActionLink registerLink = new ActionLink("Register", register);
266 registerLink.setNormalColor(getLinkNormalColor());
267 NonOpaquePanel button = new NonOpaquePanel(new BorderLayout());
268 button.setBorder(new EmptyBorder(4, 10, 4, 10));
269 button.add(registerLink);
270 installFocusable(button, register, KeyEvent.VK_UP, KeyEvent.VK_RIGHT, true);
271 NonOpaquePanel wrap = new NonOpaquePanel();
272 wrap.setBorder(new EmptyBorder(0, 10, 0, 0));
274 panel.add(wrap, BorderLayout.WEST);
275 registeredVisible = true;
279 toolbar.setLayout(new BoxLayout(toolbar, BoxLayout.X_AXIS));
280 toolbar.add(createActionLink("Configure", IdeActions.GROUP_WELCOME_SCREEN_CONFIGURE, AllIcons.General.Settings, !registeredVisible));
281 toolbar.add(createActionLink("Get Help", IdeActions.GROUP_WELCOME_SCREEN_DOC, null, false));
283 panel.add(toolbar, BorderLayout.EAST);
286 panel.setBorder(new EmptyBorder(0,0,8,21));
290 private JComponent createActionLink(final String text, final String groupId, Icon icon, boolean focusListOnLeft) {
291 final Ref<ActionLink> ref = new Ref<ActionLink>(null);
292 AnAction action = new AnAction() {
294 public void actionPerformed(@NotNull AnActionEvent e) {
295 ActionGroup configureGroup = (ActionGroup)ActionManager.getInstance().getAction(groupId);
296 final PopupFactoryImpl.ActionGroupPopup popup = (PopupFactoryImpl.ActionGroupPopup)JBPopupFactory.getInstance()
297 .createActionGroupPopup(null, new IconsFreeActionGroup(configureGroup), e.getDataContext(), false, false, false, null,
299 popup.showUnderneathOfLabel(ref.get());
302 ref.set(new ActionLink(text, icon, action));
303 ref.get().setPaintUnderline(false);
304 ref.get().setNormalColor(getLinkNormalColor());
305 NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
306 panel.setBorder(new EmptyBorder(4, 10, 4, 10));
307 panel.add(ref.get());
308 panel.add(createArrow(ref.get()), BorderLayout.EAST);
309 installFocusable(panel, action, KeyEvent.VK_UP, KeyEvent.VK_DOWN, focusListOnLeft);
313 private JComponent createActionPanel() {
314 JPanel actions = new NonOpaquePanel();
315 actions.setLayout(new BoxLayout(actions, BoxLayout.Y_AXIS));
316 ActionManager actionManager = ActionManager.getInstance();
317 ActionGroup quickStart = (ActionGroup)actionManager.getAction(IdeActions.GROUP_WELCOME_SCREEN_QUICKSTART);
318 DefaultActionGroup group = new DefaultActionGroup();
319 collectAllActions(group, quickStart);
321 for (AnAction action : group.getChildren(null)) {
322 JPanel button = new JPanel(new BorderLayout());
323 button.setOpaque(false);
324 button.setBorder(new EmptyBorder(8, 20, 8, 20));
325 Presentation presentation = action.getTemplatePresentation();
326 action.update(new AnActionEvent(null, DataManager.getInstance().getDataContext(this),
327 ActionPlaces.WELCOME_SCREEN, presentation, ActionManager.getInstance(), 0));
328 if (presentation.isVisible()) {
329 String text = presentation.getText();
330 if (text.endsWith("...")) {
331 text = text.substring(0, text.length() - 3);
333 Icon icon = presentation.getIcon();
334 if (icon.getIconHeight() != 16 || icon.getIconWidth() != 16) {
335 icon = EmptyIcon.ICON_16;
337 ActionLink link = new ActionLink(text, icon, action);
338 link.setPaintUnderline(false);
339 link.setNormalColor(getLinkNormalColor());
341 if (action instanceof WelcomePopupAction) {
342 button.add(createArrow(link), BorderLayout.EAST);
344 installFocusable(button, action, KeyEvent.VK_UP, KeyEvent.VK_DOWN, true);
349 actions.setBorder(new EmptyBorder(0, 0, 0, 0));
350 WelcomeScreenActionsPanel panel = new WelcomeScreenActionsPanel();
351 panel.actions.add(actions);
355 private void collectAllActions(DefaultActionGroup group, ActionGroup actionGroup) {
356 for (AnAction action : actionGroup.getChildren(null)) {
357 if (action instanceof ActionGroup) {
358 collectAllActions(group, (ActionGroup)action);
365 private JComponent createLogo() {
366 NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
367 ApplicationInfoEx app = ApplicationInfoEx.getInstanceEx();
368 JLabel logo = new JLabel(IconLoader.getIcon(app.getWelcomeScreenLogoUrl()));
369 logo.setHorizontalAlignment(SwingConstants.CENTER);
370 panel.add(logo, BorderLayout.NORTH);
371 JLabel appName = new JLabel(ApplicationNamesInfo.getInstance().getFullProductName());
372 Font font = getProductFont();
373 appName.setForeground(JBColor.foreground());
374 appName.setFont(font.deriveFont(36f).deriveFont(Font.PLAIN));
375 appName.setHorizontalAlignment(SwingConstants.CENTER);
376 String appVersion = "Version " + app.getFullVersion();
378 if (app.isEAP() && app.getBuild().getBuildNumber() < Integer.MAX_VALUE) {
379 appVersion += " (" + app.getBuild().asString() + ")";
382 JLabel version = new JLabel(appVersion);
383 version.setFont(font.deriveFont(16f).deriveFont(Font.PLAIN));
384 version.setHorizontalAlignment(SwingConstants.CENTER);
385 version.setForeground(Gray._128);
388 panel.add(version, BorderLayout.SOUTH);
389 panel.setBorder(new EmptyBorder(20, 10, 30, 10));
393 private Font getProductFont() {
394 String name = "/fonts/Roboto-Light.ttf";
395 URL url = AppUIUtil.class.getResource(name);
397 Logger.getInstance(AppUIUtil.class).warn("Resource missing: " + name);
401 InputStream is = url.openStream();
403 return Font.createFont(Font.TRUETYPE_FONT, is);
409 catch (Throwable t) {
410 Logger.getInstance(AppUIUtil.class).warn("Cannot load font: " + url, t);
413 return UIUtil.getLabelFont();
416 private JComponent createRecentProjects() {
417 JPanel panel = new JPanel(new BorderLayout());
418 panel.add(new NewRecentProjectPanel(this), BorderLayout.CENTER);
419 panel.setBackground(getProjectsBackground());
420 panel.setBorder(new CustomLineBorder(getSeparatorColor(), 0,0,0,1));
424 private void installFocusable(final JComponent comp, final AnAction action, final int prevKeyCode, final int nextKeyCode, final boolean focusListOnLeft) {
425 comp.setFocusable(true);
426 comp.setFocusTraversalKeysEnabled(true);
427 comp.addKeyListener(new KeyAdapter() {
429 public void keyPressed(KeyEvent e) {
430 final JList list = UIUtil.findComponentOfType(FlatWelcomeFrame.this.getComponent(), JList.class);
431 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
432 action.actionPerformed(new AnActionEvent(e,
433 DataManager.getInstance().getDataContext(),
434 ActionPlaces.WELCOME_SCREEN,
435 action.getTemplatePresentation().clone(),
436 ActionManager.getInstance(),
438 } else if (e.getKeyCode() == prevKeyCode) {
440 } else if (e.getKeyCode() == nextKeyCode) {
442 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
443 if (focusListOnLeft) {
450 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
455 comp.addFocusListener(new FocusListener() {
457 public void focusGained(FocusEvent e) {
458 comp.setOpaque(true);
459 comp.setBackground(getActionLinkSelectionColor());
463 public void focusLost(FocusEvent e) {
464 comp.setOpaque(false);
465 comp.setBackground(getMainBackground());
471 protected void focusPrev(JComponent comp) {
472 FocusTraversalPolicy policy = FlatWelcomeFrame.this.getFocusTraversalPolicy();
473 if (policy != null) {
474 Component prev = policy.getComponentBefore(FlatWelcomeFrame.this, comp);
481 protected void focusNext(JComponent comp) {
482 FocusTraversalPolicy policy = FlatWelcomeFrame.this.getFocusTraversalPolicy();
483 if (policy != null) {
484 Component next = policy.getComponentAfter(FlatWelcomeFrame.this, comp);
492 public void setupFrame(JFrame frame) {
497 public void dispose() {
501 private class IconsFreeActionGroup extends ActionGroup {
502 private final ActionGroup myGroup;
504 public IconsFreeActionGroup(ActionGroup group) {
510 public AnAction[] getChildren(@Nullable AnActionEvent e) {
511 AnAction[] children = myGroup.getChildren(e);
512 AnAction[] patched = new AnAction[children.length];
513 for (int i = 0; i < children.length; i++) {
514 patched[i] = patch(children[i]);
519 private AnAction patch(final AnAction child) {
520 if (child instanceof ActionGroup) {
521 return new IconsFreeActionGroup((ActionGroup)child);
524 Presentation presentation = child.getTemplatePresentation();
525 return new AnAction(presentation.getText(),
526 presentation.getDescription(),
529 public void actionPerformed(@NotNull AnActionEvent e) {
530 child.actionPerformed(e);
534 public void update(@NotNull AnActionEvent e) {
536 e.getPresentation().setIcon(null);
540 public boolean isDumbAware() {
541 return child.isDumbAware();
548 private static JLabel createArrow(final ActionLink link) {
549 JLabel arrow = new JLabel(AllIcons.General.Combo3);
550 arrow.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
551 arrow.setVerticalAlignment(SwingConstants.BOTTOM);
552 new ClickListener() {
554 public boolean onClick(@NotNull MouseEvent e, int clickCount) {
555 final MouseEvent newEvent = new MouseEvent(link, e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getClickCount(),
556 e.isPopupTrigger(), e.getButton());
557 link.doClick(newEvent);
565 public BalloonLayout getBalloonLayout() {
566 return myBalloonLayout;
570 public Rectangle suggestChildFrameBounds() {
576 public Project getProject() {
577 return ProjectManager.getInstance().getDefaultProject();
581 public void setFrameTitle(String title) {
586 public void setFileTitle(String fileTitle, File ioFile) {
591 public IdeRootPaneNorthExtension getNorthExtension(String key) {
596 public JComponent getComponent() {
597 return getRootPane();
600 public static void notifyFrameClosed(JFrame frame) {
601 saveLocation(frame.getBounds());
604 public static class WelcomeScreenActionsPanel {
606 private JPanel actions;