/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation (CCombo)
* compeople AG - adjustments for autocompletion
*******************************************************************************/
package org.eclipse.riena.ui.swt.facades.internal;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleControlAdapter;
import org.eclipse.swt.accessibility.AccessibleControlEvent;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.accessibility.AccessibleTextAdapter;
import org.eclipse.swt.accessibility.AccessibleTextEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.riena.ui.swt.CompletionCombo;
/**
* RCP specific implementation of a {@link CompletionCombo}. There are two
* concrete subclasses:
* <ul>
* <li>CompletionComboRCP – a CompletionCombo with a text field and a list
* widget</li>
* <li>CompletionComboWithImageRCP – a CompletionCombo with a text field,
* with an optional image on the left, and a table widget (which can show an
* image next to each item)</li>
* </ul>
*/
abstract class AbstractCompletionComboRCP extends CompletionCombo {
protected AbstractCompletionComboRCP(final Composite parent, final int style) {
super(parent, style);
initAccessible();
}
@Override
public boolean traverse(final int event) {
/*
* When the traverse event is sent to the CCombo, it will create a list
* of controls to tab to next. Since the CCombo is a composite, the next
* control is the Text field which is a child of the CCombo. It will set
* focus to the text field which really is itself. So, call the traverse
* next events directly on the text.
*/
if (event == SWT.TRAVERSE_ARROW_NEXT || event == SWT.TRAVERSE_TAB_NEXT) {
return getTextControl().traverse(event);
}
return super.traverse(event);
}
void initAccessible() {
final Text text = getTextControl();
final Button arrow = getButtonControl();
final Control list = getListControl();
final AccessibleAdapter accessibleAdapter = new AccessibleAdapter() {
@Override
public void getName(final AccessibleEvent e) {
String name = null;
final Label label = getAssociatedLabel();
if (label != null) {
name = stripMnemonic(label.getText());
}
e.result = name;
}
@Override
public void getKeyboardShortcut(final AccessibleEvent e) {
String shortcut = null;
final Label label = getAssociatedLabel();
if (label != null) {
final String labelText = label.getText();
if (labelText != null) {
final char mnemonic = _findMnemonic(labelText);
if (mnemonic != '\0') {
shortcut = "Alt+" + mnemonic; //$NON-NLS-1$
}
}
}
e.result = shortcut;
}
@Override
public void getHelp(final AccessibleEvent e) {
e.result = getToolTipText();
}
};
getAccessible().addAccessibleListener(accessibleAdapter);
text.getAccessible().addAccessibleListener(accessibleAdapter);
list.getAccessible().addAccessibleListener(accessibleAdapter);
arrow.getAccessible().addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(final AccessibleEvent e) {
e.result = isDropped() ? SWT.getMessage("SWT_Close") : SWT.getMessage("SWT_Open"); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
public void getKeyboardShortcut(final AccessibleEvent e) {
e.result = "Alt+Down Arrow"; //$NON-NLS-1$
}
@Override
public void getHelp(final AccessibleEvent e) {
e.result = getToolTipText();
}
});
getAccessible().addAccessibleTextListener(new AccessibleTextAdapter() {
@Override
public void getCaretOffset(final AccessibleTextEvent e) {
e.offset = text.getCaretPosition();
}
@Override
public void getSelectionRange(final AccessibleTextEvent e) {
final Point sel = text.getSelection();
e.offset = sel.x;
e.length = sel.y - sel.x;
}
});
getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
@Override
public void getChildAtPoint(final AccessibleControlEvent e) {
final Point testPoint = toControl(e.x, e.y);
if (getBounds().contains(testPoint)) {
e.childID = ACC.CHILDID_SELF;
}
}
@Override
public void getLocation(final AccessibleControlEvent e) {
final Rectangle location = getBounds();
final Point pt = getParent().toDisplay(location.x, location.y);
e.x = pt.x;
e.y = pt.y;
e.width = location.width;
e.height = location.height;
}
@Override
public void getChildCount(final AccessibleControlEvent e) {
e.detail = 0;
}
@Override
public void getRole(final AccessibleControlEvent e) {
e.detail = ACC.ROLE_COMBOBOX;
}
@Override
public void getState(final AccessibleControlEvent e) {
e.detail = ACC.STATE_NORMAL;
}
@Override
public void getValue(final AccessibleControlEvent e) {
e.result = getText();
}
});
text.getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
@Override
public void getRole(final AccessibleControlEvent e) {
e.detail = text.getEditable() ? ACC.ROLE_TEXT : ACC.ROLE_LABEL;
}
});
arrow.getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
@Override
public void getDefaultAction(final AccessibleControlEvent e) {
e.result = isDropped() ? SWT.getMessage("SWT_Close") : SWT.getMessage("SWT_Open"); //$NON-NLS-1$ //$NON-NLS-2$
}
});
}
/*
* Return the lowercase of the first non-'&' character following an '&'
* character in the given string. If there are no '&' characters in the
* given string, return '\0'.
*/
char _findMnemonic(final String string) {
if (string == null) {
return '\0';
}
int index = 0;
final int length = string.length();
do {
while (index < length && string.charAt(index) != '&') {
index++;
}
if (++index >= length) {
return '\0';
}
if (string.charAt(index) != '&') {
return Character.toLowerCase(string.charAt(index));
}
index++;
} while (index < length);
return '\0';
}
/*
* Return the Label immediately preceding the receiver in the z-order, or
* null if none.
*/
Label getAssociatedLabel() {
final Control[] siblings = getParent().getChildren();
for (int i = 0; i < siblings.length; i++) {
if (siblings[i] == this) {
if (i > 0 && siblings[i - 1] instanceof Label) {
return (Label) siblings[i - 1];
}
}
}
return null;
}
String stripMnemonic(final String string) {
int index = 0;
final int length = string.length();
do {
while ((index < length) && (string.charAt(index) != '&')) {
index++;
}
if (++index >= length) {
return string;
}
if (string.charAt(index) != '&') {
return string.substring(0, index - 1) + string.substring(index, length);
}
index++;
} while (index < length);
return string;
}
}