/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.awt.swingpeers; import java.awt.AWTError; import java.awt.AWTEvent; import java.awt.Button; import java.awt.Canvas; import java.awt.Checkbox; import java.awt.CheckboxMenuItem; import java.awt.Choice; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.EventQueue; import java.awt.FileDialog; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.Insets; import java.awt.Label; import java.awt.Menu; import java.awt.MenuBar; import java.awt.MenuItem; import java.awt.Panel; import java.awt.Point; import java.awt.PopupMenu; import java.awt.Rectangle; import java.awt.ScrollPane; import java.awt.Scrollbar; import java.awt.Shape; import java.awt.TextArea; import java.awt.TextField; import java.awt.Toolkit; import java.awt.Window; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.peer.DragSourceContextPeer; import java.awt.event.InvocationEvent; import java.awt.image.BufferedImage; import java.awt.peer.ButtonPeer; import java.awt.peer.CanvasPeer; import java.awt.peer.CheckboxMenuItemPeer; import java.awt.peer.CheckboxPeer; import java.awt.peer.ChoicePeer; import java.awt.peer.ComponentPeer; import java.awt.peer.DialogPeer; import java.awt.peer.FileDialogPeer; import java.awt.peer.FramePeer; import java.awt.peer.LabelPeer; import java.awt.peer.LightweightPeer; import java.awt.peer.ListPeer; import java.awt.peer.MenuBarPeer; import java.awt.peer.MenuItemPeer; import java.awt.peer.MenuPeer; import java.awt.peer.PanelPeer; import java.awt.peer.PopupMenuPeer; import java.awt.peer.ScrollPanePeer; import java.awt.peer.ScrollbarPeer; import java.awt.peer.TextAreaPeer; import java.awt.peer.TextFieldPeer; import java.awt.peer.WindowPeer; import java.beans.PropertyVetoException; import java.util.WeakHashMap; import java.lang.reflect.Method; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import javax.swing.JComponent; import javax.swing.JInternalFrame; import javax.swing.JMenuBar; import javax.swing.RepaintManager; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.plaf.metal.OceanTheme; import org.jnode.awt.JNodeAwtContext; import org.jnode.awt.JNodeToolkit; import org.jnode.annotation.SharedStatics; import sun.awt.AWTAutoShutdown; import sun.awt.AppContext; import sun.awt.SunToolkit; /** * AWT toolkit implemented entirely with JFC peers, thus allowing a lightweight * simulation of the operating system desktop. * * @author Levente S\u00e1ntha * @author Ewout Prangsma (epr@users.sourceforge.net) */ @SharedStatics public final class SwingToolkit extends JNodeToolkit { /** * An empty border */ static final Border EMPTY_BORDER = new EmptyBorder(0, 0, 0, 0); /** * My repaint manager. Only valid between onInitialize and onClose */ private RepaintManager repaintManager; public static void add(Component component, JComponent peer) { final ISwingContainerPeer containerPeer = getContainerPeer(component); if (containerPeer != null) { containerPeer.addAWTComponent(component, peer); } } /** * Copies the generic component properties from the AWT component into the * peer. * * @param awtComponent * @param peer */ static void copyAwtProperties(Component awtComponent, Component peer) { Color c; Font f; if ((c = awtComponent.getForeground()) != null) { peer.setForeground(c); } if ((c = awtComponent.getBackground()) != null) { peer.setBackground(c); } if ((f = awtComponent.getFont()) != null) { peer.setFont(f); } } @SuppressWarnings("deprecation") private static ISwingContainerPeer getContainerPeer(Component component) { final Component parent = component.getParent(); if (parent == null) { return null; } else { final ComponentPeer parentPeer = parent.getPeer(); if (parentPeer instanceof ISwingContainerPeer) { return (ISwingContainerPeer) parentPeer; } else { return getContainerPeer(parent); } } } /** * Paint all the lightweight children of the given container. * * @param awtContainer * @param g */ public static void paintLightWeightChildren(Container awtContainer, Graphics g, int dx, int dy) { final Shape oldClip = g.getClip(); try { final Component[] comps = awtContainer.getComponents(); final int cnt = comps.length; for (int i = 0; i < cnt; i++) { final Component child = comps[i]; if (child.isVisible() && child.isLightweight()) { final int x = child.getX() - dx; final int y = child.getY() - dy; final int width = child.getWidth(); final int height = child.getHeight(); g.setClip(x, y, width, height); g.translate(x, y); try { child.paint(g); } finally { g.translate(-x, -y); } } } } finally { g.setClip(oldClip); } } private DesktopFrame desktopFrame; /** * Initialize this instance. */ public SwingToolkit() { } // Peers protected ButtonPeer createButton(Button target) { return new SwingButtonPeer(this, target); } protected CanvasPeer createCanvas(Canvas target) { // return super.createCanvas( target ); return new SwingCanvasPeer(this, target); } protected CheckboxPeer createCheckbox(Checkbox target) { return new SwingCheckboxPeer(this, target); } protected CheckboxMenuItemPeer createCheckboxMenuItem( CheckboxMenuItem target) { return new SwingCheckboxMenuItemPeer(this, target); } protected ChoicePeer createChoice(Choice target) { return new SwingChoicePeer(this, target); } protected LightweightPeer createComponent(Component target) { //todo investigate the idea used in super for applying here if (target instanceof Container) return new SwingLightweightContainerPeer(this, (Container) target); else return new SwingLightweightPeer(this, target); } protected DialogPeer createDialog(Dialog target) { return new SwingDialogPeer(this, target); } public DragSourceContextPeer createDragSourceContextPeer( DragGestureEvent dge) { return null; } protected FileDialogPeer createFileDialog(FileDialog target) { return null; } protected FramePeer createFrame(final Frame target) { final FramePeer[] ret = new FramePeer[1]; final Runnable run = new Runnable() { public void run() { if (!isGuiActive()) { //throw new AWTError("AWT is currently not available"); initGui(); } if (target instanceof DesktopFrame) { setTop(target); log.debug("createFrame:desktopFramePeer(" + target + ")"); // Only desktop is real frame //return new DesktopFramePeer(SwingToolkit.this, (DesktopFrame) target); synchronized (ret) { ret[0] = new DesktopFramePeer(SwingToolkit.this, (DesktopFrame) target); try { Method method = AWTAutoShutdown.class.getDeclaredMethod("registerPeer", Object.class, Object.class); method.setAccessible(true); method.invoke(AWTAutoShutdown.getInstance(), target, ret[0]); } catch (Exception x) { x.printStackTrace(); } } } else /*if (target instanceof JFrame) */ { if (!isGuiActive()) { throw new AWTError("Gui is not active"); } log.debug("createFrame:normal(" + target + ")"); // Other frames are emulated //return new SwingFramePeer(SwingToolkit.this, target); synchronized (ret) { ret[0] = new SwingFramePeer(SwingToolkit.this, target); } } /*else { if (!isGuiActive()) { throw new AWTError("Gui is not active"); } log.debug("createFrame:normal(" + target + ")"); // Other frames are emulated return new SwingJFramePeer(this, target); } */ } }; AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { //peer frames should be created in the same app context where the desktop is //todo refactor this into a generic inter-appcontext invoke and wait AppContext ac = SunToolkit.targetToAppContext(target); if (ac != null) { EventQueue eq = (EventQueue) ac.get(sun.awt.AppContext.EVENT_QUEUE_KEY); if (eq != null) { try { Method met = EventQueue.class.getDeclaredMethod("initDispatchThread"); met.setAccessible(true); met.invoke(eq); } catch (Exception x) { x.printStackTrace(); } } } // invoke and wait -- EventQueue eq = getMainEventQueue(); if (eq == getSystemEventQueueImpl()) { run.run(); synchronized (ret) { return ret[0]; } } try { Field field = EventQueue.class.getField("dispatchThread"); field.setAccessible(true); Thread edt = (Thread) field.get(eq); if (Thread.currentThread() == edt) { run.run(); synchronized (ret) { return ret[0]; } } } catch (Exception x) { throw new RuntimeException(x); } class AWTInvocationLock { } Object lock = new AWTInvocationLock(); InvocationEvent event = new InvocationEvent(Toolkit.getDefaultToolkit(), run, lock, true); try { synchronized (lock) { eq.postEvent(event); lock.wait(); } } catch (Exception x) { throw new RuntimeException(x); } Throwable eventThrowable = event.getThrowable(); if (eventThrowable != null) { throw new RuntimeException(eventThrowable); } // --invoke and wait return null; } }); synchronized (ret) { return ret[0]; } } protected LabelPeer createLabel(Label target) { return new SwingLabelPeer(this, target); } protected ListPeer createList(java.awt.List target) { return new SwingListPeer(this, target); } protected MenuPeer createMenu(Menu target) { return new SwingMenuPeer(this, target); } protected MenuBarPeer createMenuBar(MenuBar target) { return new SwingMenuBarPeer(this, target); } protected MenuItemPeer createMenuItem(MenuItem target) { return new SwingMenuItemPeer(this, target); } protected PanelPeer createPanel(Panel target) { return new SwingPanelPeer(this, target); } protected PopupMenuPeer createPopupMenu(PopupMenu target) { return new SwingPopupMenuPeer(this, target); } protected ScrollbarPeer createScrollbar(Scrollbar target) { return new SwingScrollbarPeer(this, target); } protected ScrollPanePeer createScrollPane(ScrollPane target) { return new SwingScrollPanePeer(this, target); } protected TextAreaPeer createTextArea(TextArea target) { return new SwingTextAreaPeer(this, target); } protected TextFieldPeer createTextField(TextField target) { return new SwingTextFieldPeer(this, target); } protected WindowPeer createWindow(Window target) { return new SwingWindowPeer(this, target); } public JNodeAwtContext getAwtContext() { return desktopFrame; } /** * @see org.jnode.awt.JNodeToolkit#refresh() */ protected void refresh() { log.info("Refresh"); final JNodeAwtContext ctx = getAwtContext(); if (ctx == null) { log.info("Refresh: no AWT context"); return; } final Container root = ctx.getAwtRoot(); if (root != null) { root.repaint(); } else { log.info("Refresh: no AWT root"); } } public Component getTopComponentAt(int x, int y) { if (desktopFrame == null) { //no AWT yet, drop the event return null; } Component comp = desktopFrame.getComponentAt(x, y); if (comp instanceof SwingBaseWindow) { SwingBaseWindow<?, ?> base = (SwingBaseWindow<?, ?>) comp; if (base.isShowing()) { Window w = base.getAWTComponent(); if (w instanceof Frame) { MenuBar mb = ((Frame) w).getMenuBar(); if (mb != null) { JMenuBar jmb = ((SwingMenuBarPeer) mb.getPeer()).jComponent; Point p = new Point(x, y); SwingUtilities.convertPointFromScreen(p, jmb); comp = SwingUtilities.getDeepestComponentAt(jmb, p.x, p.y); if (comp != null && (comp != jmb || jmb.contains(p.x, p.y))) { return comp; } } } Point p = new Point(x, y); SwingUtilities.convertPointFromScreen(p, w); comp = SwingUtilities.getDeepestComponentAt(w, p.x, p.y); if (comp == w) { p = new Point(x, y); SwingUtilities.convertPointFromScreen(p, base); comp = SwingUtilities.getDeepestComponentAt(base, p.x, p.y); } } } else { comp = super.getTopComponentAt(x, y); SwingBaseWindow<?, ?> window = (SwingBaseWindow<?, ?>) SwingUtilities. getAncestorOfClass(SwingBaseWindow.class, comp); if (window != null) { Rectangle r = window.getBounds(); Insets ins = window.getSwingPeer().getInsets(); r.x = r.x + ins.left; r.y = r.y + ins.top; r.width = r.width - ins.left - ins.right; r.height = r.height - ins.top - ins.bottom; if (r.contains(x, y)) { Component c = window.getAWTComponent().findComponentAt(x - r.x + ins.left, y - r.y + ins.top); if (c != null) { comp = c; } } } } return comp; } public void activateWindow(Component comp) { if (comp == null) return; Window w = SwingUtilities.getWindowAncestor(comp); if (w == null) return; WindowPeer p = (WindowPeer) w.getPeer(); if (p instanceof SwingBaseWindowPeer) { JInternalFrame f = (JInternalFrame) ((SwingBaseWindowPeer<?, ?>) p).peerComponent; if (f.isShowing() && !f.isSelected()) { try { f.setSelected(true); } catch (PropertyVetoException pve) { //ignore } } } } /** * @see org.jnode.awt.JNodeToolkit#onClose() */ protected void onClose() { log.debug("onClose"); // Stop the repaint manager if (repaintManager != null) { //repaintManager.shutdown(); repaintManager = null; } // sometime when the start of wat has failed, desktopFrame can be null // so, we must check it is not null if (desktopFrame != null) { // Close the desktop desktopFrame.dispose(); desktopFrame = null; } } // ///////////////////////////////////////////////////////////////////////////////////// // Private final void onDisposeFrame(SwingBaseWindowPeer<?, ?> windowPeer) { // Nothing to do } /** * @see org.jnode.awt.JNodeToolkit#onInitialize() */ protected void onInitialize() { log.debug("onInitialize"); // Set the repaint manager RepaintManager.setCurrentManager(repaintManager = new RepaintManager() { private WeakHashMap<Component, BufferedImage> bufferMap = new WeakHashMap<Component, BufferedImage>(); @SuppressWarnings("unused") private final Component DEFA_KEY = new Component() { private static final long serialVersionUID = 1L; }; @Override public Image getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { BufferedImage buffer = bufferMap.get(c); if (buffer == null || buffer.getWidth() < proposedWidth || buffer.getHeight() < proposedHeight) { buffer = new BufferedImage(proposedWidth, proposedHeight, BufferedImage.TYPE_INT_ARGB); bufferMap.put(c, buffer); } return buffer; } }); try { MetalLookAndFeel.setCurrentTheme(new OceanTheme()); UIManager.setLookAndFeel(new MetalLookAndFeel()); } catch (Exception x) { log.warn("Look And Feel not found: ", x); } // Create the desktop desktopFrame = new DesktopFrame(getScreenSize()); desktopFrame.show(); desktopFrame.getContentPane().repaint(); } protected void onResize() { //nothing to do here yet } public boolean isWindow(Component comp) { return comp instanceof SwingWindow; } /** * Sets the source of the event to the given component. */ static <T extends AWTEvent> T convertEvent(T event, Component awtComponent) { event.setSource(awtComponent); return event; } /** * Run the runnable now, if the current thread is the event dispatch thread, * otherwise invoke it on the event thread. */ public static void invokeNowOrLater(final Runnable runnable) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); } else { SwingUtilities.invokeLater(runnable); } } /* * Modality */ public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) { //todo implement it return true; } }