All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.autocomplete.ParameterizedCompletionDescriptionToolTip Maven / Gradle / Ivy

/*
 * 12/21/2008
 *
 * AutoCompleteDescWindow.java - A window containing a description of the
 * currently selected completion.
 * Copyright (C) 2008 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 */
package org.fife.ui.autocomplete;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.InputMap;
import javax.swing.JLabel;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.Highlighter.Highlight;


/**
 * A "tooltip" that displays information on the function or method currently
 * being entered.
 *
 * @author Robert Futrell
 * @version 1.0
 */
class ParameterizedCompletionDescriptionToolTip {

	/**
	 * The actual tooltip.
	 */
	private JWindow tooltip;

	/**
	 * The painter to paint borders around the variables.
	 */
	Highlighter.HighlightPainter p;

	/**
	 * The tags for the highlights around parameters.
	 */
	List tags;

	/**
	 * The parent AutoCompletion instance.
	 */
	private AutoCompletion ac;

	/**
	 * The label that holds the description.
	 */
	private JLabel descLabel;

	/**
	 * The completion being described.
	 */
	private ParameterizedCompletion pc;

	/**
	 * Listens for events in the text component while this window is vislble.
	 */
	private Listener listener;

	/**
	 * The minimum offset into the document that the caret can move to
	 * before this tooltip disappears.
	 */
	private int minPos;

	/**
	 * The maximum offset into the document that the caret can move to
	 * before this tooltip disappears.
	 */
	private Position maxPos; // Moves with text inserted.

	/**
	 * The currently "selected" parameter in the displayed text.
	 */
	private int lastSelectedParam;

	private Object oldTabKey;
	private Action oldTabAction;
	private Object oldShiftTabKey;
	private Action oldShiftTabAction;
	private Object oldEnterKey;
	private Action oldEnterAction;
	private Object oldEscapeKey;
	private Action oldEscapeAction;
	private Object oldClosingKey;
	private Action oldClosingAction;

	private static final String IM_KEY_TAB = "ParamCompDescTip.Tab";
	private static final String IM_KEY_SHIFT_TAB = "ParamCompDescTip.ShiftTab";
	private static final String IM_KEY_ESCAPE = "ParamCompDescTip.Escape";
	private static final String IM_KEY_ENTER = "ParamCompDescTip.Enter";
	private static final String IM_KEY_CLOSING = "ParamCompDescTip.Closing";


	/**
	 * Constructor.
	 *
	 * @param owner The parent window.
	 * @param ac The parent autocompletion.
	 * @param pc The completion being described.
	 */
	public ParameterizedCompletionDescriptionToolTip(Window owner,
						AutoCompletion ac, ParameterizedCompletion pc) {

		tooltip = new JWindow(owner);
		this.ac = ac;
		this.pc = pc;

		descLabel = new JLabel();
		descLabel.setBorder(BorderFactory.createCompoundBorder(
					BorderFactory.createLineBorder(Color.BLACK),
					BorderFactory.createEmptyBorder(2, 5, 2, 5)));
		descLabel.setOpaque(true);
		descLabel.setBackground(TipUtil.getToolTipBackground());
		tooltip.setContentPane(descLabel);

		lastSelectedParam = -1;
		updateText(0);

		tooltip.setFocusableWindowState(false);
		listener = new Listener();

		p = new OutlineHighlightPainter(Color.GRAY);
		tags = new ArrayList(1); // Usually small

	}


	private List getParameterHighlights() {
		List paramHighlights = new ArrayList(1);
		JTextComponent tc = ac.getTextComponent();
		Highlight[] highlights = tc.getHighlighter().getHighlights();
		for (int i=0; idot &&
					hl.getStartOffset()<=currentNext.getStartOffset())) {
				currentNext = hl;
				pos = i;
			}
		}

		if (currentNext!=null && dot=dot ||
						(hl.getStartOffset()currentPrev.getStartOffset())) {
					currentPrev = hl;
					pos = i;
				}
			}
		}

		if (currentPrev!=null && dot>currentPrev.getStartOffset()) {
			 // "+1" is a workaround for Java Highlight issues.
			tc.setSelectionStart(currentPrev.getStartOffset()+1);
			tc.setSelectionEnd(currentPrev.getEndOffset());
			updateText(pos);
		}
		else {
			tc.setCaretPosition(maxPos.getOffset());
			setVisible(false, false);
		}

	}


	/**
	 * Removes the bounding boxes around parameters.
	 */
	private void removeParameterHighlights() {
		JTextComponent tc = ac.getTextComponent();
		Highlighter h = tc.getHighlighter();
		for (int i=0; iscreenSize.width) { // completions don't fit
			x = screenSize.width - tooltip.getWidth();
		}

		tooltip.setLocation(x, y);

	}


	/**
	 * Toggles the visibility of this tooltip.
	 *
	 * @param visible Whether the tooltip should be visible.
	 * @param addParamListStart Whether or not
	 *        {@link CompletionProvider#getParameterListStart()} should be
	 *        added to the text component.  If visible is
	 *        false, this parameter is ignored.
	 */
	public void setVisible(boolean visible, boolean addParamListStart) {
		if (visible!=tooltip.isVisible()) {
			JTextComponent tc = ac.getTextComponent();
			if (visible) {
				listener.install(tc, addParamListStart);
			}
			else {
				listener.uninstall();
			}
			tooltip.setVisible(visible);
		}
	}


	/**
	 * Removes the key bindings we installed.
	 *
	 * @see #installKeyBindings()
	 */
	private void uninstallKeyBindings() {

		if (AutoCompletion.getDebug()) {
			System.out.println("ToolTip: Uninstalling keybindings");
		}


		JTextComponent tc = ac.getTextComponent();
		InputMap im = tc.getInputMap();
		ActionMap am = tc.getActionMap();

		KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
		im.put(ks, oldTabKey);
		am.put(IM_KEY_TAB, oldTabAction);

		ks = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK);
		im.put(ks, oldShiftTabKey);
		am.put(IM_KEY_SHIFT_TAB, oldShiftTabAction);

		ks = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
		im.put(ks, oldEnterKey);
		am.put(IM_KEY_ENTER, oldEnterAction);

		ks = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
		im.put(ks, oldEscapeKey);
		am.put(IM_KEY_ESCAPE, oldEscapeAction);

		char end = pc.getProvider().getParameterListEnd();
		ks = KeyStroke.getKeyStroke(end);
		im.put(ks, oldClosingKey);
		am.put(IM_KEY_CLOSING, oldClosingAction);

	}


	/**
	 * Updates the text in the tooltip to have the current parameter
	 * disiplayed in bold.  The "current parameter" is determined from the
	 * current caret position.
	 */
	private void updateText() {

		JTextComponent tc = ac.getTextComponent();
		int dot = tc.getCaretPosition();
		if (dot>0) {
			dot--; // Workaround for Java Highlight issues
		}
		int index = -1;

		List paramHighlights = getParameterHighlights();
		for (int i=0; i=h.getStartOffset() && dot");
		int paramCount = pc.getParamCount();
		for (int i=0; i");
			}
			sb.append(pc.getParam(i).toString());
			if (i==selectedParam) {
				sb.append("");
			}
			if (i=0 && selectedParam");
				sb.append(desc);
			}
		}

		descLabel.setText(sb.toString());
		tooltip.pack();

	}


	/**
	 * Updates the LookAndFeel of this window and the description
	 * window.
	 */
	public void updateUI() {
		SwingUtilities.updateComponentTreeUI(tooltip);
	}


	/**
	 * Called when the user presses Enter while entering parameters.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class GotoEndAction extends AbstractAction {

		public void actionPerformed(ActionEvent e) {
			JTextComponent tc = ac.getTextComponent();
			tc.setCaretPosition(maxPos.getOffset());
			setVisible(false, false);
		}

	}


	/**
	 * Called when the user types the character marking the closing of the
	 * parameter list, such as ')'.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class ClosingAction extends AbstractAction {

		public void actionPerformed(ActionEvent e) {

			JTextComponent tc = ac.getTextComponent();
			int dot = tc.getCaretPosition();
			char end = pc.getProvider().getParameterListEnd();
		
			// Are they at or past the end of the parameters?
			if (dot>=maxPos.getOffset()-1) { // ">=" for overwrite mode

				if (dot==maxPos.getOffset()) { // Happens in overwrite mode
					tc.replaceSelection(Character.toString(end));
				}

				else { // Typical case.
					// Try to decide if we're closing a paren that is a part
					// of the (last) arg being typed.
					String text = getArgumentText(dot);
					if (text!=null) {
						char start = pc.getProvider().getParameterListStart();
						int startCount = getCount(text, start);
						int endCount = getCount(text, end);
						if (startCount>endCount) { // Just closing a paren
							tc.replaceSelection(Character.toString(end));
							return;
						}
					}
					tc.setCaretPosition(maxPos.getOffset());
				}

				setVisible(false, false);

			}

			// If not (in the middle of parameters), just insert the paren.
			else {
				tc.replaceSelection(Character.toString(end));
			}

		}

		public String getArgumentText(int offs) {
			List paramHighlights = getParameterHighlights();
			if (paramHighlights==null || paramHighlights.size()==0) {
				return null;
			}
			for (int i=0; i=h.getStartOffset() && offs<=h.getEndOffset()) {
					int start = h.getStartOffset() + 1;
					int len = h.getEndOffset() - start;
					JTextComponent tc = ac.getTextComponent();
					Document doc = tc.getDocument();
					try {
						return doc.getText(start, len);
					} catch (BadLocationException ble) {
						UIManager.getLookAndFeel().provideErrorFeedback(tc);
						ble.printStackTrace();
						return null;
					}
				}
			}
			return null;
		}

		public int getCount(String text, char ch) {
			int count = 0;
			int old = 0;
			int pos = 0;
			while ((pos=text.indexOf(ch, old))>-1) {
				count++;
				old = pos + 1;
			}
			
			return count;
		}

	}


	/**
	 * Action performed when the user hits the escape key.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class HideAction extends AbstractAction {

		public void actionPerformed(ActionEvent e) {
			setVisible(false, false);
		}

	}


	/**
	 * Listens for various events in the text component while this tooltip
	 * is visible.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class Listener implements FocusListener, CaretListener {

		/**
		 * Called when the text component's caret moves.
		 *
		 * @param e The event.
		 */
		public void caretUpdate(CaretEvent e) {
			if (maxPos==null) { // Sanity check
				setVisible(false, false);
			}
			int dot = e.getDot();
			if (dotmaxPos.getOffset()) {
				setVisible(false, false);
			}
			updateText();
		}


		/**
		 * Called when the text component gains focus.
		 *
		 * @param e The event.
		 */
		public void focusGained(FocusEvent e) {
			// Do nothing
		}


		/**
		 * Called when the text component loses focus.
		 *
		 * @param e The event.
		 */
		public void focusLost(FocusEvent e) {
			setVisible(false, false);
		}


		/**
		 * Returns the text to insert for a parameter.
		 *
		 * @param param The parameter.
		 * @return The text.
		 */
		private String getParamText(ParameterizedCompletion.Parameter param) {
			String text = param.getName();
			if (text==null) {
				text = param.getType();
				if (text==null) { // Shouldn't ever happen
					text = "arg";
				}
			}
			return text;
		}


		/**
		 * Installs this listener onto a text component.
		 *
		 * @param tc The text component to install onto.
		 * @param addParamListStart Whether or not
		 *        {@link CompletionProvider#getParameterListStart()} should be
		 *        added to the text component.
		 * @see #uninstall()
		 */
		public void install(JTextComponent tc, boolean addParamStartList) {

			// Add listeners to the text component.
			tc.addCaretListener(this);
			tc.addFocusListener(this);
			installKeyBindings();

			StringBuffer sb = new StringBuffer();
			if (addParamStartList) {
				sb.append(pc.getProvider().getParameterListStart());
			}
			int dot = tc.getCaretPosition() + sb.length();
			int paramCount = pc.getParamCount();
			List paramLocs = null;
			if (paramCount>0) {
				paramLocs = new ArrayList(paramCount);
			}
			Highlighter h = tc.getHighlighter();

			try {

				// Get the range in which the caret can move before we hide
				// this tooltip.
				minPos = dot;
				maxPos = tc.getDocument().createPosition(dot-sb.length());
				int firstParamLen = 0;

				// Create the text to insert (keep it one completion for
				// performance and simplicity of undo/redo).
				int start = dot;
				for (int i=0; i0) {
					tc.moveCaretPosition(dot+firstParamLen);
				}

			} catch (BadLocationException ble) {
				ble.printStackTrace(); // Never happens
			}

		}


		/**
		 * Uninstalls this listener from the current text component.
		 * 
		 */
		public void uninstall() {

			JTextComponent tc = ac.getTextComponent();
			tc.removeCaretListener(this);
			tc.removeFocusListener(this);
			uninstallKeyBindings();

			// Remove WeakReferences in javax.swing.text.
			maxPos = null;
			minPos = -1;
			removeParameterHighlights();

		}


	}


	/**
	 * Action performed when the user hits the tab key.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class NextParamAction extends AbstractAction {

		public void actionPerformed(ActionEvent e) {
			moveToNextParam();
		}

	}


	/**
	 * Action performed when the user hits shift+tab.
	 *
	 * @author Robert Futrell
	 * @version 1.0
	 */
	private class PrevParamAction extends AbstractAction {

		public void actionPerformed(ActionEvent e) {
			moveToPreviousParam();
		}

	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy