| /* |
| * 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; |
| } |
| } |
| } |