/* * Copyright 2000-2015 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.ide.ui.laf.darcula.ui; import com.intellij.ide.ui.laf.darcula.DarculaUIUtil; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.plaf.basic.BasicTextFieldUI; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import javax.swing.text.Position; import java.awt.*; import java.awt.event.*; /** * @author Konstantin Bulenkov */ public abstract class TextFieldWithPopupHandlerUI extends BasicTextFieldUI implements Condition { protected final JTextField myTextField; private MyMouseMotionAdapter myMyMouseMotionAdapter; private MyMouseAdapter myMouseAdapter; private FocusAdapter myFocusAdapter; public TextFieldWithPopupHandlerUI(JTextField textField) { myTextField = textField; installListeners(); } protected boolean hasText() { JTextComponent component = getComponent(); return (component != null) && !StringUtil.isEmpty(component.getText()); } protected abstract SearchAction getActionUnder(@NotNull Point p); protected abstract void showSearchPopup(); protected void installListeners() { final TextFieldWithPopupHandlerUI ui = this; myFocusAdapter = new MyFocusAdapter(); myTextField.addFocusListener(myFocusAdapter); myMyMouseMotionAdapter = new MyMouseMotionAdapter(ui); myTextField.addMouseMotionListener(myMyMouseMotionAdapter); myMouseAdapter = new MyMouseAdapter(ui); myTextField.addMouseListener(myMouseAdapter); } @Override protected void uninstallListeners() { myTextField.removeFocusListener(myFocusAdapter); myTextField.removeMouseMotionListener(myMyMouseMotionAdapter); myTextField.removeMouseListener(myMouseAdapter); } @Override public int getNextVisualPositionFrom(JTextComponent t, int pos, Position.Bias b, int direction, Position.Bias[] biasRet) throws BadLocationException { int position = DarculaUIUtil.getPatchedNextVisualPositionFrom(t, pos, direction); return position != -1 ? position : super.getNextVisualPositionFrom(t, pos, b, direction, biasRet); } @Override public boolean value(Object o) { if (o instanceof MouseEvent) { MouseEvent me = (MouseEvent)o; if (getActionUnder(me.getPoint()) != null) { if (me.getID() == MouseEvent.MOUSE_CLICKED) { //noinspection SSBasedInspection SwingUtilities.invokeLater(() -> myMouseAdapter.mouseClicked(me)); } return true; } } return false; } public static boolean isSearchField(Component c) { return c instanceof JTextField && "search".equals(((JTextField)c).getClientProperty("JTextField.variant")); } public static boolean isSearchFieldWithHistoryPopup(Component c) { return isSearchField(c) && ((JTextField)c).getClientProperty("JTextField.Search.FindPopup") instanceof JPopupMenu; } @Nullable public static AbstractAction getNewLineAction(Component c) { if ( !isSearchField(c) || !Registry.is("ide.find.show.add.newline.hint")) return null; Object action = ((JTextField)c).getClientProperty("JTextField.Search.NewLineAction"); return action instanceof AbstractAction ? (AbstractAction)action : null; } public enum SearchAction { POPUP, CLEAR, NEWLINE } private class MyMouseMotionAdapter extends MouseMotionAdapter { private final TextFieldWithPopupHandlerUI myUi; public MyMouseMotionAdapter(TextFieldWithPopupHandlerUI ui) {myUi = ui;} @Override public void mouseMoved(MouseEvent e) { if (myUi.getComponent() != null && isSearchField(myTextField)) { SearchAction action = myUi.getActionUnder(e.getPoint()); if (action == SearchAction.POPUP && !isSearchFieldWithHistoryPopup(myTextField)) { action = null; } if (action != null) { myTextField.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { myTextField.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); } } } } private class MyMouseAdapter extends MouseAdapter { private final TextFieldWithPopupHandlerUI myUi; public MyMouseAdapter(TextFieldWithPopupHandlerUI ui) {myUi = ui;} @Override public void mouseClicked(MouseEvent e) { if (isSearchField(myTextField)) { final SearchAction action = myUi.getActionUnder(e.getPoint()); if (action != null) { switch (action) { case POPUP: myUi.showSearchPopup(); break; case CLEAR: Object listener = myTextField.getClientProperty("JTextField.Search.CancelAction"); if (listener instanceof ActionListener) { ((ActionListener)listener).actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "action")); } myTextField.setText(""); break; case NEWLINE: { AbstractAction newLineAction = getNewLineAction(myTextField); if (newLineAction != null) { newLineAction.actionPerformed(new ActionEvent(myTextField, ActionEvent.ACTION_PERFORMED, "action")); } break; } } e.consume(); } } } } private class MyFocusAdapter extends FocusAdapter { @Override public void focusGained(FocusEvent e) { myTextField.repaint(); } @Override public void focusLost(FocusEvent e) { myTextField.repaint(); } } }