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.GearPlain, !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(), JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false, ActionPlaces.WELCOME_SCREEN);
298 popup.showUnderneathOfLabel(ref.get());
301 ref.set(new ActionLink(text, icon, action));
302 ref.get().setPaintUnderline(false);
303 ref.get().setNormalColor(getLinkNormalColor());
304 NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
305 panel.setBorder(new EmptyBorder(4, 10, 4, 10));
306 panel.add(ref.get());
307 panel.add(createArrow(ref.get()), BorderLayout.EAST);
308 installFocusable(panel, action, KeyEvent.VK_UP, KeyEvent.VK_DOWN, focusListOnLeft);
312 private JComponent createActionPanel() {
313 JPanel actions = new NonOpaquePanel();
314 actions.setLayout(new BoxLayout(actions, BoxLayout.Y_AXIS));
315 ActionManager actionManager = ActionManager.getInstance();
316 ActionGroup quickStart = (ActionGroup)actionManager.getAction(IdeActions.GROUP_WELCOME_SCREEN_QUICKSTART);
317 DefaultActionGroup group = new DefaultActionGroup();
318 collectAllActions(group, quickStart);
320 for (AnAction action : group.getChildren(null)) {
321 JPanel button = new JPanel(new BorderLayout());
322 button.setOpaque(false);
323 button.setBorder(new EmptyBorder(8, 20, 8, 20));
324 Presentation presentation = action.getTemplatePresentation();
325 action.update(new AnActionEvent(null, DataManager.getInstance().getDataContext(this),
326 ActionPlaces.WELCOME_SCREEN, presentation, ActionManager.getInstance(), 0));
327 if (presentation.isVisible()) {
328 String text = presentation.getText();
329 if (text.endsWith("...")) {
330 text = text.substring(0, text.length() - 3);
332 Icon icon = presentation.getIcon();
333 if (icon.getIconHeight() != 16 || icon.getIconWidth() != 16) {
334 icon = EmptyIcon.ICON_16;
336 ActionLink link = new ActionLink(text, icon, action);
337 link.setPaintUnderline(false);
338 link.setNormalColor(getLinkNormalColor());
340 if (action instanceof WelcomePopupAction) {
341 button.add(createArrow(link), BorderLayout.EAST);
343 installFocusable(button, action, KeyEvent.VK_UP, KeyEvent.VK_DOWN, true);
348 actions.setBorder(new EmptyBorder(0, 0, 0, 0));
349 WelcomeScreenActionsPanel panel = new WelcomeScreenActionsPanel();
350 panel.actions.add(actions);
354 private void collectAllActions(DefaultActionGroup group, ActionGroup actionGroup) {
355 for (AnAction action : actionGroup.getChildren(null)) {
356 if (action instanceof ActionGroup) {
357 collectAllActions(group, (ActionGroup)action);
364 private JComponent createLogo() {
365 NonOpaquePanel panel = new NonOpaquePanel(new BorderLayout());
366 ApplicationInfoEx app = ApplicationInfoEx.getInstanceEx();
367 JLabel logo = new JLabel(IconLoader.getIcon(app.getWelcomeScreenLogoUrl()));
368 logo.setHorizontalAlignment(SwingConstants.CENTER);
369 panel.add(logo, BorderLayout.NORTH);
370 JLabel appName = new JLabel(ApplicationNamesInfo.getInstance().getFullProductName());
371 Font font = getProductFont();
372 appName.setForeground(JBColor.foreground());
373 appName.setFont(font.deriveFont(36f).deriveFont(Font.PLAIN));
374 appName.setHorizontalAlignment(SwingConstants.CENTER);
375 String appVersion = "Version " + app.getFullVersion();
377 if (app.isEAP() && app.getBuild().getBuildNumber() < Integer.MAX_VALUE) {
378 appVersion += " (" + app.getBuild().asString() + ")";
381 JLabel version = new JLabel(appVersion);
382 version.setFont(font.deriveFont(16f).deriveFont(Font.PLAIN));
383 version.setHorizontalAlignment(SwingConstants.CENTER);
384 version.setForeground(Gray._128);
387 panel.add(version, BorderLayout.SOUTH);
388 panel.setBorder(new EmptyBorder(20, 10, 30, 10));
392 private Font getProductFont() {
393 String name = "/fonts/Roboto-Light.ttf";
394 URL url = AppUIUtil.class.getResource(name);
396 Logger.getInstance(AppUIUtil.class).warn("Resource missing: " + name);
400 InputStream is = url.openStream();
402 return Font.createFont(Font.TRUETYPE_FONT, is);
408 catch (Throwable t) {
409 Logger.getInstance(AppUIUtil.class).warn("Cannot load font: " + url, t);
412 return UIUtil.getLabelFont();
415 private JComponent createRecentProjects() {
416 JPanel panel = new JPanel(new BorderLayout());
417 panel.add(new NewRecentProjectPanel(this), BorderLayout.CENTER);
418 panel.setBackground(getProjectsBackground());
419 panel.setBorder(new CustomLineBorder(getSeparatorColor(), 0,0,0,1));
423 private void installFocusable(final JComponent comp, final AnAction action, final int prevKeyCode, final int nextKeyCode, final boolean focusListOnLeft) {
424 comp.setFocusable(true);
425 comp.setFocusTraversalKeysEnabled(true);
426 comp.addKeyListener(new KeyAdapter() {
428 public void keyPressed(KeyEvent e) {
429 final JList list = UIUtil.findComponentOfType(FlatWelcomeFrame.this.getComponent(), JList.class);
430 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
431 InputEvent event = e;
432 if (e.getComponent() instanceof JComponent) {
433 ActionLink link = UIUtil.findComponentOfType((JComponent)e.getComponent(), ActionLink.class);
435 event = new MouseEvent(link, MouseEvent.MOUSE_CLICKED, e.getWhen(), e.getModifiers(), 0, 0, 1, false, MouseEvent.BUTTON1);
438 action.actionPerformed(new AnActionEvent(event,
439 DataManager.getInstance().getDataContext(),
440 ActionPlaces.WELCOME_SCREEN,
441 action.getTemplatePresentation().clone(),
442 ActionManager.getInstance(),
444 } else if (e.getKeyCode() == prevKeyCode) {
446 } else if (e.getKeyCode() == nextKeyCode) {
448 } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
449 if (focusListOnLeft) {
456 } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
461 comp.addFocusListener(new FocusListener() {
463 public void focusGained(FocusEvent e) {
464 comp.setOpaque(true);
465 comp.setBackground(getActionLinkSelectionColor());
469 public void focusLost(FocusEvent e) {
470 comp.setOpaque(false);
471 comp.setBackground(getMainBackground());
477 protected void focusPrev(JComponent comp) {
478 FocusTraversalPolicy policy = FlatWelcomeFrame.this.getFocusTraversalPolicy();
479 if (policy != null) {
480 Component prev = policy.getComponentBefore(FlatWelcomeFrame.this, comp);
487 protected void focusNext(JComponent comp) {
488 FocusTraversalPolicy policy = FlatWelcomeFrame.this.getFocusTraversalPolicy();
489 if (policy != null) {
490 Component next = policy.getComponentAfter(FlatWelcomeFrame.this, comp);
498 public void setupFrame(JFrame frame) {
503 public void dispose() {
507 private class IconsFreeActionGroup extends ActionGroup {
508 private final ActionGroup myGroup;
510 public IconsFreeActionGroup(ActionGroup group) {
511 super(group.getTemplatePresentation().getText(), group.getTemplatePresentation().getDescription(), null);
516 public boolean isPopup() {
517 return myGroup.isPopup();
522 public AnAction[] getChildren(@Nullable AnActionEvent e) {
523 AnAction[] children = myGroup.getChildren(e);
524 AnAction[] patched = new AnAction[children.length];
525 for (int i = 0; i < children.length; i++) {
526 patched[i] = patch(children[i]);
531 private AnAction patch(final AnAction child) {
532 if (child instanceof ActionGroup) {
533 return new IconsFreeActionGroup((ActionGroup)child);
536 Presentation presentation = child.getTemplatePresentation();
537 return new AnAction(presentation.getText(),
538 presentation.getDescription(),
541 public void actionPerformed(@NotNull AnActionEvent e) {
542 child.actionPerformed(e);
546 public void update(@NotNull AnActionEvent e) {
548 e.getPresentation().setIcon(null);
552 public boolean isDumbAware() {
553 return child.isDumbAware();
560 private static JLabel createArrow(final ActionLink link) {
561 JLabel arrow = new JLabel(AllIcons.General.Combo3);
562 arrow.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
563 arrow.setVerticalAlignment(SwingConstants.BOTTOM);
564 new ClickListener() {
566 public boolean onClick(@NotNull MouseEvent e, int clickCount) {
567 final MouseEvent newEvent = new MouseEvent(link, e.getID(), e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getClickCount(),
568 e.isPopupTrigger(), e.getButton());
569 link.doClick(newEvent);
577 public BalloonLayout getBalloonLayout() {
578 return myBalloonLayout;
582 public Rectangle suggestChildFrameBounds() {
588 public Project getProject() {
589 return ProjectManager.getInstance().getDefaultProject();
593 public void setFrameTitle(String title) {
598 public void setFileTitle(String fileTitle, File ioFile) {
603 public IdeRootPaneNorthExtension getNorthExtension(String key) {
608 public JComponent getComponent() {
609 return getRootPane();
612 public static void notifyFrameClosed(JFrame frame) {
613 saveLocation(frame.getBounds());
616 public static class WelcomeScreenActionsPanel {
618 private JPanel actions;