/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.internal.ui.ridgets.swt;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.riena.core.RienaStatus;
import org.eclipse.riena.ui.ridgets.IActionRidget;
import org.eclipse.riena.ui.ridgets.IDefaultActionManager;
import org.eclipse.riena.ui.ridgets.IRidget;
import org.eclipse.riena.ui.ridgets.IWindowRidget;
import org.eclipse.riena.ui.swt.utils.SwtUtilities;
/**
* Manages the default button state for one or more ridgets. See
* {@link IDefaultActionManager} for details.
*
* @see IWindowRidget#addDefaultAction(IRidget, IActionRidget)
* @see IDefaultActionManager
*
* @since 2.0
*/
public final class DefaultActionManager implements IDefaultActionManager, Listener {
private final IWindowRidget windowRidget;
private final Map<IRidget, IActionRidget> ridget2button;
private Map<Control, Button> control2button;
private Shell shell;
private Display display;
/**
* Create a new {@link DefaultActionManager} instance
*
* @param windowRidget
* a {@link IWindowRidget}; never null.
*/
public DefaultActionManager(final IWindowRidget windowRidget) {
Assert.isNotNull(windowRidget);
this.windowRidget = windowRidget;
ridget2button = new HashMap<IRidget, IActionRidget>(1);
}
/**
* Callers can add one or more (focusRidget, actionRidget) pairs.
* <p>
* When the focusRidget's control, or one of the controls therein, obtains
* the focus, the matching actionRidget will be set as the default action.
* <p>
* The matching algorithm works "inside-out", i.e. it will start with the
* innermost widget and work upwards. It stops when the first match is
* found.
*
* @param focusRidget
* an {@link IRidget}; never null
* @param actionRidget
* an {@link IActionRidget}; never null
*/
public void addAction(final IRidget focusRidget, final IActionRidget actionRidget) {
Assert.isNotNull(focusRidget);
Assert.isNotNull(actionRidget);
ridget2button.put(focusRidget, actionRidget);
}
public void activate() {
if (control2button == null && !RienaStatus.isTest()) {
shell = ((Control) windowRidget.getUIControl()).getShell();
display = shell.getDisplay();
control2button = new HashMap<Control, Button>();
final HashSet<IRidget> toRemove = new HashSet<IRidget>();
for (final Entry<IRidget, IActionRidget> entry : ridget2button.entrySet()) {
final IRidget focusRidget = entry.getKey();
final Control control = (Control) focusRidget.getUIControl();
if (null == control) {
toRemove.add(focusRidget);
continue;
}
final Button button = (Button) entry.getValue().getUIControl();
if (null == button) {
toRemove.add(focusRidget);
continue;
}
control2button.put(control, button);
}
cleanRidgetMapping(toRemove);
updateDefaultButton(display.getFocusControl());
display.removeFilter(SWT.FocusIn, this);
display.addFilter(SWT.FocusIn, this);
}
}
/**
* Removes unbound ridgets from ridget mapping
*
* @param toRemove
* set of ridgets to remove
*/
private void cleanRidgetMapping(final HashSet<IRidget> toRemove) {
for (final IRidget remove : toRemove) {
ridget2button.remove(remove);
}
}
public void deactivate() {
if (display != null) {
display.removeFilter(SWT.FocusIn, this);
display = null;
}
if (shell != null) {
clearDefaultButton(shell);
shell = null;
}
control2button = null;
}
public void dispose() {
deactivate();
ridget2button.clear();
}
/**
* @noreference This method is not intended to be referenced by clients.
*/
public void handleEvent(final Event event) {
if (SWT.FocusIn == event.type && event.widget instanceof Control) {
final Control control = (Control) event.widget;
updateDefaultButton(control);
}
}
// helping methods
//////////////////
private void clearDefaultButton(final Shell shell) {
if (!SwtUtilities.isDisposed(shell)) {
// the setDefaultButton(...) API is strange! The first call
// will just reset the saved button to null, the second call will
// make null the default button
shell.setDefaultButton(null);
shell.setDefaultButton(null);
}
}
private Button findDefaultButton(final Control start) {
Button result = null;
Control control = start;
while (result == null && control != null) {
result = control2button.get(control);
control = control.getParent();
}
return result;
}
private void updateDefaultButton(final Control control) {
final Button button = findDefaultButton(control);
if (SwtUtilities.isDisposed(button)) {
clearDefaultButton(shell);
return;
}
if (button != shell.getDefaultButton()) {
// System.out.println("Focus on: " + event.widget + ", " + button);
shell.setDefaultButton(button);
}
}
}