/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.client.ui.rcp.widgets; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.bindings.keys.ParseException; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.IContentProposalListener2; import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jubula.client.core.businessprocess.IComponentNameCache; import org.eclipse.jubula.client.ui.constants.Constants; import org.eclipse.jubula.client.ui.constants.IconConstants; import org.eclipse.jubula.client.ui.rcp.Plugin; import org.eclipse.jubula.client.ui.utils.DelayableTimer; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; /** * A SWT.Text field with a pop up list. * @author BREDEX GmbH * @created 15.08.2005 */ @SuppressWarnings("synthetic-access") public final class CompNamePopUpTextField extends CheckedCompNameText { /** * @author BREDEX GmbH * @created Dec 8, 2008 */ private final class IContentProposalListener2Implementation implements IContentProposalListener2 { /** * {@inheritDoc} */ public void proposalPopupClosed(ContentProposalAdapter adapter) { m_popupOpen = false; } /** * {@inheritDoc} */ public void proposalPopupOpened(ContentProposalAdapter adapter) { m_popupOpen = true; } } /** data key to init pop up or not */ public static final String INITPOPUP = "INITPOPUP"; //$NON-NLS-1$ /** the max. lines of the popup */ protected static final int HEIGHT = 8; /** the min. width (pixel) of the popup */ protected static final int WIDTH = 130; /** KeyCode for SPACE */ protected static final char SPACE = ' '; /** intern */ private CompNamesProposalProvider m_contentProposalProvider; /** intern */ private ContentProposalAdapter m_contentProposalAdapter; /** intern */ private ILabelProvider m_labelProvider; /** is the popup with the content proposal open? */ private boolean m_popupOpen = false; /** was the data modified */ private boolean m_modified = false; /** track popup state */ private IContentProposalListener2 m_popupListener; /** * Constructs a text field. When pressing STRG+SPACE a list pops up. * * @param compCache The Component Name cache to use. * @param composite The parent composite. * @param style The style of the text field. */ public CompNamePopUpTextField(IComponentNameCache compCache, Composite composite, int style) { super(composite, style); KeyStroke ks = null; try { ks = KeyStroke.getInstance("Ctrl+Space"); //$NON-NLS-1$ } catch (ParseException e) { // ignore until you want to play with the constant above } m_labelProvider = new LabelProvider() { public Image getImage(Object element) { CompNamesProposal p = (CompNamesProposal)element; char type = p.getLabel().charAt(0); switch (type) { case 'G': // GLOBAL return IconConstants.GLOBAL_NAME_IMAGE; case 'L': // LOCAL return IconConstants.LOCAL_NAME_IMAGE; case 'A': // AUT return IconConstants.AUT_COMP_NAME_IMAGE; default: return null; } } public String getText(Object element) { CompNamesProposal p = (CompNamesProposal)element; return p.getLabel().substring(1); } public boolean isLabelProperty(Object element, String property) { return false; } }; m_popupListener = new IContentProposalListener2Implementation(); m_contentProposalProvider = new CompNamesProposalProvider(compCache); enableContentProposal(m_contentProposalProvider, ks); int delay = Plugin.getDefault().getPreferenceStore().getInt( Constants.MILLIS_TO_OPEN_COMP_NAMES_CONTENT_PROPOSAL); final DelayableTimer contentProposalTimer = new DelayableTimer(delay, new Runnable() { public void run() { Display.getDefault().syncExec(new Runnable() { public void run() { if (!isDisposed() && isFocusControl()) { openContentProposals(); } } }); } }); addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { if (!getText().equals(StringConstants.EMPTY) && triggerCharWasTyped(e)) { contentProposalTimer.schedule(); } } public void keyPressed(KeyEvent e) { contentProposalTimer.cancel(); } }); } /** * Checks if a key event was invoked by a character which is supposed to trigger component name proposals * @param e the key event * @return whether the character is supposed to trigger component name proposals */ protected boolean triggerCharWasTyped(KeyEvent e) { char[] triggerChars = createTriggerChars(); for (char c : triggerChars) { if (e.character == c) { return true; } } return false; } /** * used in properties view */ public void activateSelectionEvent() { addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { m_modified = true; } }); addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent e) { if (!m_popupOpen) { if (m_modified) { fireSelectionEvent(); } } } }); } /** * fire a pseudo event to trigger callers */ private void fireSelectionEvent() { getDisplay().syncExec(new Runnable() { public void run() { if (!CompNamePopUpTextField.this.isDisposed()) { Event modifyEvent = new Event(); modifyEvent.widget = CompNamePopUpTextField.this; modifyEvent.type = SWT.Modify; CompNamePopUpTextField.this.notifyListeners(SWT.Modify, modifyEvent); } } }); } /** * Do the actual registration of the provider * @param contentProposalProvider provider for the content * @param keyStroke activation KeyStroke * start the proposal */ private void enableContentProposal( IContentProposalProvider contentProposalProvider, KeyStroke keyStroke) { m_contentProposalAdapter = new ContentProposalAdapter(this, new TextContentAdapter(), contentProposalProvider, keyStroke, null); m_contentProposalAdapter.setLabelProvider(m_labelProvider); m_contentProposalAdapter .addContentProposalListener(m_popupListener); m_contentProposalAdapter .setProposalAcceptanceStyle( ContentProposalAdapter.PROPOSAL_REPLACE); } /** * @return the characters that should trigger a content proposal */ private char[] createTriggerChars() { char[] trigger = new char[2 * 26 + 10 + 2]; int index = 0; for (char c = 'a'; c <= 'z'; ++c) { trigger[index++] = c; trigger[index++] = Character.toUpperCase(c); } for (int i = 0; i < 10; ++i) { trigger[index++] = Character.forDigit(i, 10); } trigger[index++] = '_'; trigger[index++] = '\b'; return trigger; } /** * {@inheritDoc} */ protected void checkSubclass() { // do nothing } /** * @param filter match against type from comp system */ public void setFilter(String filter) { if (m_contentProposalProvider != null) { m_contentProposalProvider.setTypeFilter(filter); } } /** * @return the popupOpen */ public boolean isPopupOpen() { return m_popupOpen; } /** * * @param compCache The Component Name cache to use. */ public void setComponentNameCache(IComponentNameCache compCache) { if (m_contentProposalProvider != null) { m_contentProposalProvider.setComponentNameCache(compCache); } } /** * Opens the proposals for the component name */ private void openContentProposals() { if (!isDisposed()) { // check if content proposals were already opened if (m_contentProposalAdapter.isProposalPopupOpen()) { return; } // create a fake event that ctrl+space was pressed, to open content proposals Event triggerEvent = new Event(); triggerEvent.character = SWT.SPACE; triggerEvent.doit = true; triggerEvent.keyCode = SWT.SPACE; triggerEvent.stateMask = SWT.CTRL; triggerEvent.type = SWT.KeyDown; triggerEvent.widget = this; notifyListeners(SWT.KeyDown, triggerEvent); } } }