blob: 3528ac6f05d49dd6f0db16488e51bc9a3f8eb70f [file] [log] [blame]
/*
* 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.avdmanager;
import com.android.sdklib.internal.avd.AvdInfo;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.JBMenuItem;
import com.intellij.openapi.ui.JBPopupMenu;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.List;
/**
* An action panel that behaves similarly to an Android overflow menu. Actions which
* do not fit in the space provided are relegated to an overflow menu that can be invoked by
* clicking on the last icon of the menu.
*/
public class AvdActionPanel extends JPanel implements AvdUiAction.AvdInfoProvider {
@NotNull private final AvdInfo myAvdInfo;
private final AvdRefreshProvider myRefreshProvider;
private final JBPopupMenu myOverflowMenu = new JBPopupMenu();
private final FocusableHyperlinkLabel myOverflowMenuButton = new FocusableHyperlinkLabel("", AllIcons.ToolbarDecorator.Mac.MoveDown);
private final Border myMargins = IdeBorderFactory.createEmptyBorder(5, 3, 5, 3);
public List<FocusableHyperlinkLabel> myVisibleComponents = Lists.newArrayList();
private boolean myFocused;
private int myFocusedComponent = -1;
public interface AvdRefreshProvider {
void refreshAvds();
@Nullable Project getProject();
void notifyRun();
@NotNull JComponent getComponent();
}
public AvdActionPanel(@NotNull AvdInfo avdInfo, int numVisibleActions, AvdRefreshProvider refreshProvider) {
myRefreshProvider = refreshProvider;
setOpaque(true);
setBorder(IdeBorderFactory.createEmptyBorder(10, 10, 10, 10));
myAvdInfo = avdInfo;
List<AvdUiAction> actions = getActions();
setLayout(new FlowLayout(FlowLayout.RIGHT, 3, 0));
int visibleActionCount = 0;
boolean errorState = false;
if (avdInfo.getStatus() != AvdInfo.AvdStatus.OK) {
if (AvdManagerConnection.isAvdRepairable(avdInfo.getStatus())) {
RepairAvdAction action = new RepairAvdAction(this);
FocusableHyperlinkLabel repairAction = new FocusableHyperlinkLabel(action.getText(), action.getIcon());
add(repairAction);
repairAction.addHyperlinkListener(action);
myVisibleComponents.add(repairAction);
} else {
add(new JBLabel("Failed to load", AllIcons.General.BalloonError, SwingConstants.LEADING));
}
numVisibleActions = 0;
errorState = true;
}
for (AvdUiAction action : actions) {
JComponent actionLabel;
// Add extra items to the overflow menu
if (errorState || numVisibleActions != -1 && visibleActionCount >= numVisibleActions) {
JBMenuItem menuItem = new JBMenuItem(action);
myOverflowMenu.add(menuItem);
actionLabel = menuItem;
} else {
// Add visible items to the panel
actionLabel = new FocusableHyperlinkLabel("", action.getIcon());
((FocusableHyperlinkLabel)actionLabel).addHyperlinkListener(action);
add(actionLabel);
myVisibleComponents.add((FocusableHyperlinkLabel)actionLabel);
visibleActionCount++;
}
actionLabel.setToolTipText(action.getDescription());
actionLabel.setBorder(myMargins);
}
myOverflowMenuButton.setBorder(myMargins);
add(myOverflowMenuButton);
myVisibleComponents.add(myOverflowMenuButton);
myOverflowMenuButton.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
myOverflowMenu
.show(myOverflowMenuButton, myOverflowMenuButton.getX() - myOverflowMenu.getPreferredSize().width, myOverflowMenuButton.getY());
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == KeyEvent.VK_ENTER || e.getKeyChar() == KeyEvent.VK_SPACE) {
runFocusedAction();
}
}
});
}
@NotNull
private List<AvdUiAction> getActions() {
return ImmutableList.of(new RunAvdAction(this),
new EditAvdAction(this),
new DuplicateAvdAction(this),
//new ExportAvdAction(this), // TODO: implement export/import
new WipeAvdDataAction(this),
new ShowAvdOnDiskAction(this),
new AvdSummaryAction(this),
new DeleteAvdAction(this),
new StopAvdAction(this));
}
@NotNull
@Override
public AvdInfo getAvdInfo() {
return myAvdInfo;
}
@Override
public void refreshAvds() {
myRefreshProvider.refreshAvds();
}
@Nullable
@Override
public Project getProject() {
return myRefreshProvider.getProject();
}
@NotNull
@Override
public JComponent getComponent() {
return myRefreshProvider.getComponent();
}
@Override
public void notifyRun() {
myRefreshProvider.notifyRun();
}
public void showPopup(@NotNull Component c, @NotNull MouseEvent e) {
myOverflowMenu.show(c, e.getX(), e.getY());
}
public void runFocusedAction() {
myVisibleComponents.get(myFocusedComponent).doClick();
}
public boolean cycleFocus(boolean backward) {
if (backward) {
if (myFocusedComponent == -1) {
myFocusedComponent = myVisibleComponents.size() - 1;
return true;
} else {
myFocusedComponent--;
return myFocusedComponent != -1;
}
} else {
if (myFocusedComponent == myVisibleComponents.size() - 1) {
myFocusedComponent = -1;
return false;
} else {
myFocusedComponent++;
return true;
}
}
}
public void setFocused(boolean focused) {
myFocused = focused;
if (!focused) {
myFocusedComponent = -1;
}
}
private class FocusableHyperlinkLabel extends HyperlinkLabel {
FocusableHyperlinkLabel(String text, Icon icon) {
super(text, JBColor.foreground(), JBColor.background(), JBColor.foreground());
setIcon(icon);
setOpaque(false);
setUseIconAsLink(true);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (myFocused && myFocusedComponent != -1 && myVisibleComponents.get(myFocusedComponent) == this) {
g.setColor(UIUtil.getTableSelectionForeground());
UIUtil.drawDottedRectangle(g, 0, 0, getWidth() - 2, getHeight() - 2);
}
}
}
}