blob: a546264e590b177e99601eb4b889d78ded2f171b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.ui.swing;
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.PopupMenuListener;
import javax.swing.text.JTextComponent;
import org.eclipse.scout.commons.CollectionUtility;
import org.eclipse.scout.commons.EventListenerList;
import org.eclipse.scout.commons.StringUtility;
import org.eclipse.scout.commons.job.JobEx;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.client.ui.action.ActionUtility;
import org.eclipse.scout.rt.client.ui.action.IAction;
import org.eclipse.scout.rt.client.ui.action.IActionFilter;
import org.eclipse.scout.rt.client.ui.action.IActionVisitor;
import org.eclipse.scout.rt.client.ui.action.menu.IMenu;
import org.eclipse.scout.rt.client.ui.action.menu.root.IContextMenu;
/**
* a swing runnable that can be enqueued into the awt event queue when run it
* creates swing menus out of scout menus and shows a popup menu
*/
public class SwingPopupWorker implements Runnable {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwingPopupWorker.class);
private final ISwingEnvironment m_env;
private final Point m_point;
private IContextMenu m_contextMenu;
private final List<? extends IMenu> m_scoutMenus;
private boolean m_isLightWeightPopup;
private final EventListenerList m_listeners;
private Component m_target;
private final JTextComponent m_systemMenuOwner;
private final IActionFilter m_actionFilter;
public SwingPopupWorker(ISwingEnvironment env, Component target, Point point, IContextMenu contextMenu) {
this(env, target, point, contextMenu.getChildActions());
m_contextMenu = contextMenu;
}
public SwingPopupWorker(ISwingEnvironment env, Component target, Point point, List<IMenu> scoutMenus) {
this(env, target, null, point, scoutMenus, ActionUtility.TRUE_FILTER);
}
public SwingPopupWorker(ISwingEnvironment env, Component target, Point point, IContextMenu contextMenu, IActionFilter actionFilter) {
this(env, target, null, point, contextMenu.getChildActions(), actionFilter);
m_contextMenu = contextMenu;
}
public SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, IContextMenu contextMenu, IActionFilter actionFilter) {
this(env, target, systemMenuOwner, point, contextMenu.getChildActions(), actionFilter);
m_contextMenu = contextMenu;
}
protected SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, List<? extends IMenu> scoutMenus, IActionFilter actionFilter) {
this(env, target, systemMenuOwner, point, scoutMenus, actionFilter, true);
}
public SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, IContextMenu contextMenu, boolean isLightWeightPopup) {
this(env, target, systemMenuOwner, point, contextMenu.getChildActions(), isLightWeightPopup);
m_contextMenu = contextMenu;
}
public SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, List<? extends IMenu> scoutMenus, boolean isLightWeightPopup) {
this(env, target, systemMenuOwner, point, scoutMenus, ActionUtility.TRUE_FILTER, isLightWeightPopup);
}
public SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, IContextMenu contextMenu, IActionFilter actionFilter, boolean isLightWeightPopup) {
this(env, target, systemMenuOwner, point, contextMenu.getChildActions(), actionFilter, isLightWeightPopup);
m_contextMenu = contextMenu;
}
protected SwingPopupWorker(ISwingEnvironment env, Component target, JTextComponent systemMenuOwner, Point point, List<? extends IMenu> scoutMenus, IActionFilter actionFilter, boolean isLightWeightPopup) {
m_env = env;
m_target = target;
m_systemMenuOwner = systemMenuOwner;
m_point = point;
m_scoutMenus = scoutMenus;
m_actionFilter = actionFilter;
m_isLightWeightPopup = isLightWeightPopup;
m_listeners = new EventListenerList();
}
public void addListener(PopupMenuListener l) {
m_listeners.add(PopupMenuListener.class, l);
}
public void removeListener(PopupMenuListener l) {
m_listeners.add(PopupMenuListener.class, l);
}
@Override
public void run() {
// about to show
Runnable t = new Runnable() {
@SuppressWarnings("deprecation")
@Override
public void run() {
if (m_contextMenu != null) {
m_contextMenu.callAboutToShow(m_actionFilter);
}
else {
for (IMenu m : m_scoutMenus) {
m.acceptVisitor(new IActionVisitor() {
@Override
public int visit(IAction action) {
if (action instanceof IMenu) {
((IMenu) action).aboutToShow();
((IMenu) action).prepareAction();
}
return CONTINUE;
}
});
}
}
}
};
JobEx prepareJob = m_env.invokeScoutLater(t, 0);
try {
prepareJob.join(1200);
}
catch (InterruptedException e) {
LOG.error("error during prepare menus.", e);
}
IActionFilter displayFilter = ActionUtility.createCombinedFilter(ActionUtility.createVisibleFilter(), m_actionFilter);
List<? extends IMenu> normalizedMenus = ActionUtility.normalizedActions(m_scoutMenus, displayFilter);
if (!CollectionUtility.hasElements(normalizedMenus) && m_systemMenuOwner == null) {
return;
}
//
JPopupMenu pop = new JPopupMenu();
for (PopupMenuListener l : m_listeners.getListeners(PopupMenuListener.class)) {
pop.addPopupMenuListener(l);
}
pop.setLightWeightPopupEnabled(m_isLightWeightPopup);
for (JMenuItem item : getSystemMenus()) {
pop.add(item);
}
if (pop.getComponentCount() > 0 && CollectionUtility.hasElements(normalizedMenus)) {
pop.addSeparator();
}
// recursively add actions
m_env.appendActions(pop, normalizedMenus, displayFilter);
try {
if (pop.getComponentCount() > 0) {
Point whereOnTarget = m_point;
// adjust, if outside screen
if (!m_target.isVisible()) {
Component visibleAncestor = m_target;
while (visibleAncestor != null && (!visibleAncestor.isVisible())) {
visibleAncestor = visibleAncestor.getParent();
}
whereOnTarget = SwingUtilities.convertPoint(m_target, whereOnTarget, visibleAncestor);
m_target = visibleAncestor;
}
Point compLocationOnScreen = m_target.getLocationOnScreen();
Point p = new Point(whereOnTarget);
p.translate(compLocationOnScreen.x, compLocationOnScreen.y);
Rectangle r = new Rectangle(p, pop.getPreferredSize());
// <bsh 2010-11-22>
// Always make sure, that the menu appears on the same screen than the component it belongs to.
Rectangle ownerBounds = new Rectangle(
m_target.getLocationOnScreen().x,
m_target.getLocationOnScreen().y,
m_target.getWidth(),
m_target.getHeight()
);
if (m_target.getParent() instanceof JViewport && m_target.getParent().getParent() instanceof JScrollPane) {
Container scrollpane = m_target.getParent().getParent();
ownerBounds = new Rectangle(
scrollpane.getLocationOnScreen().x,
scrollpane.getLocationOnScreen().y,
scrollpane.getWidth(),
scrollpane.getHeight()
);
}
Rectangle ownerScreen = SwingUtility.getFullScreenBoundsFor(ownerBounds, true);
r = SwingUtility.validateRectangleOnScreen(r, ownerScreen, true, true);
// Check if the menu hides the component. If so, kindly try to move the menu away.
// Do _not_ do this when the component is larger than the menu (e.g. for context menus).
if (r.y < ownerBounds.y && r.height > ownerBounds.height) {
Rectangle tmp;
Rectangle result;
// Try to move the menu _below_ the component
tmp = new Rectangle(r);
tmp.y = ownerBounds.y + ownerBounds.height + 2;
result = SwingUtility.validateRectangleOnScreen(tmp, ownerScreen, true, true);
if (result.equals(tmp)) {
r = tmp;
}
else {
// Try to move the menu _above_ the component
tmp = new Rectangle(r);
tmp.y = ownerBounds.y - pop.getPreferredSize().height - 2;
result = SwingUtility.validateRectangleOnScreen(tmp, ownerScreen, true, true);
if (result.equals(tmp)) {
r = tmp;
}
else {
// Give up
}
}
}
// </bsh>
p = r.getLocation();
p.translate(-compLocationOnScreen.x, -compLocationOnScreen.y);
pop.show(m_target, p.x, p.y);
}
}
catch (Exception e) {
LOG.error(null, e);
}
}
private List<JMenuItem> getSystemMenus() {
List<JMenuItem> items = new ArrayList<JMenuItem>();
if (m_systemMenuOwner != null) {
if (m_systemMenuOwner.isEditable()) {
JMenuItem cutItem = new JMenuItem(SwingUtility.getNlsText("Cut"));
cutItem.setEnabled(StringUtility.hasText(m_systemMenuOwner.getSelectedText()));
cutItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
m_systemMenuOwner.cut();
}
});
items.add(cutItem);
}
JMenuItem copyItem = new JMenuItem(SwingUtility.getNlsText("Copy"));
if (m_systemMenuOwner.isEnabled() && m_systemMenuOwner.isEditable()) {
copyItem.setEnabled(StringUtility.hasText(m_systemMenuOwner.getSelectedText()));
}
copyItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
if (m_systemMenuOwner.isEnabled() && m_systemMenuOwner.isEditable()) {
m_systemMenuOwner.copy();
}
else {
//Ticket 86'427: Kopieren - Einfügen
boolean hasSelection = StringUtility.hasText(m_systemMenuOwner.getSelectedText());
if (hasSelection) {
m_systemMenuOwner.copy();
}
else {
m_systemMenuOwner.selectAll();
m_systemMenuOwner.copy();
m_systemMenuOwner.select(0, 0);
}
}
}
});
items.add(copyItem);
if (m_systemMenuOwner.isEditable()) {
JMenuItem pasteItem = new JMenuItem(SwingUtility.getNlsText("Paste"));
pasteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
m_systemMenuOwner.paste();
}
});
items.add(pasteItem);
}
}
return items;
}
public void enqueue() {
m_env.invokeSwingLater(this);
}
}