/*******************************************************************************
* Copyright (c) 2013 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.rc.rcp.swt.aut;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
import org.eclipse.jface.preference.IPreferenceNode;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jubula.rc.swt.listener.ComponentHandler;
import org.eclipse.jubula.tools.internal.constants.AutEnvironmentConstants;
import org.eclipse.jubula.tools.internal.constants.SwtToolkitConstants;
import org.eclipse.jubula.tools.internal.utils.EnvironmentUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.CoolBar;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Widget;
/**
* This listener assigns names to components as they become visible. The
* assigned name is determined by using the component and its surroundings.
*
* @author BREDEX GmbH
* @created Oct 19, 2007, 2013
*/
public abstract class RcpSwtComponentNamer implements Listener {
/** ID suffix for tool bars belonging to a part (view/editor) */
private static final String TOOLBAR_ID_SUFFIX = ".toolbar"; //$NON-NLS-1$
/** Key for RCP partId in component data */
private static final String TEST_RCP_DATA_KEY =
SwtToolkitConstants.RCP_NAME;
/** map for naming dialog buttons */
private static Map<Integer, String> componentNAMES =
new HashMap<Integer, String>();
/** is name generation enabled */
private static boolean generateNames = false;
static {
generateNames = Boolean.valueOf(
EnvironmentUtils.getProcessEnvironment().getProperty(
AutEnvironmentConstants.GENERATE_COMPONENT_NAMES))
.booleanValue();
addCompName(IDialogConstants.ABORT_ID, "abort"); //$NON-NLS-1$
addCompName(IDialogConstants.BACK_ID, "back"); //$NON-NLS-1$
addCompName(IDialogConstants.CANCEL_ID, "cancel"); //$NON-NLS-1$
addCompName(IDialogConstants.CLIENT_ID, "client"); //$NON-NLS-1$
addCompName(IDialogConstants.CLOSE_ID, "close"); //$NON-NLS-1$
addCompName(IDialogConstants.DESELECT_ALL_ID, "deselectAll"); //$NON-NLS-1$
addCompName(IDialogConstants.DETAILS_ID, "details"); //$NON-NLS-1$
addCompName(IDialogConstants.FINISH_ID, "finish"); //$NON-NLS-1$
addCompName(IDialogConstants.HELP_ID, "help"); //$NON-NLS-1$
addCompName(IDialogConstants.IGNORE_ID, "ignore"); //$NON-NLS-1$
addCompName(IDialogConstants.INTERNAL_ID, "internal"); //$NON-NLS-1$
addCompName(IDialogConstants.NEXT_ID, "next"); //$NON-NLS-1$
addCompName(IDialogConstants.NO_ID, "no"); //$NON-NLS-1$
addCompName(IDialogConstants.NO_TO_ALL_ID, "noToAll"); //$NON-NLS-1$
addCompName(IDialogConstants.OK_ID, "ok"); //$NON-NLS-1$
addCompName(IDialogConstants.OPEN_ID, "open"); //$NON-NLS-1$
addCompName(IDialogConstants.PROCEED_ID, "proceed"); //$NON-NLS-1$
addCompName(IDialogConstants.RETRY_ID, "retry"); //$NON-NLS-1$
addCompName(IDialogConstants.SELECT_ALL_ID, "selectAll"); //$NON-NLS-1$
addCompName(IDialogConstants.SELECT_TYPES_ID, "selectTypes"); //$NON-NLS-1$
addCompName(IDialogConstants.SKIP_ID, "skip"); //$NON-NLS-1$
addCompName(IDialogConstants.STOP_ID, "stop"); //$NON-NLS-1$
addCompName(IDialogConstants.YES_ID, "yes"); //$NON-NLS-1$
addCompName(IDialogConstants.YES_TO_ALL_ID, "yesToAll"); //$NON-NLS-1$
}
/**
* add component id <-> name mapping
*
* @param compID
* the component identifier
* @param compName
* the component name
*/
private static void addCompName(int compID, String compName) {
String staticNamePreafix = "dialog.button."; //$NON-NLS-1$
componentNAMES.put(new Integer(compID), staticNamePreafix + compName);
}
/**
* {@inheritDoc}
*/
public void handleEvent(Event event) {
addNameData(event.widget);
Item[] items;
if (event.widget instanceof ToolBar) {
items = ((ToolBar) event.widget).getItems();
} else if (event.widget instanceof CoolBar) {
items = ((CoolBar) event.widget).getItems();
} else {
return;
}
for (int i = 0; i < items.length; i++) {
addNameData(items[i]);
}
}
/**
* Adds name information to the given widget, if necessary.
*
* @param widget
* The widget to name.
*/
private void addNameData(Widget widget) {
// Assign name
if (hasWidgetToBeNamed(widget)) {
Object data = getWidgetData(widget);
if (data instanceof IContributionItem) {
// Name buttons and toolitems according to the action that
// they represent, if possible.
String actionId = ((IContributionItem) data).getId();
if (actionId != null && actionId.trim().length() > 0) {
setComponentName(widget, actionId);
ComponentHandler.getAutHierarchy().refreshComponentName(
widget);
}
} else if (data instanceof PreferenceDialog) {
PreferenceDialog prefDialog = (PreferenceDialog) data;
// Add a listener to add name data as pages are
// selected/created.
prefDialog.addPageChangedListener(new IPageChangedListener() {
public void pageChanged(PageChangedEvent event) {
addNameDataToPrefPage(event.getSelectedPage());
}
});
// The listener won't notice the initally selected page,
// so we have to add that name data here.
addNameDataToPrefPage(prefDialog.getSelectedPage());
}
if (generateNames && data instanceof Dialog) {
Dialog dialog = (Dialog) data;
setNameForDialogButtonBarButtons(dialog);
}
}
}
/**
*
* @param widget
* The widget for which to get the data.
* @return the data object corresponding to the given widget.
*/
private static Object getWidgetData(Widget widget) {
Object data = widget.getData();
// Handle the case of CoolBar containing CoolItem containing ToolBar.
// The CoolItem is the widget that represents the toolbar
// contribution, but it (the CoolItem) is not in our AUT
// component hierarchy, due to the fact that the ToolBar's
// getParent() returns the CoolBar rather than the CoolItem.
// To resolve this discrepancy, we use the data from the
// corresponding CoolItem to generate a name for the ToolBar.
if (widget instanceof ToolBar) {
Composite toolbarParent = ((ToolBar) widget).getParent();
if (toolbarParent instanceof CoolBar) {
CoolItem[] coolItems = ((CoolBar) toolbarParent).getItems();
for (int i = 0; i < coolItems.length; i++) {
CoolItem item = coolItems[i];
if (item != null && item.getControl() == widget) {
data = item.getData();
}
}
}
}
return data;
}
/**
* @param dialog
* the dialog
*/
private void setNameForDialogButtonBarButtons(Dialog dialog) {
try {
Method getButtonMethod = Dialog.class.getDeclaredMethod(
"getButton", new Class[] { int.class }); //$NON-NLS-1$
getButtonMethod.setAccessible(true);
Iterator<Integer> components = componentNAMES.keySet().iterator();
while (components.hasNext()) {
Integer componentID = components.next();
invokeNameSetting(dialog, getButtonMethod, componentID,
componentNAMES.get(componentID));
}
} catch (SecurityException e) {
// ignore exceptions
} catch (NoSuchMethodException e) {
// ignore exceptions
}
}
/**
* use this method to set a name on the given object
*
* @param useObject
* the object
* @param methodToInvoke
* the method to invoke
* @param buttonID
* the button id
* @param buttonName
* the button name
*/
private static void invokeNameSetting(
Object useObject, Method methodToInvoke,
Integer buttonID, Object buttonName) {
Object ret = null;
try {
ret = methodToInvoke.invoke(useObject, new Object[] { buttonID });
} catch (IllegalArgumentException e) {
// ignore exceptions
} catch (IllegalAccessException e) {
// ignore exceptions
} catch (InvocationTargetException e) {
// ignore exceptions
}
if (ret instanceof Button) {
Button button = (Button) ret;
if (hasWidgetToBeNamed(button)) {
setComponentName(button, buttonName.toString());
}
}
}
/**
* Attaches name data to the given page appropriate.
*
* @param selectedPage
* The page to which we will try to attach the name data.
*/
private void addNameDataToPrefPage(Object selectedPage) {
if (selectedPage == null) {
return;
}
PreferenceManager prefMan = getPreferenceManager();
if (prefMan == null) {
return;
}
Iterator iter = prefMan.getElements(PreferenceManager.PRE_ORDER)
.iterator();
while (iter.hasNext()) {
IPreferenceNode prefNode = (IPreferenceNode) iter.next();
if (selectedPage.equals(prefNode.getPage())) {
Control pageControl = prefNode.getPage().getControl();
String prefNodeId = prefNode.getId();
// Assign id to page composite only if the composite exists
// and if the id is usable
if (hasWidgetToBeNamed(pageControl)
&& prefNodeId != null
&& prefNodeId.trim().length() > 0) {
setComponentName(pageControl, prefNodeId);
Shell prefShell = pageControl.getDisplay().getActiveShell();
Event activateEvent = new Event();
activateEvent.time = (int) System.currentTimeMillis();
activateEvent.type = SWT.Activate;
activateEvent.widget = prefShell;
prefShell.notifyListeners(SWT.Activate, activateEvent);
}
// We found the page we were looking for, so we can stop
// searching.
break;
}
}
}
/**
* @param widget
* The SWT widget to look at.
* @return True, if the given SWT widget is not null, is not disposed and the component
* name has not been set, otherwise false.
*/
public static boolean hasWidgetToBeNamed(Widget widget) {
boolean hasToBeNamed =
// exists
widget != null
// is not destroyed
&& !widget.isDisposed()
// the test component name has not been set
&& widget.getData(TEST_RCP_DATA_KEY) == null;
return hasToBeNamed;
}
/**
* Sets the given component name id to the given widget.
* @param widget
* The widget setting the id on.
* @param id
* The id to set to the widget.
*/
public static void setComponentName(Widget widget, String id) {
widget.setData(TEST_RCP_DATA_KEY, id);
}
/**
* Set the component name of a tool bar. Calls {@link #setComponentName(Widget, String)}
* with appending the suffix tool bar to the finalPartId.
* @param partToolbar The part of the tool bar.
* @param finalPartId The ID for the part of the tool bar.
*/
public static void setToolbarComponentName(Widget partToolbar,
String finalPartId) {
setComponentName(partToolbar, finalPartId + TOOLBAR_ID_SUFFIX);
}
/**
* @return An instance of the preference manager, or null if not available.
* This method must be implemented depending on Eclipse RCP e3 and e4.
*/
protected abstract PreferenceManager getPreferenceManager();
}