blob: 60ed663ee7adbbcb54fd81dbbe28bc908e8e1584 [file] [log] [blame]
/*
* Copyright (C) 2015 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.gradle.editor.ui;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.FixedComboBoxEditor;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.Gray;
import com.intellij.util.ui.MacUIUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.ComboPopup;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* This is a copy-paste from {@link FixedComboBoxEditor} with the difference that it uses gradle editor background color for painting
* border ({@link MacComboBoxEditorBorder#paintBorder(Component, Graphics, int, int, int, int)})
*/
public class GradleEditorComboBoxEditor implements ComboBoxEditor {
public static final Border EDITOR_BORDER = new MacComboBoxEditorBorder(false);
public static final Border DISABLED_EDITOR_BORDER = new MacComboBoxEditorBorder(true);
private JTextField myField;
private Object oldValue;
public GradleEditorComboBoxEditor() {
if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) {
myField = new MacComboBoxTextField();
} else {
myField = new JTextField();
myField.setBorder(null);
}
}
protected JTextField getField() {
return myField;
}
@Override
public Component getEditorComponent() {
return myField;
}
@Override
public void setItem(Object anObject) {
if (anObject != null) {
myField.setText(anObject.toString());
oldValue = anObject;
}
else {
myField.setText("");
}
}
@SuppressWarnings("RedundantArrayCreation")
@Override
public Object getItem() {
Object newValue = myField.getText();
if (oldValue != null && !(oldValue instanceof String)) {
// The original value is not a string. Should return the value in it's
// original type.
if (newValue.equals(oldValue.toString())) {
return oldValue;
}
else {
// Must take the value from the editor and get the value and cast it to the new type.
Class<?> cls = oldValue.getClass();
try {
Method method = cls.getMethod("valueOf", new Class[]{String.class});
newValue = method.invoke(oldValue, new Object[]{myField.getText()});
}
catch (Exception ex) {
// Fail silently and return the newValue (a String object)
}
}
}
return newValue;
}
@Override
public void selectAll() {
myField.selectAll();
myField.requestFocus();
}
@Override
public void addActionListener(ActionListener l) {
}
@Override
public void removeActionListener(ActionListener l) {
}
@Nullable
private static ComboPopup getComboboxPopup(final JComboBox comboBox) {
final ComboBoxUI ui = comboBox.getUI();
ComboPopup popup = null;
if (ui instanceof BasicComboBoxUI) {
try {
final Field popupField = BasicComboBoxUI.class.getDeclaredField("popup");
popupField.setAccessible(true);
popup = (ComboPopup)popupField.get(ui);
}
catch (NoSuchFieldException e1) {
popup = null;
}
catch (IllegalAccessException e1) {
popup = null;
}
}
return popup;
}
private class MacComboBoxTextField extends JTextField implements DocumentListener, FocusListener {
private MacComboBoxTextField() {
setBorder(isEnabled() ? EDITOR_BORDER : DISABLED_EDITOR_BORDER);
//setFont(UIUtil.getListFont());
final InputMap inputMap = getInputMap();
inputMap.put(KeyStroke.getKeyStroke("DOWN"), "aquaSelectNext");
inputMap.put(KeyStroke.getKeyStroke("KP_DOWN"), "aquaSelectNext");
inputMap.put(KeyStroke.getKeyStroke("UP"), "aquaSelectPrevious");
inputMap.put(KeyStroke.getKeyStroke("KP_UP"), "aquaSelectPrevious");
inputMap.put(KeyStroke.getKeyStroke("HOME"), "aquaSelectHome");
inputMap.put(KeyStroke.getKeyStroke("END"), "aquaSelectEnd");
inputMap.put(KeyStroke.getKeyStroke("PAGE_UP"), "aquaSelectPageUp");
inputMap.put(KeyStroke.getKeyStroke("PAGE_DOWN"), "aquaSelectPageDown");
inputMap.put(KeyStroke.getKeyStroke("ENTER"), "aquaEnterPressed");
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "aquaSpacePressed");
//getActionMap().put("macEnterPressed", macEnterPressedAction);
//getDocument().addDocumentListener(this);
addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("enabled".equals(evt.getPropertyName())) {
setBorder(Boolean.TRUE.equals(evt.getNewValue()) ? EDITOR_BORDER : DISABLED_EDITOR_BORDER);
repaint();
}
}
});
addFocusListener(this);
}
@Override
public boolean hasFocus() {
final Container parent = getParent();
if (parent instanceof ComboBox) {
try {
Field field = parent.getClass().getDeclaredField("myPaintingNow");
field.setAccessible(true);
if (field.get(parent) == Boolean.TRUE) {
return false; // to disable focus painting around combobox button
}
}
catch (Exception ignore) {
}
}
return super.hasFocus();
}
@Override
public void focusGained(FocusEvent e) {
repaintCombobox();
}
private void repaintCombobox() {
final Container parent = getParent();
if (parent == null) return;
if (parent instanceof JComponent && Boolean.TRUE == ((JComponent)parent).getClientProperty("JComboBox.isTableCellEditor")) return;
final Container grandParent = parent.getParent();
if (grandParent != null) {
grandParent.repaint();
}
}
@Override
public void focusLost(FocusEvent e) {
repaintCombobox();
}
@Override
public Dimension getMinimumSize() {
final Dimension minimumSize = super.getMinimumSize();
return new Dimension(minimumSize.width, minimumSize.height + 2);
}
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
@Override
public void setBounds(final int x, final int y, final int width, final int height) {
UIUtil.setComboBoxEditorBounds(x, y, width, height, this);
}
@Override
public void insertUpdate(DocumentEvent e) {
textChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
textChanged();
}
@Override
public void changedUpdate(DocumentEvent e) {
textChanged();
}
private void textChanged() {
final Container ancestor = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
if (ancestor == null || !ancestor.isVisible()) return;
final JComboBox comboBox = (JComboBox)ancestor;
if (!comboBox.isPopupVisible()) return;
final ComboPopup popup = getComboboxPopup(comboBox);
if (popup == null) return;
String s = myField.getText();
final ListModel listmodel = comboBox.getModel();
int i = listmodel.getSize();
if (s.length() > 0) {
for (int j = 0; j < i; j++) {
Object obj = listmodel.getElementAt(j);
if (obj == null) continue;
String s1 = obj.toString();
if (s1 != null && (s1.startsWith(s) || s1.equals(s))) {
popup.getList().setSelectedIndex(j);
return;
}
}
}
popup.getList().clearSelection();
}
}
public static class MacComboBoxEditorBorder implements Border {
private boolean myDisabled;
public MacComboBoxEditorBorder(final boolean disabled) {
myDisabled = disabled;
}
@Override
public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) {
Color topColor;
Color secondTopColor;
Color leftRightColor;
Color bottomColor;
if (myDisabled) {
topColor = Gray._200;
secondTopColor = Gray._250;
leftRightColor = Gray._205;
bottomColor = Gray._220;
}
else {
topColor = Gray._150;
secondTopColor = Gray._230;
leftRightColor = Gray._175;
bottomColor = Gray._200;
}
int _y = y + MacUIUtil.MAC_COMBO_BORDER_V_OFFSET;
g.setColor(topColor);
g.drawLine(x + 3, _y + 3, x + width - 1, _y + 3);
g.setColor(secondTopColor);
g.drawLine(x + 3, _y + 4, x + width - 1, _y + 4);
g.setColor(leftRightColor);
g.drawLine(x + 3, _y + 4, x + 3, _y + height - 4);
g.drawLine(x + width - 1, _y + 4, x + width - 1, _y + height - 4);
g.setColor(bottomColor);
g.drawLine(x + 4, _y + height - 4, x + width - 2, _y + height - 4);
g.setColor(GradleEditorUiConstants.BACKGROUND_COLOR);
g.fillRect(x, y, width, 3 + (SystemInfo.isMacOSLion ? 1 : 0));
g.fillRect(x, _y, 3, height);
g.fillRect(x, _y + height - 3, width, 3);
}
@Override
public Insets getBorderInsets(final Component c) {
return new Insets(6, 6, 4, 3);
}
@Override
public boolean isBorderOpaque() {
return true;
}
}
}