/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.extension.ui.editors; import java.util.List; import org.eclipse.jface.fieldassist.ContentProposal; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.fieldassist.IControlContentAdapter; import org.eclipse.jface.fieldassist.IControlContentAdapter2; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.teiid.core.designer.util.StringConstants; /** * Provides matching CCombo items based on keystrokes for a readonly combo. */ abstract class CComboProposalProvider implements IControlContentAdapter, IControlContentAdapter2, IContentProposalProvider { private static final long DEFAULT_DURATION = 500; private final CCombo combo; private long duration = DEFAULT_DURATION; private String pattern = StringConstants.EMPTY_STRING; private long t1 = System.currentTimeMillis(); private long t2; /** * @param combo the combo whose proposals are being requested (cannot be <code>null</code>) */ protected CComboProposalProvider(final CCombo combo) { this(combo, DEFAULT_DURATION); } /** * @param combo the combo whose proposals are being requested (cannot be <code>null</code>) * @param duration the amount of time to wait before resetting the pattern used to match items */ protected CComboProposalProvider(final CCombo combo, final long duration) { this.duration = duration; this.combo = combo; combo.addKeyListener(new KeyAdapter() { /** * @see org.eclipse.swt.events.KeyAdapter#keyPressed(org.eclipse.swt.events.KeyEvent) */ @Override public void keyPressed(KeyEvent e) { handleKeyPressed(e); } }); } /** * @return the characters that trigger auto-activation of proposals (can be <code>null</code>) */ protected abstract char[] getActivationChars(); /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getControlContents(org.eclipse.swt.widgets.Control) */ @Override public final String getControlContents(final Control control) { return this.combo.getText(); } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getCursorPosition(org.eclipse.swt.widgets.Control) */ @Override public final int getCursorPosition(final Control control) { return this.combo.getSelection().x; } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#getInsertionBounds(org.eclipse.swt.widgets.Control) */ @Override public final Rectangle getInsertionBounds(final Control control) { final int position = this.combo.getSelection().y; final String contents = this.combo.getText(); final GC gc = new GC(this.combo); gc.setFont(this.combo.getFont()); final Point extent = gc.textExtent(contents.substring(0, Math.min(position, contents.length()))); gc.dispose(); return new Rectangle(this.combo.getClientArea().x + extent.x, this.combo.getClientArea().y, 1, this.combo.getClientArea().height); } /** * @see org.eclipse.jface.fieldassist.IContentProposalProvider#getProposals(java.lang.String, int) */ @Override public final IContentProposal[] getProposals(final String contents, final int position) { if (this.combo.getListVisible()) { return new IContentProposal[0]; } final List<String> matches = proposalsFor(this.pattern); if (matches.isEmpty()) { return new IContentProposal[0]; } final IContentProposal[] proposals = new IContentProposal[matches.size()]; int i = 0; for (final String match : matches) { proposals[i++] = new ContentProposal(match); } return proposals; } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#getSelection(org.eclipse.swt.widgets.Control) */ @Override public final Point getSelection(final Control control) { return this.combo.getSelection(); } final void handleKeyPressed(final KeyEvent e) { this.t2 = System.currentTimeMillis(); if ((this.t2 - this.t1) > this.duration) { this.pattern = StringConstants.EMPTY_STRING; } this.t1 = this.t2; if (!Character.isLetter(e.character)) { this.pattern = StringConstants.EMPTY_STRING; } else { this.pattern += e.character; } } /** * Initializes the proposal provider. Must be called after construction. */ final void init() { final ContentProposalAdapter proposalAdapter = new ContentProposalAdapter(this.combo, this, this, null, getActivationChars()); proposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#insertControlContents(org.eclipse.swt.widgets.Control, java.lang.String, int) */ @Override public final void insertControlContents(final Control control, final String text, final int cursorPosition) { final String contents = this.combo.getText(); final Point selection = this.combo.getSelection(); final StringBuilder builder = new StringBuilder(); builder.append(contents.substring(0, selection.x)).append(text); if (selection.y < contents.length()) { builder.append(contents.substring(selection.y, contents.length())); } setText(builder.toString()); // // selection.x = (selection.x + cursorPosition); // selection.y = selection.x; // this.combo.setSelection(selection); } /** * @param pattern the pattern to use (can be <code>null</code> or empty) * @return the proposals that match (never <code>null</code> but can be empty) */ protected abstract List<String> proposalsFor(final String pattern); /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setControlContents(org.eclipse.swt.widgets.Control, java.lang.String, int) */ @Override public final void setControlContents(final Control control, final String contents, final int cursorPosition) { setText(contents); setCursorPosition(control, cursorPosition); } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter#setCursorPosition(org.eclipse.swt.widgets.Control, int) */ @Override public final void setCursorPosition(final Control control, final int index) { this.combo.setSelection(new Point(index, index)); } /** * @see org.eclipse.jface.fieldassist.IControlContentAdapter2#setSelection(org.eclipse.swt.widgets.Control, org.eclipse.swt.graphics.Point) */ @Override public final void setSelection(final Control control, final Point range) { this.combo.setSelection(range); } private void setText(final String newSelection) { final int index = this.combo.indexOf(newSelection); if (index != -1) { this.combo.select(index); } } }