fix comboboxes UI under Mac OS X LAF
authorAlexey Pegov <alexey.pegov@jetbrains.com>
Tue, 24 Aug 2010 14:01:34 +0000 (18:01 +0400)
committerAlexey Pegov <alexey.pegov@jetbrains.com>
Tue, 24 Aug 2010 17:09:54 +0000 (21:09 +0400)
16 files changed:
java/debugger/impl/src/com/intellij/debugger/ui/DebuggerExpressionComboBox.java
java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/BreakpointPropertiesPanel.form
platform/lang-impl/src/com/intellij/ui/StringComboboxEditor.java
platform/platform-api/src/com/intellij/openapi/ui/ComboBox.java
platform/platform-api/src/com/intellij/openapi/ui/ComboBoxWithWidePopup.java
platform/platform-api/src/com/intellij/openapi/ui/MacComboBoxEditor.java [new file with mode: 0644]
platform/platform-api/src/com/intellij/openapi/ui/TreeComboBox.java
platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java
platform/platform-impl/src/com/intellij/ui/ComboboxEditorTextField.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/EditorComboBox.java
platform/platform-impl/src/com/intellij/ui/EditorComboBoxEditor.java
platform/platform-impl/src/com/intellij/ui/EditorTextField.java
platform/util/src/com/intellij/util/ui/MacUIUtil.java [new file with mode: 0644]
platform/util/src/com/intellij/util/ui/UIUtil.java

index 339d844f2d8561ebb2d43d02802c2fc9796e9a4a..f14587391352884daca400a98eca17b307311d54 100644 (file)
@@ -55,7 +55,10 @@ public class DebuggerExpressionComboBox extends DebuggerEditorImpl {
     }
 
     public void setItem(Object item) {
-      super.setItem(createDocument((TextWithImports)item));
+      final Object currentItem = getItem();
+      if (currentItem == null || !currentItem.equals(item)) {
+        super.setItem(createDocument((TextWithImports)item));
+      }
       /* Causes PSI being modified from PSI events. See IDEADEV-22102 
       final Editor editor = getEditor();
       if (editor != null) {
@@ -77,43 +80,14 @@ public class DebuggerExpressionComboBox extends DebuggerEditorImpl {
     if (myComboBox.getItemCount() > 0) {
       myComboBox.setSelectedIndex(0);
     }
-    if(myComboBox.isEditable()) {
-      myComboBox.getEditor().setItem(item);
-    }
-    else {
-      myItem =  item;
-    }
+
+    myItem = item;
   }
 
   public TextWithImports getText() {
     return (TextWithImports)myComboBox.getEditor().getItem();
   }
 
-  protected void updateEditor(TextWithImports item) {
-    if(!myComboBox.isEditable()) return;
-
-    boolean focusOwner = myEditor != null && myEditor.getEditorComponent().isFocusOwner();
-    int offset = 0;
-    TextWithImports oldItem = null;
-    if(focusOwner) {
-      offset = myEditor.getEditor().getCaretModel().getOffset();
-      oldItem = (TextWithImports)myEditor.getItem();
-    }
-    myEditor = new MyEditorComboBoxEditor(getProject(), myFactory.getFileType());
-    myComboBox.setEditor(myEditor);
-    myComboBox.setRenderer(new EditorComboBoxRenderer(myEditor));
-
-    myComboBox.setMaximumRowCount(MAX_ROWS);
-
-    myEditor.setItem(item);
-    if(focusOwner) {
-      myEditor.getEditorComponent().requestFocus();
-      if(oldItem.equals(item)) {
-        myEditor.getEditor().getCaretModel().moveToOffset(offset);
-      }
-    }
-  }
-
   private void setRecents() {
     boolean focusOwner = myEditor != null && myEditor.getEditorComponent().isFocusOwner();
     myComboBox.removeAllItems();
@@ -137,20 +111,18 @@ public class DebuggerExpressionComboBox extends DebuggerEditorImpl {
   }
 
   public void setEnabled(boolean enabled) {
-      if(myComboBox.isEditable() == enabled) return;
-
-      myComboBox.setEnabled(enabled);
-      myComboBox.setEditable(enabled);
-
-      if(enabled) {
-        setRecents();
-        updateEditor(myItem);
-      } else {
-        myItem = (TextWithImports)myComboBox.getEditor().getItem();
-        myComboBox.removeAllItems();
-        myComboBox.addItem(myItem);
-      }
+    myComboBox.setEnabled(enabled);
+
+    if (enabled) {
+      setRecents();
+      myEditor.setItem(myItem);
+    }
+    else {
+      myItem = (TextWithImports)myComboBox.getEditor().getItem();
+      myComboBox.removeAllItems();
+      myComboBox.addItem(myItem);
     }
+  }
 
   public TextWithImports createText(String text, String importsString) {
     return new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, text, importsString);
@@ -172,15 +144,20 @@ public class DebuggerExpressionComboBox extends DebuggerEditorImpl {
   public DebuggerExpressionComboBox(Project project, PsiElement context, @NonNls String recentsId, final CodeFragmentFactory factory) {
     super(project, context, recentsId, factory);
     myComboBox = new ComboBox(-1);
+    myComboBox.setEditable(true);
+
+    myEditor = new MyEditorComboBoxEditor(getProject(), myFactory.getFileType());
+    myComboBox.setEditor(myEditor);
+    myComboBox.setRenderer(new EditorComboBoxRenderer(myEditor));
+
     // Have to turn this off because when used in DebuggerTreeInplaceEditor, the combobox popup is hidden on every change of selection
     // See comment to SynthComboBoxUI.FocusHandler.focusLost()
     myComboBox.setLightWeightPopupEnabled(false);
-    setLayout(new BorderLayout());
+    setLayout(new BorderLayout(0, 0));
     add(myComboBox);
 
     setText(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, ""));
     myItem =  createText("");
-    setEnabled(true);
   }
 
   public JComponent getPreferredFocusedComponent() {
index a3b69aae6d82106e19098997ccbc3da6f6ea0cec..cb70ee793db836c07a9c2f6974ad6f6e19cdc5de 100644 (file)
@@ -3,7 +3,7 @@
   <grid id="6570" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
     <margin top="0" left="0" bottom="0" right="0"/>
     <constraints>
-      <xy x="23" y="37" width="586" height="347"/>
+      <xy x="23" y="37" width="631" height="347"/>
     </constraints>
     <properties/>
     <border type="none"/>
                       <grid id="80950" binding="myLogExpressionComboPanel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
                         <margin top="0" left="0" bottom="0" right="0"/>
                         <constraints>
-                          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="1" fill="1" indent="0" use-parent-layout="false">
-                            <minimum-size width="-1" height="22"/>
-                            <maximum-size width="-1" height="22"/>
-                          </grid>
+                          <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
                         </constraints>
                         <properties/>
                         <border type="none"/>
                   <xy id="f3049" binding="myConditionComboPanel" layout-manager="XYLayout" hgap="-1" vgap="-1">
                     <margin top="0" left="0" bottom="0" right="0"/>
                     <constraints>
-                      <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false">
-                        <minimum-size width="-1" height="22"/>
-                        <maximum-size width="-1" height="22"/>
-                      </grid>
+                      <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
                     </constraints>
                     <properties/>
                     <border type="none"/>
index 914ff071b4fa8aff430dd7df5413ab5e78dfa6c9..fba1e1a402ff0fba5b373e726698028c4ec87626 100644 (file)
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.Result;
 import com.intellij.openapi.command.WriteCommandAction;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.StdFileTypes;
 import com.intellij.openapi.project.Project;
@@ -66,5 +67,8 @@ public class StringComboboxEditor extends EditorComboBoxEditor {
         getDocument().setText(s);
       }
     }.execute();
+
+    final Editor editor = getEditor();
+    if (editor != null) editor.getCaretModel().moveToOffset(s.length());
   }
 }
index 46ab42ddaf29dbcc52f617c857f08533134db92d..d2d775966d4852cb074027f276340b4f8d8d9ec7 100644 (file)
@@ -15,6 +15,8 @@
  */
 package com.intellij.openapi.ui;
 
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.ui.MacUIUtil;
 import com.intellij.util.ui.UIUtil;
 
 import javax.swing.*;
@@ -76,7 +78,6 @@ public class ComboBox extends ComboBoxWithWidePopup {
     if (v && !wasVisible && !UIUtil.isUnderNativeMacLookAndFeel()) {
       reconfigureEditor();
     }
-
   }
 
   private void reconfigureEditor() {
@@ -131,11 +132,18 @@ public class ComboBox extends ComboBoxWithWidePopup {
           }
         }
       }
-    }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+    }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
   }
 
   public final void setEditor(final ComboBoxEditor editor) {
-    super.setEditor(new MyEditor(this, editor));
+    ComboBoxEditor _editor = editor;
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) {
+      if ("AquaComboBoxEditor".equals(editor.getClass().getSimpleName())) {
+        _editor = new MacComboBoxEditor();
+      }
+    }
+
+    super.setEditor(new MyEditor(this, _editor));
   }
 
   public final Dimension getMinimumSize() {
@@ -171,6 +179,12 @@ public class ComboBox extends ComboBoxWithWidePopup {
     return height;
   }
 
+  @Override
+  public void paint(Graphics g) {
+    super.paint(g);
+    MacUIUtil.drawComboboxFocusRing(this, g);
+  }
+
   private static final class MyEditor implements ComboBoxEditor {
     private final JComboBox myComboBox;
     private final ComboBoxEditor myDelegate;
index 3b89b6bf44b0154b8a2741261d646efc22c8dbe7..9af7d9f2fc05a58a13d793dd47d24954d8013176 100644 (file)
@@ -16,6 +16,9 @@
 
 package com.intellij.openapi.ui;
 
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.util.ui.UIUtil;
+
 import javax.swing.*;
 import java.util.Vector;
 import java.awt.*;
@@ -26,14 +29,20 @@ public class ComboBoxWithWidePopup extends JComboBox {
 
   public ComboBoxWithWidePopup(final ComboBoxModel aModel) {
     super(aModel);
+
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25);
   }
 
   public ComboBoxWithWidePopup(final Object items[]) {
     super(items);
+
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25);
   }
 
   public ComboBoxWithWidePopup(final Vector<?> items) {
     super(items);
+
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25);
   }
 
   public ComboBoxWithWidePopup() {
diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MacComboBoxEditor.java b/platform/platform-api/src/com/intellij/openapi/ui/MacComboBoxEditor.java
new file mode 100644 (file)
index 0000000..9bbcbd1
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.ui;
+
+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.BasicComboBoxEditor;
+import javax.swing.plaf.basic.BasicComboBoxUI;
+import javax.swing.plaf.basic.ComboPopup;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+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;
+
+/**
+ * User: spLeaner
+ */
+public class MacComboBoxEditor implements ComboBoxEditor {
+  public static final Border EDITOR_BORDER = new MacComboBoxEditorBorder(false);
+  public static final Border DISABLED_EDITOR_BORDER = new MacComboBoxEditorBorder(true);
+
+  private MacComboBoxTextField myField;
+  private Object oldValue;
+
+  public MacComboBoxEditor() {
+    myField = new MacComboBoxTextField();
+  }
+
+  @Override
+  public Component getEditorComponent() {
+    return myField;
+  }
+
+  public void setItem(Object anObject) {
+    if (anObject != null) {
+      myField.setText(anObject.toString());
+      oldValue = anObject;
+    }
+    else {
+      myField.setText("");
+    }
+  }
+
+  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;
+  }
+
+  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 boolean myRepaintingParent;
+
+    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() {
+      if (myRepaintingParent) {
+        return false; // to disable focus painting around combobox button
+      }
+      return super.hasFocus();
+    }
+
+    @Override
+    public void focusGained(FocusEvent e) {
+      repaintCombobox();
+    }
+
+    private void repaintCombobox() {
+      final Container parent = getParent();
+      assert parent != null;
+      final Container grandParent = parent.getParent();
+      if (grandParent != null) {
+        myRepaintingParent = true;
+        grandParent.repaint();
+        SwingUtilities.invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            myRepaintingParent = false;
+          }
+        });
+      }
+    }
+
+    @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 = new Color(200, 200, 200);
+        secondTopColor = new Color(250, 250, 250);
+        leftRightColor = new Color(205, 205, 205);
+        bottomColor = new Color(220, 220, 220);
+      }
+      else {
+        topColor = new Color(150, 150, 150);
+        secondTopColor = new Color(230, 230, 230);
+        leftRightColor = new Color(175, 175, 175);
+        bottomColor = new Color(200, 200, 200);
+      }
+
+      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(UIManager.getColor("Panel.background"));
+
+      g.fillRect(x, y, width, 3);
+      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;
+    }
+  }
+
+  private static abstract class MacComboBoxAction extends AbstractAction {
+
+    @Override
+    public void actionPerformed(final ActionEvent e) {
+      final Object source = e.getSource();
+      if (source instanceof JComboBox) {
+        final JComboBox comboBox = (JComboBox)source;
+        if (!comboBox.isEnabled() || !comboBox.isShowing()) return;
+
+        if (comboBox.isPopupVisible()) {
+          ComboPopup popup = getComboboxPopup(comboBox);
+          if (popup != null) {
+            performComboBoxAction(comboBox, popup);
+          }
+        }
+        else {
+          comboBox.setPopupVisible(true);
+        }
+      }
+    }
+
+    protected abstract void performComboBoxAction(final JComboBox comboBox, final ComboPopup popup);
+  }
+
+  private AbstractAction macEnterPressedAction = new AbstractAction() {
+    @Override
+    public void actionPerformed(ActionEvent e) {
+      if (!myField.isEnabled() || !myField.isShowing()) return;
+
+      final Container ancestor = SwingUtilities.getAncestorOfClass(JComboBox.class, myField);
+      if (ancestor == null) return;
+
+      final JComboBox comboBox = (JComboBox)ancestor;
+      if (!comboBox.isEnabled()) return;
+
+      if (!comboBox.isPopupVisible()) {
+        selectAll();
+        return;
+      }
+
+      ComboPopup popup = getComboboxPopup(comboBox);
+      if (popup != null) {
+        if (popup.getList().getSelectedIndex() < 0) {
+          comboBox.setPopupVisible(false);
+        }
+
+        if (Boolean.TRUE.equals(comboBox.getClientProperty("JComboBox.isTableCellEditor"))) {
+          comboBox.setSelectedIndex(popup.getList().getSelectedIndex());
+          return;
+        }
+
+        if (comboBox.isPopupVisible()) {
+          comboBox.setSelectedIndex(popup.getList().getSelectedIndex());
+          comboBox.setPopupVisible(false);
+
+          selectAll();
+        }
+      }
+    }
+  };
+
+  private MacComboBoxAction highlightNextAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(final JComboBox comboBox, final ComboPopup popup) {
+      int i = comboBox.getSelectedIndex();
+
+      if (i < comboBox.getModel().getSize() - 1) {
+        comboBox.setSelectedIndex(i + 1);
+        //comboBox.ensureIndexIsVisible(i + 1);
+      }
+
+      comboBox.repaint();
+    }
+  };
+
+  private MacComboBoxAction highlightPreviousAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(final JComboBox comboBox, final ComboPopup popup) {
+      final JList list = popup.getList();
+      int i = list.getSelectedIndex();
+      if (i > 0) {
+        list.setSelectedIndex(i - 1);
+        list.ensureIndexIsVisible(i - 1);
+      }
+
+      list.repaint();
+    }
+  };
+
+  private MacComboBoxAction highlightFirstAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(final JComboBox comboBox, final ComboPopup popup) {
+      final JList list = popup.getList();
+      list.setSelectedIndex(0);
+      list.ensureIndexIsVisible(0);
+    }
+  };
+
+  private MacComboBoxAction highlightLastAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(JComboBox comboBox, final ComboPopup popup) {
+      final JList list = popup.getList();
+      int i = list.getModel().getSize();
+      list.setSelectedIndex(i - 1);
+      list.ensureIndexIsVisible(i - 1);
+    }
+  };
+
+  MacComboBoxAction highlightPageUpAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(final JComboBox comboBox, final ComboPopup popup) {
+      final JList list = popup.getList();
+      int i = list.getSelectedIndex();
+      int j = list.getFirstVisibleIndex();
+
+      if (i != j) {
+        list.setSelectedIndex(j);
+        return;
+      }
+
+      int k = list.getVisibleRect().height / list.getCellBounds(0, 0).height;
+      int l = j - k;
+      if (l < 0) l = 0;
+
+      list.ensureIndexIsVisible(l);
+      list.setSelectedIndex(l);
+    }
+  };
+
+  private MacComboBoxAction highlightPageDownAction = new MacComboBoxAction() {
+    @Override
+    protected void performComboBoxAction(JComboBox comboBox, final ComboPopup popup) {
+      final JList list = popup.getList();
+      int i = list.getSelectedIndex();
+      int j = list.getLastVisibleIndex();
+
+      if (i != j) {
+        list.setSelectedIndex(j);
+        return;
+      }
+
+      int k = list.getVisibleRect().height / list.getCellBounds(0, 0).height;
+      int l = list.getModel().getSize() - 1;
+      int i1 = j + k;
+      if (i1 > l) i1 = l;
+
+      list.ensureIndexIsVisible(i1);
+      list.setSelectedIndex(i1);
+    }
+  };
+}
\ No newline at end of file
index 8e79587d20648de3746e8c40b274880069483a58..37251ec23a74ff4d60da899b92bac39d4687d84d 100644 (file)
@@ -41,7 +41,7 @@ public class TreeComboBox extends JComboBox {
     myTreeModel = model;
     setModel(new TreeModelWrapper(myTreeModel));
     setRenderer(new TreeListCellRenderer(this, model));
-    if (SystemInfo.isMac) setMaximumRowCount(25);
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) setMaximumRowCount(25);
   }
 
   public TreeModel getTreeModel() {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/DelegateColorScheme.java
new file mode 100644 (file)
index 0000000..570d1f1
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.openapi.editor.colors.impl;
+
+import com.intellij.openapi.editor.colors.ColorKey;
+import com.intellij.openapi.editor.colors.EditorColorsScheme;
+import com.intellij.openapi.editor.colors.EditorFontType;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.editor.markup.TextAttributes;
+import com.intellij.openapi.util.InvalidDataException;
+import com.intellij.openapi.util.WriteExternalException;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+
+/**
+ * User: spLeaner
+ */
+public abstract class DelegateColorScheme implements EditorColorsScheme {
+
+  private EditorColorsScheme myDelegate;
+
+  public DelegateColorScheme(@NotNull final EditorColorsScheme delegate) {
+    myDelegate = delegate;
+  }
+
+  @Override
+  public void setName(String name) {
+    myDelegate.setName(name);
+  }
+
+  @Override
+  public TextAttributes getAttributes(TextAttributesKey key) {
+    return myDelegate.getAttributes(key);
+  }
+
+  @Override
+  public void setAttributes(TextAttributesKey key, TextAttributes attributes) {
+    myDelegate.setAttributes(key, attributes);
+  }
+
+  @Override
+  public Color getDefaultBackground() {
+    return myDelegate.getDefaultBackground();
+  }
+
+  @Override
+  public Color getDefaultForeground() {
+    return myDelegate.getDefaultForeground();
+  }
+
+  @Override
+  public Color getColor(ColorKey key) {
+    return myDelegate.getColor(key);
+  }
+
+  @Override
+  public void setColor(ColorKey key, Color color) {
+    myDelegate.setColor(key, color);
+  }
+
+  @Override
+  public int getEditorFontSize() {
+    return myDelegate.getEditorFontSize();
+  }
+
+  @Override
+  public void setEditorFontSize(int fontSize) {
+    myDelegate.setEditorFontSize(fontSize);
+  }
+
+  @Override
+  public String getEditorFontName() {
+    return myDelegate.getEditorFontName();
+  }
+
+  @Override
+  public void setEditorFontName(String fontName) {
+    myDelegate.setEditorFontName(fontName);
+  }
+
+  @Override
+  public Font getFont(EditorFontType key) {
+    return myDelegate.getFont(key);
+  }
+
+  @Override
+  public void setFont(EditorFontType key, Font font) {
+    myDelegate.setFont(key, font);
+  }
+
+  @Override
+  public float getLineSpacing() {
+    return myDelegate.getLineSpacing();
+  }
+
+  @Override
+  public void setLineSpacing(float lineSpacing) {
+    myDelegate.setLineSpacing(lineSpacing);
+  }
+
+  @Override
+  public void readExternal(Element element) throws InvalidDataException {
+  }
+
+  @Override
+  public void writeExternal(Element element) throws WriteExternalException {
+  }
+
+  @Override
+  public String getName() {
+    return myDelegate.getName();
+  }
+
+  public Object clone() {
+    return myDelegate.clone();
+  }
+}
index dc70b5ff8bbca1349c3eb1dd809d1f0674ab54f7..a547b6fe5d6ab0a7562917dd540aaff2d4286bed 100644 (file)
@@ -543,6 +543,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
     Disposer.dispose(myCaretModel);
     clearCaretThread();
     //myFoldingModel.dispose(); TODO range marker tree
+
+    myFocusListeners.clear();
   }
 
   private void clearCaretThread() {
index c028263d6159f03bdefbf3fd2f0269c981b7f79b..6ca8e0a53e4a41dcc9c10da5170f754b646db25f 100644 (file)
@@ -969,7 +969,12 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable {
 
   private static class MyEditor implements ComboBoxEditor {
     private KeymapImpl myKeymap = null;
-    private final JTextField myTextField = new JTextField();
+    private final JTextField myTextField = new JTextField() {
+      @Override
+      public void setBounds(int x, int y, int width, int height) {
+        UIUtil.setComboBoxEditorBounds(x, y, width, height, this);
+      }
+    };
 
     public MyEditor() {
       myTextField.getDocument().addDocumentListener(new DocumentAdapter() {
diff --git a/platform/platform-impl/src/com/intellij/ui/ComboboxEditorTextField.java b/platform/platform-impl/src/com/intellij/ui/ComboboxEditorTextField.java
new file mode 100644 (file)
index 0000000..2f19fc5
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.ui;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.editor.ex.FocusChangeListener;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.MacComboBoxEditor;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.util.ui.UIUtil;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import java.awt.*;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+/**
+ * User: spLeaner
+ */
+public class ComboboxEditorTextField extends EditorTextField {
+
+  public static final Border EDITOR_TEXTFIELD_BORDER = new MacComboBoxEditor.MacComboBoxEditorBorder(false) {
+    @Override
+    public Insets getBorderInsets(Component c) {
+      return new Insets(5, 6, 5, 3);
+    }
+  };
+
+  public static final Border EDITOR_TEXTFIELD_DISABLED_BORDER = new MacComboBoxEditor.MacComboBoxEditorBorder(true) {
+    @Override
+    public Insets getBorderInsets(Component c) {
+      return new Insets(5, 6, 5, 3);
+    }
+  };
+
+  public ComboboxEditorTextField(@NotNull String text, Project project, FileType fileType) {
+    super(text, project, fileType);
+  }
+
+  public ComboboxEditorTextField(Document document, Project project, FileType fileType) {
+    this(document, project, fileType, false);
+  }
+
+  public ComboboxEditorTextField(Document document, Project project, FileType fileType, boolean isViewer) {
+    super(document, project, fileType, isViewer);
+  }
+
+  @Override
+  protected boolean shouldHaveBorder() {
+    return UIManager.getBorder("ComboBox.border") == null;
+  }
+
+  @Override
+  public void setBounds(int x, int y, int width, int height) {
+    UIUtil.setComboBoxEditorBounds(x, y, width, height, this);
+  }
+
+  @Override
+  protected EditorEx createEditor() {
+    final EditorEx result = super.createEditor();
+
+    if (UIUtil.isUnderAquaLookAndFeel()) {
+      result.setBorder(isEnabled() ? EDITOR_TEXTFIELD_BORDER : EDITOR_TEXTFIELD_DISABLED_BORDER);
+    }
+
+    result.addFocusListener(new FocusChangeListener() {
+      @Override
+      public void focusGained(Editor editor) {
+        repaintComboBox();
+      }
+
+      @Override
+      public void focusLost(Editor editor) {
+        repaintComboBox();
+      }
+    });
+
+    return result;
+  }
+
+  @Override
+  public Dimension getMinimumSize() {
+    return getPreferredSize();
+  }
+
+  @Override
+  public Dimension getPreferredSize() {
+    final Dimension preferredSize = super.getPreferredSize();
+    return new Dimension(preferredSize.width, (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel() ? 28 : preferredSize.height));
+  }
+
+  @Override
+  public void setEnabled(boolean enabled) {
+    if (UIUtil.isUnderAquaLookAndFeel()) {
+      final Editor editor = getEditor();
+      if (editor != null) {
+        editor.setBorder(enabled ? EDITOR_TEXTFIELD_BORDER : EDITOR_TEXTFIELD_DISABLED_BORDER);
+      }
+    }
+
+    super.setEnabled(enabled);
+  }
+
+  private void repaintComboBox() {
+    // TODO:
+    if (SystemInfo.isMac && UIUtil.isUnderAquaLookAndFeel()) {
+      IdeFocusManager.getInstance(getProject()).doWhenFocusSettlesDown(new Runnable() {
+        @Override
+        public void run() {
+          final Container parent = getParent();
+          if (parent != null) parent.repaint();
+        }
+      });
+    }
+  }
+}
index 5b320c544bede560469da4b5088dc1bdf5d51b98..f6d8469ca684ca7109804e05aba6765eac386a2c 100644 (file)
@@ -20,8 +20,8 @@ import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.CaretModel;
 import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.EditorFactory;
 import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorFactory;
 import com.intellij.openapi.editor.event.DocumentEvent;
 import com.intellij.openapi.editor.event.DocumentListener;
 import com.intellij.openapi.editor.ex.EditorEx;
@@ -30,15 +30,19 @@ import com.intellij.openapi.fileTypes.FileTypes;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.TextComponentAccessor;
 import com.intellij.util.ArrayUtil;
+import com.intellij.util.ui.MacUIUtil;
 
 import javax.swing.*;
 import java.awt.*;
-import java.awt.event.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
 import java.util.ArrayList;
 
 /**
  * @author max
  */
+// TODO[pegov]: should extend ComboBox not JComboBox!
 public class EditorComboBox extends JComboBox implements DocumentListener {
   public static TextComponentAccessor<EditorComboBox> COMPONENT_ACCESSOR = new TextComponentAccessor<EditorComboBox>() {
     public String getText(EditorComboBox component) {
@@ -122,6 +126,13 @@ public class EditorComboBox extends JComboBox implements DocumentListener {
     }
   }
 
+  @Override
+  public void paint(Graphics g) {
+    super.paint(g);
+
+    MacUIUtil.drawComboboxFocusRing(this, g);
+  }
+
   public Project getProject() {
     return myProject;
   }
@@ -244,12 +255,7 @@ public class EditorComboBox extends JComboBox implements DocumentListener {
   }
 
   private void setEditor() {
-    myEditorField = new EditorTextField(myDocument, myProject, myFileType, myIsViewer) {
-      @Override
-      protected boolean shouldHaveBorder() {
-        return UIManager.getBorder("ComboBox.border") == null;
-      }
-    };
+    myEditorField = new ComboboxEditorTextField(myDocument, myProject, myFileType, myIsViewer);
     final ComboBoxEditor editor = new MyEditor();
     setEditor(editor);
     setRenderer(new EditorComboBoxRenderer(editor));
index 346f37eeceb7d00171036d5ea90afbae48c562f2..bbbb0aa57e6fa7fcc9d1d5929b5004b6751cc46b 100644 (file)
@@ -34,13 +34,7 @@ public class EditorComboBoxEditor implements ComboBoxEditor{
   @NonNls protected static final String NAME = "ComboBox.textField";
 
   public EditorComboBoxEditor(Project project, FileType fileType) {
-    myTextField = new EditorTextField((Document)null, project, fileType) {
-      @Override
-      protected boolean shouldHaveBorder() {
-        return UIManager.getBorder("ComboBox.border") == null;
-      }
-    };
-
+    myTextField = new ComboboxEditorTextField((Document)null, project, fileType);
     myTextField.setName(NAME);
   }
 
index 3bc345ff2a37ec9bbfa8a6861fa439a440cfa891..66a42a198eb4ac39ce4c4a4879428a4e1ce95885 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.intellij.ui;
 
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
 import com.intellij.openapi.actionSystem.DataProvider;
 import com.intellij.openapi.actionSystem.PlatformDataKeys;
 import com.intellij.openapi.application.Application;
@@ -23,16 +24,19 @@ import com.intellij.openapi.command.CommandProcessor;
 import com.intellij.openapi.command.UndoConfirmationPolicy;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.*;
-import com.intellij.openapi.editor.colors.EditorColors;
-import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.editor.colors.*;
+import com.intellij.openapi.editor.colors.impl.DelegateColorScheme;
 import com.intellij.openapi.editor.event.DocumentEvent;
 import com.intellij.openapi.editor.event.DocumentListener;
 import com.intellij.openapi.editor.ex.EditorEx;
 import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
+import com.intellij.openapi.editor.markup.TextAttributes;
 import com.intellij.openapi.fileTypes.FileType;
 import com.intellij.openapi.fileTypes.FileTypes;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Key;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
 import com.intellij.util.IJSwingUtilities;
 import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NotNull;
@@ -263,6 +267,13 @@ public class EditorTextField extends JPanel implements DocumentListener, TextCom
   }
 
   void releaseEditor(final Editor editor) {
+    if (myProject != null && myIsViewer) {
+      final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
+      if (psiFile != null) {
+        DaemonCodeAnalyzer.getInstance(myProject).setHighlightingEnabled(psiFile, true);
+      }
+    }
+
     remove(editor.getComponent());
     final Application application = ApplicationManager.getApplication();
     final Runnable runnable = new Runnable() {
@@ -348,14 +359,43 @@ public class EditorTextField extends JPanel implements DocumentListener, TextCom
     settings.setVirtualSpace(false);
     editor.setHorizontalScrollbarVisible(false);
     editor.setVerticalScrollbarVisible(false);
+    editor.setCaretEnabled(!myIsViewer);
     settings.setLineCursorWidth(1);
 
     setupEditorFont(editor);
 
+    if (myProject != null && myIsViewer) {
+      final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(editor.getDocument());
+      if (psiFile != null) {
+        DaemonCodeAnalyzer.getInstance(myProject).setHighlightingEnabled(psiFile, false);
+      }
+    }
+
     if (myProject != null && myFileType != null) {
       editor.setHighlighter(EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, myFileType));
     }
-    editor.getColorsScheme().setColor(EditorColors.CARET_ROW_COLOR, null);
+
+    final EditorColorsScheme colorsScheme = editor.getColorsScheme();
+    colorsScheme.setColor(EditorColors.CARET_ROW_COLOR, null);
+    if (!isEnabled()) {
+      editor.setColorsScheme(new DelegateColorScheme(colorsScheme) {
+        @Override
+        public Color getColor(ColorKey key) {
+          return super.getColor(key);
+        }
+
+        @Override
+        public TextAttributes getAttributes(TextAttributesKey key) {
+          final TextAttributes attributes = super.getAttributes(key);
+          if (!isEnabled()) {
+            return new TextAttributes(UIUtil.getInactiveTextColor(), attributes.getBackgroundColor(), attributes.getEffectColor(), attributes.getEffectType(), attributes.getFontType());
+          }
+
+          return attributes;
+        }
+      });
+    }
+
     editor.setOneLineMode(true);
     editor.getCaretModel().moveToOffset(myDocument.getTextLength());
     if (!shouldHaveBorder()) {
diff --git a/platform/util/src/com/intellij/util/ui/MacUIUtil.java b/platform/util/src/com/intellij/util/ui/MacUIUtil.java
new file mode 100644 (file)
index 0000000..7230abb
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * 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.intellij.util.ui;
+
+import com.intellij.util.graph.Graph;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.GeneralPath;
+
+/**
+ * User: spLeaner
+ */
+public class MacUIUtil {
+
+  private MacUIUtil() {
+  }
+
+  public static Color getFocusRingColor() {
+    final Object o = UIManager.get("Focus.color");
+    if (o instanceof Color) {
+      return (Color) o;
+    }
+
+    return new Color(64, 113, 167);
+  }
+
+  public static void paintComboboxFocusRing(@NotNull final Graphics2D g2d, @NotNull final Rectangle bounds) {
+    final Color color = getFocusRingColor();
+    final Color[] colors = new Color[] {
+      new Color(color.getRed(), color.getGreen(), color.getBlue(), 180),
+      new Color(color.getRed(), color.getGreen(), color.getBlue(), 130),
+      new Color(color.getRed(), color.getGreen(), color.getBlue(), 80),
+      new Color(color.getRed(), color.getGreen(), color.getBlue(), 80)
+    };
+
+    final Object oldAntialiasingValue = g2d.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+    final Object oldStrokeControlValue = g2d.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL);
+
+    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+    final boolean useQuartz = true; // TODO:
+    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, useQuartz ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE);
+
+    //g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+    final GeneralPath path1 = new GeneralPath();
+    path1.moveTo(2, 4);
+    path1.quadTo(2, 2, 4, 2);
+    path1.lineTo(bounds.width - 7, 2);
+    path1.quadTo(bounds.width - 5, 3, bounds.width - 4, 5);
+    path1.lineTo(bounds.width - 4, bounds.height - 7);
+    path1.quadTo(bounds.width - 5, bounds.height - 5, bounds.width - 7, bounds.height - 4);
+    path1.lineTo(4, bounds.height - 4);
+    path1.quadTo(2, bounds.height - 4, 2, bounds.height - 6);
+    path1.closePath();
+
+    g2d.setColor(colors[0]);
+    g2d.draw(path1);
+
+    final GeneralPath path2 = new GeneralPath();
+    path2.moveTo(1, 5);
+    path2.quadTo(1, 1, 5, 1);
+    path2.lineTo(bounds.width - 8, 1);
+    path2.quadTo(bounds.width - 4, 2, bounds.width - 3, 6);
+    path2.lineTo(bounds.width - 3, bounds.height - 7);
+    path2.quadTo(bounds.width - 4, bounds.height - 4, bounds.width - 8, bounds.height - 3);
+    path2.lineTo(4, bounds.height - 3);
+    path2.quadTo(1, bounds.height - 3, 1, bounds.height - 6);
+    path2.closePath();
+
+    g2d.setColor(colors[1]);
+    g2d.draw(path2);
+
+    final GeneralPath path3 = new GeneralPath();
+    path3.moveTo(0, 4);
+    path3.quadTo(0, 0, 7, 0);
+    path3.lineTo(bounds.width - 9, 0);
+    path3.quadTo(bounds.width - 2, 1, bounds.width - 2, 7);
+    path3.lineTo(bounds.width - 2, bounds.height - 8);
+    path3.quadTo(bounds.width - 3, bounds.height - 1, bounds.width - 12, bounds.height - 2);
+    path3.lineTo(7, bounds.height - 2);
+    path3.quadTo(0, bounds.height - 1, 0, bounds.height - 7);
+    path3.closePath();
+
+    g2d.setColor(colors[2]);
+    g2d.draw(path3);
+
+    // restore rendering hints
+    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAntialiasingValue);
+    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, oldStrokeControlValue);
+  }
+
+  public static void drawComboboxFocusRing(@NotNull final JComboBox combobox, @NotNull final Graphics g) {
+    if (combobox.isEnabled() && combobox.isEditable() && UIUtil.isUnderAquaLookAndFeel()) {
+      final Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
+      if (focusOwner != null) {
+        final Container ancestor = SwingUtilities.getAncestorOfClass(JComboBox.class, focusOwner);
+        if (ancestor == combobox) {
+          MacUIUtil.paintComboboxFocusRing((Graphics2D) g, combobox.getBounds());
+        }
+      }
+    }
+  }
+}
index 4cce773ac07ca1dd54a78d980120305988b921df..668193bac0ded8fd0824a204b225663558bce2b0 100644 (file)
@@ -1583,5 +1583,15 @@ public class UIUtil {
     }
   }
 
+  public static void setComboBoxEditorBounds(int x, int y, int width, int height, JComponent editor) {
+    if(SystemInfo.isMac && isUnderAquaLookAndFeel()) {
+      // fix for too wide combobox editor, see AquaComboBoxUI.layoutContainer:
+      // it adds +4 pixels to editor width. WTF?!
+      editor.reshape(x, y, width - 4, height - 1);
+    } else {
+      editor.reshape(x, y, width, height);
+    }
+  }
+
 }