/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Nomad 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Created on Jan 21, 2007 */ package net.sf.nmedit.jtheme.component.plaf.mcui; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Rectangle; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragGestureRecognizer; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceContext; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.plaf.ComponentUI; import net.sf.nmedit.jpatch.PModule; import net.sf.nmedit.jpatch.PModuleContainer; import net.sf.nmedit.jpatch.PModuleDescriptor; import net.sf.nmedit.jpatch.PPatch; import net.sf.nmedit.jpatch.dnd.ModulesBoundingBox; import net.sf.nmedit.jpatch.dnd.PDragDrop; import net.sf.nmedit.jpatch.dnd.PModuleTransferData; import net.sf.nmedit.jpatch.dnd.PModuleTransferDataWrapper; import net.sf.nmedit.jtheme.JTContext; import net.sf.nmedit.jtheme.JTException; import net.sf.nmedit.jtheme.component.JTLayerRoot; import net.sf.nmedit.jtheme.component.JTModule; import net.sf.nmedit.jtheme.component.JTModuleContainer; import net.sf.nmedit.jtheme.component.layer.DNDLayer; import net.sf.nmedit.jtheme.util.ModuleImageRenderer; import net.sf.nmedit.nmutils.Platform; import net.sf.nmedit.nmutils.dnd.FileDnd; import net.sf.nmedit.nmutils.swing.ApplicationClipboard; public class JTModuleContainerUI extends ComponentUI { public static final String backgroundKey = "ModuleContainerUI.background"; public static final String DnDAllowedKey = "ModuleContainerUI.DnDAllowed"; public static final String baseFontKey = "ModuleContainer.font"; protected JTModuleContainer jtc; public JTModuleContainerUI(JTModuleContainer jtc) { this.jtc = jtc; } public static JTModuleContainerUI createUI(JComponent c) { return new JTModuleContainerUI((JTModuleContainer)c); } public JTModuleContainer getModuleContainer() { return jtc; } public void createPopupMenu(JTModuleContainer mc, MouseEvent e) { JPopupMenu popup = new JPopupMenu(); popup.add(new ContainerAction(this.getModuleContainer(), ContainerAction.DELETE_UNUSED)); popup.addSeparator(); popup.add(new ContainerAction(this.getModuleContainer(), ContainerAction.SHAKE)); popup.addSeparator(); getModuleContainer().installModulesMenu(popup); popup.show(mc, e.getX(), e.getY()); } public void installUI(JComponent c) { JTModuleContainer jtc = (JTModuleContainer) c; JTContext context = jtc.getContext(); UIDefaults defaults = context.getUIDefaults(); Color background = defaults.getColor(backgroundKey); if (background != null) c.setBackground(background); Font baseFont = defaults.getFont(baseFontKey); if (baseFont != null) c.setFont(baseFont); // System.out.println(DnDAllowedKey+": "+defaults.getBoolean(DnDAllowedKey)); boolean dndAllowed = defaults.getBoolean(DnDAllowedKey); installEventHandler(jtc, dndAllowed); } public void uninstallUI(JComponent c) { JTModuleContainer jtc = (JTModuleContainer) c; //JTContext context = jtc.getContext(); // UIDefaults defaults = context.getUIDefaults(); uninstallEventHandler(jtc); } public void update(Graphics g, JComponent c) { if (c.isOpaque()) { g.setColor(c.getBackground()); g.fillRect(0, 0, c.getWidth(),c.getHeight()); } paint(g, c); } public void paint(Graphics g, JComponent c) { // nothing happens here } protected EventHandler eventHandler; private DNDLayer dndLayer = null; private boolean hasPaintableSelection() { return dndLayer != null; } private void setPaintableSelection(DNDLayer ps) { if (ps != dndLayer) { if (ps == null) { // repaint and remove DNDLayer old = dndLayer; if (old != null) old.uninstall(); dndLayer = null; } else { ps.install(jtc); dndLayer = ps; } } } private DNDLayer setCurrentTransfer(Collection<? extends PModule> modules, Image transferImage) { return setCurrentTransfer(modules, new Point(5, 5), transferImage); } private DNDLayer setCurrentTransfer(Collection<? extends PModule> modules, Point dragPoint, Image transferImage) { if (modules != null) { ModulesBoundingBox box = new ModulesBoundingBox(modules, dragPoint); DNDLayer layer = new DNDLayer(jtc.getContext()); Rectangle boundingBox = box.getBoundingBox(); box.setTransferImage(transferImage); layer.setModulesBoundingBox(box); layer.setRelativeStart(dragPoint.x-boundingBox.x, dragPoint.y-boundingBox.y); layer.setLastCursorLocation(dragPoint); move(layer,dragPoint); setPaintableSelection(layer); return layer; } else { setPaintableSelection(null); return null; } } private void installEventHandler(JTModuleContainer jtc, boolean dndAllowed) { eventHandler = createEventHandler(jtc, dndAllowed); } private void uninstallEventHandler(JTModuleContainer jtc) { EventHandler handler = lookupEventHandler(jtc); if (handler != null) handler.uninstall(); } protected Object EventHandlerKey() { return "JTModuleContainerDnDHandler"; } protected EventHandler createEventHandler(JTModuleContainer jtc, boolean dndAllowed) { EventHandler handler = new EventHandler(this, dndAllowed); jtc.putClientProperty(EventHandlerKey(), handler); return handler; } protected EventHandler lookupEventHandler(JTModuleContainer jtc) { Object obj = jtc.getClientProperty(EventHandlerKey()); if (obj != null && (obj instanceof EventHandler)) return (EventHandler) obj; return null; } public static class EventHandler implements ContainerListener, DropTargetListener, DragGestureListener, DragSourceListener, MouseListener, MouseMotionListener { private JTModuleContainerUI jtcUI; private boolean dndAllowed; public EventHandler(JTModuleContainerUI jtcUI, boolean dndAllowed) { this.dndAllowed = dndAllowed; this.jtcUI = jtcUI; if (jtcUI.getModuleContainer().isDnDAllowed()) install(); } public JTModuleContainer getModuleContainer() { return jtcUI.getModuleContainer(); } private transient InputMap inputMapWhenFocused ; protected InputMap createInputMapWhenFocused() { if (inputMapWhenFocused == null) { inputMapWhenFocused = new InputMap(); fillInputMap(inputMapWhenFocused); } return inputMapWhenFocused; } protected void fillInputMap(InputMap map) { int vk_delete = KeyEvent.VK_DELETE; if (Platform.flavor() == Platform.OS.MacOSFlavor) vk_delete = KeyEvent.VK_BACK_SPACE; KeyStroke deleteModules = KeyStroke.getKeyStroke(vk_delete, 0); map.put(deleteModules, ContainerAction.DELETE); KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); map.put(escape, ContainerAction.ABORT_PASTE); KeyStroke selectAllKey; if (Platform.isFlavor(Platform.OS.MacOSFlavor)) { selectAllKey = KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.META_DOWN_MASK); } else { selectAllKey = KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK); } map.put(selectAllKey, ContainerAction.SELECT_ALL); if (Platform.isFlavor(Platform.OS.MacOSFlavor)) { map.put(KeyStroke.getKeyStroke("meta X"), ContainerAction.CUT); map.put(KeyStroke.getKeyStroke("meta C"), ContainerAction.COPY); map.put(KeyStroke.getKeyStroke("meta V"), ContainerAction.PASTE); } else { map.put(KeyStroke.getKeyStroke("ctrl X"), ContainerAction.CUT); map.put(KeyStroke.getKeyStroke("ctrl C"), ContainerAction.COPY); map.put(KeyStroke.getKeyStroke("ctrl V"), ContainerAction.PASTE); } } public void installKeyboardActions( JTModuleContainer mc) { // NMLazyActionMap.installLazyActionMap(module.getContext().getUIDefaults(), // module, BasicEventHandler.class, moduleActionMapKey); ActionMap map = mc.getActionMap(); map.put(ContainerAction.DELETE, new ContainerAction(mc, ContainerAction.DELETE)); map.put(ContainerAction.SELECT_ALL, new ContainerAction(mc, ContainerAction.SELECT_ALL)); map.put(ContainerAction.COPY, new ContainerAction(mc, ContainerAction.COPY, ApplicationClipboard.getApplicationClipboard())); map.put(ContainerAction.CUT, new ContainerAction(mc, ContainerAction.CUT, ApplicationClipboard.getApplicationClipboard())); map.put(ContainerAction.PASTE, new ContainerAction(mc, ContainerAction.PASTE, ApplicationClipboard.getApplicationClipboard())); map.put(ContainerAction.ABORT_PASTE, new ContainerAction(mc, ContainerAction.ABORT_PASTE)); InputMap im = createInputMapWhenFocused(); SwingUtilities.replaceUIInputMap(mc, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, im); } public void uninstallKeyboardActions(JTModuleContainer mc) { SwingUtilities.replaceUIInputMap(mc, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); // TODO this line shouldn't be necessary, but if setUI() was called twice // each time with a new ui instance then the input map will cause a StackOverflowError // if a key was pressed mc.setInputMap(JComponent.WHEN_FOCUSED, new InputMap()); SwingUtilities.replaceUIActionMap(mc, null); } public void install() { JTModuleContainer jtc = getModuleContainer(); installAtModuleContainer(jtc); installKeyboardActions(jtc); if (dndAllowed) { for (int i=jtc.getComponentCount()-1;i>=0;i--) { Component component = jtc.getComponent(i); if (installsAtChild(component)) installAtChild(component); } } } protected boolean installsAtChild(Component component) { return component instanceof JTModule; } public void uninstall() { JTModuleContainer jtc = getModuleContainer(); uninstallAtModuleContainer(jtc); uninstallKeyboardActions(jtc); if (dndAllowed) { for (int i=jtc.getComponentCount()-1;i>=0;i--) { Component component = jtc.getComponent(i); if (installsAtChild(component)) uninstallAtChild(component); } } } public void componentAdded(ContainerEvent e) { Component component = e.getChild(); if (installsAtChild(component)) installAtChild(component); } public void componentRemoved(ContainerEvent e) { Component component = e.getChild(); if (installsAtChild(component)) uninstallAtChild(component); } protected DropTarget moduleContainerDropTarget; protected int dndActions = DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK; // protected int dndActions = DnDConstants.ACTION_COPY_OR_MOVE; protected DropTargetListener dropTargetListener; protected DropTargetListener createDropTargetListener() { return this; } protected void installAtModuleContainer(JTModuleContainer jtc) { jtc.addMouseListener(this); jtc.addMouseMotionListener(this); if (dndAllowed) { jtc.addContainerListener(this); dropTargetListener = createDropTargetListener(); moduleContainerDropTarget = new DropTarget(jtc, dndActions, dropTargetListener, true); } } protected void uninstallAtModuleContainer(JTModuleContainer jtc) { jtc.removeMouseListener(this); if (dndAllowed) { jtc.removeContainerListener(this); } } protected void installAtChild(Component component) { component.addMouseListener(this); // JTModule module = (JTModule) component; DragSource dragSource = DragSource.getDefaultDragSource(); DragGestureRecognizer dgr = dragSource.createDefaultDragGestureRecognizer(component, dndActions, this); } protected void uninstallAtChild(Component component) { // TODO JTModule module = (JTModule) component; component.removeMouseListener(this); } protected JTModuleContainer getContainer() { return getModuleContainer(); } protected boolean isMDDropOk(int action, Transferable t) { PModuleContainer pmc = getContainer().getModuleContainer(); boolean isOKPass1 = (action & DnDConstants.ACTION_COPY) >0 && pmc != null && PDragDrop.isModuleDescriptorFlavorSupported(t); if (isOKPass1) { PModuleDescriptor md = PDragDrop.getModuleDescriptor(t); if (pmc.canAdd(md)) return true; } return false; } public void dragEnter(DropTargetDragEvent dtde) { DataFlavor flavors[] = dtde.getTransferable().getTransferDataFlavors(); DNDLayer layer = jtcUI.dndLayer; ModulesBoundingBox currentTransfer = layer == null ? null : layer.getModulesBoundingBox(); Transferable t = dtde.getTransferable(); if (currentTransfer == null) { if (t.isDataFlavorSupported(PDragDrop.ModuleSelectionFlavor)) { try { PModuleTransferDataWrapper transfer = (PModuleTransferDataWrapper) t.getTransferData(PDragDrop.ModuleSelectionFlavor); jtcUI.setCurrentTransfer(transfer.getModules(), transfer.getDragStartLocation(), transfer.getTransferImage()); } catch (Throwable e) { jtcUI.setCurrentTransfer(null, null); } } else if (FileDnd.testFileFlavor(t.getTransferDataFlavors())) { PPatch patch = getModuleContainer().getPatchContainer().getPatch(); DataFlavor fileFlavor = FileDnd.getFileFlavor(t.getTransferDataFlavors()); List<File> files = FileDnd.getTransferableFiles(fileFlavor, t); if (files.size() == 1) { PPatch newPatch; try { newPatch = patch.createFromFile(files.get(0)); } catch (Exception e) { newPatch = null; } if (newPatch != null) { PModuleContainer newMc = null; for (int i = 0; i < newPatch.getModuleContainerCount(); i++) { newMc = newPatch.getModuleContainer(i); if (newMc.getModuleCount() > 0) break; } if (newMc != null) { zeroAlign(newMc); Image transferImage = null; try { transferImage = ModuleImageRenderer.createImage( jtcUI.jtc, newMc, true, false, true); } catch (JTException jte) { transferImage = null; } jtcUI.setCurrentTransfer(newMc.getModules(), transferImage); } else { dtde.rejectDrag(); return; } } } } else { // System.out.println("flavor not supported"); } } if (isMDDropOk(dtde.getDropAction(), t)) { DNDLayer newlayer = DNDLayer.forTransferImage(jtcUI.jtc.getContext(), t); jtcUI.setPaintableSelection(newlayer); // get might return null dtde.acceptDrag(DnDConstants.ACTION_COPY); } else if (t.isDataFlavorSupported(PDragDrop.ModuleSelectionFlavor)) { dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); } else if (FileDnd.testFileFlavor(t.getTransferDataFlavors())) { dtde.acceptDrag(DnDConstants.ACTION_COPY); } else { dtde.rejectDrag(); } } private void zeroAlign(PModuleContainer mc) { // moves all modules to origin (0, 0) without changing the location relative to each other List<PModule> slist = new ArrayList<PModule>(mc.getModuleCount()); Point min = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE); for (PModule module: mc) { min.x = Math.min(min.x, module.getScreenX()); min.y = Math.min(min.y, module.getScreenY()); slist.add(module); } // sort modules ascending so moving them does not cause a revalidation of the layout Collections.sort(slist, new ZeroAlignOrder()); for (PModule module: slist) { module.setScreenLocation( module.getScreenX()-min.x, module.getScreenY()-min.y ); } } private static class ZeroAlignOrder implements Comparator<PModule> { public int compare(PModule a, PModule b) { int c; // first compare by column c = b.getScreenX()-a.getScreenX(); if (c != 0) return Integer.signum(c); // then compare by row c = b.getScreenY()-a.getScreenY(); return Integer.signum(c); } } public void dragExit(DropTargetEvent dte) { jtcUI.setPaintableSelection(null); jtcUI.setCurrentTransfer(null, null); } public void dragOver(DropTargetDragEvent dtde) { if (isMDDropOk(dtde.getDropAction(), dtde.getTransferable())) { DNDLayer ps = jtcUI.dndLayer; if (ps != null) { jtcUI.move(ps, dtde.getLocation()); } dtde.acceptDrag(DnDConstants.ACTION_COPY); return; } /*if (dtde.getCurrentDataFlavorsAsList().contains(ModuleDragSource.ModuleInfoFlavor)) { dtde.acceptDrag(DnDConstants.ACTION_COPY); } else*/ if (dtde.getCurrentDataFlavorsAsList().contains(PDragDrop.ModuleSelectionFlavor)) { PModuleTransferData data; try { data = (PModuleTransferData) dtde.getTransferable().getTransferData(PDragDrop.ModuleSelectionFlavor); if (data.getSourcePatch() == getModuleContainer().getPatchContainer().getPatch()) { dtde.acceptDrag(dtde.getDropAction() & (DnDConstants.ACTION_MOVE | DnDConstants.ACTION_COPY | DnDConstants.ACTION_LINK)); } else { dtde.acceptDrag(DnDConstants.ACTION_COPY); } if (data!=null) { DNDLayer layer = jtcUI.dndLayer; if(layer != null) jtcUI.move(layer, dtde.getLocation()); return ; } } catch (Throwable e) { ; } } if (FileDnd.testFileFlavor(dtde.getTransferable().getTransferDataFlavors())) { dtde.acceptDrag(DnDConstants.ACTION_COPY); DNDLayer layer = jtcUI.dndLayer; if(layer != null) { jtcUI.move(layer, dtde.getLocation()); return; } } dtde.rejectDrag(); } public void drop(DropTargetDropEvent dtde) { jtcUI.setCurrentTransfer(null, null); jtcUI.setPaintableSelection(null); JTModuleContainer jtmc = jtcUI.getModuleContainer(); Transferable transfer = dtde.getTransferable(); if (isMDDropOk(dtde.getDropAction(), dtde.getTransferable())) { jtmc.dropNewModule(dtde); } else if (dtde.isDataFlavorSupported(PDragDrop.ModuleSelectionFlavor) && dtde.isLocalTransfer()) { jtmc.copyMoveModules(dtde); } else if (FileDnd.testFileFlavor(transfer.getTransferDataFlavors())) { jtmc.dropPatchFile(dtde); } else { dtde.rejectDrop(); dtde.dropComplete(false); } } public void dropActionChanged(DropTargetDragEvent dtde) { if (isMDDropOk(dtde.getDropAction(), dtde.getTransferable())) { dtde.acceptDrag(DnDConstants.ACTION_COPY); return; } if (dtde.getTransferable().isDataFlavorSupported(PDragDrop.ModuleSelectionFlavor)) { dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); return; } dtde.rejectDrag(); } public void dragGestureRecognized(DragGestureEvent dge) { JTModuleContainer jtc = getModuleContainer(); Component src = dge.getComponent(); if (src instanceof JTModule) { JTModule module = (JTModule) dge.getComponent(); if (!module.isEnabled()) return; if (jtc.isSelectionEmpty()) { jtc.addSelection(module); } else if (jtc.isInSelection(module)) { // thats ok } else { if (!jtc.isInSelection(module)) { jtc.clearSelection(); jtc.addSelection(module); } } } if (!jtc.isSelectionEmpty()) { Component c = dge.getComponent(); Point dndOrigin = dge.getDragOrigin(); // if (c instanceof JTModule) // always true { dndOrigin = SwingUtilities.convertPoint(c, dndOrigin, getModuleContainer()); } Collection<? extends JTModule> jtmodules = jtc.getSelectedModules(); Collection<? extends PModule> collection = jtc.getSelectedPModules(); PModuleTransferDataWrapper transfer = new PModuleTransferDataWrapper(this.getModuleContainer().getModuleContainer(), collection, dndOrigin); ModuleImageRenderer mir = new ModuleImageRenderer(jtmodules); mir.setForDragAndDrop(true); mir.setPaintExtraBorder(true); transfer.setTransferImage(mir.render()); jtcUI.setCurrentTransfer(transfer.getModules(), transfer.getDragStartLocation(), transfer.getTransferImage()); dge.startDrag(DragSource.DefaultMoveDrop, transfer, this); } } public JTModuleContainer getSource() { return getModuleContainer(); } public Point getDragStartLocation() { throw new UnsupportedOperationException(); } public void dragDropEnd(DragSourceDropEvent dsde) { // no op } public void dragEnter(DragSourceDragEvent dsde) { DragSourceContext context = dsde.getDragSourceContext(); boolean invalid = false; switch (getSourceForEvent(dsde)) { case DRAG_MODULES_CREATE: context.setCursor(DragSource.DefaultLinkDrop); break; case DRAG_MODULES_FROM_THIS_CONTAINER: context.setCursor(DragSource.DefaultMoveDrop); break; case DRAG_MODULES_FROM_OTHER_CONTAINER: context.setCursor(DragSource.DefaultCopyDrop); break; // reject ... case DRAG_INVALID: invalid = true; break; default: break; } if (invalid) { if ((context.getSourceActions() & DnDConstants.ACTION_COPY)>0) context.setCursor(DragSource.DefaultCopyNoDrop); else if ((context.getSourceActions() & DnDConstants.ACTION_MOVE)>0) context.setCursor(DragSource.DefaultMoveNoDrop); else if ((context.getSourceActions() & DnDConstants.ACTION_LINK)>0) context.setCursor(DragSource.DefaultLinkNoDrop); else context.setCursor(DragSource.DefaultCopyNoDrop); } } static final int DRAG_INVALID = -1; // test for < 0 static final int DRAG_MODULES_FROM_THIS_CONTAINER = 0; static final int DRAG_MODULES_FROM_OTHER_CONTAINER = 1; static final int DRAG_MODULES_CREATE = 2; protected int getSourceForEvent(DragSourceEvent e) { DragSourceContext context = e.getDragSourceContext(); Transferable transfer = context.getTransferable(); if (transfer instanceof PModuleTransferDataWrapper) { if (jtcUI.jtc == context.getComponent()) return DRAG_MODULES_FROM_THIS_CONTAINER; else return DRAG_MODULES_FROM_OTHER_CONTAINER; } if (PDragDrop.isModuleDescriptorFlavorSupported(transfer)) return DRAG_MODULES_CREATE; return DRAG_INVALID; } public void dragExit(DragSourceEvent dse) { //jtcUI.setPaintableSelection(null); // } public void dragOver(DragSourceDragEvent dsde) { } public void dropActionChanged(DragSourceDragEvent dsde) { // no op } public void mouseClickedAtModuleContainer(MouseEvent e) { if (Platform.isLeftMouseButtonOnly(e) && !jtcUI.hasPaintableSelection()) getModuleContainer().clearSelection(); } public void mouseClickedAtModule(MouseEvent e) { // this is a real dilemma here. THe good way would be to do this through mouseClicked, // but at least under OSX< this is not reliable. // on mouseReleased is the better way to go, because mousePressed starts a drag and we don't want to deselect on that // so we need to check by hand if a popup menu was opened by this click, because isPopupTrigger is on mousePressed if (Platform.couldBePopupTrigger(e)) return; JTModuleContainer jtc = getModuleContainer(); JTModule module = (JTModule) e.getComponent(); boolean shift = e.isShiftDown(); if (Platform.isFlavor(Platform.OS.MacOSFlavor)) { // apple key has a different behaviour under osx boolean meta = e.isMetaDown(); if (meta && shift) return; if (meta) { if (jtc.isInSelection(module)) { jtc.removeSelection(module); } else { jtc.addSelection(module); } return; } } else { boolean ctrl = e.isControlDown(); if (shift && ctrl) return; if (ctrl) { jtc.removeSelection(module); return; } } if (shift) jtc.addSelection(module); else // !(shift||ctrl) { jtc.selectOnly(module); } } public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { // no op } public void mouseExited(MouseEvent e) { // no op } public void mousePressed(MouseEvent e) { if (jtcUI.isPasting) { jtcUI.abortPaste(); return; } JTModuleContainer mc = jtcUI.getModuleContainer(); if (e.getComponent() == mc) // do not rob focus from module { getModuleContainer().requestFocusInWindow(); } if (Platform.isPopupTrigger(e) && e.getComponent() == mc) { jtcUI.createPopupMenu(mc, e); } } public void mouseReleased(MouseEvent e) { if (dndAllowed) { if (e.getComponent() == getModuleContainer()) mouseClickedAtModuleContainer(e); } if (e.getComponent() instanceof JTModule) mouseClickedAtModule(e); JTModuleContainer mc = jtcUI.getModuleContainer(); if (Platform.isLeftMouseButtonOnly(e) && e.getComponent() == mc && jtcUI.hasPaintableSelection()) { jtcUI.setPaintableSelection(null); } if (Platform.isPopupTrigger(e) && e.getComponent() == mc) { jtcUI.createPopupMenu(mc, e); } } public void mouseDragged(MouseEvent e) { JTModuleContainer mc = jtcUI.getModuleContainer(); if (Platform.isLeftMouseButtonOnly(e) && e.getComponent() == mc) { if (!jtcUI.hasPaintableSelection()) { startNewSelectionRectangle(e); } updateSelectionRectangle(e); } } HashSet<JTModule> oldSelection = null; private void startNewSelectionRectangle(MouseEvent e) { JTModuleContainer mc = jtcUI.getModuleContainer(); DNDLayer layer = new DNDLayer(jtcUI.jtc.getContext()); layer.setLastCursorLocation(e.getPoint()); layer.setBoundingBoxBorder(); layer.setOrigin(e.getPoint()); layer.setBounds(e.getX(), e.getY(), e.getX(), e.getY()); oldSelection = new HashSet<JTModule>(mc.getSelectedModules()); jtcUI.setPaintableSelection(layer); } private void updateSelectionRectangle(MouseEvent e) { Point point = e.getPoint(); DNDLayer ps = jtcUI.dndLayer; if (ps == null) return; Rectangle select = ps.getBounds(); Point start = ps.getOrigin(); JTModuleContainer jtc = jtcUI.getModuleContainer(); int x1, x2, y1, y2; x1 = Math.min(start.x, point.x); x2 = Math.max(start.x, point.x); y1 = Math.min(start.y, point.y); y2 = Math.max(start.y, point.y); ps.setBounds(x1, y1, x2-x1, y2-y1); ps.updateScrollPosition(e.getPoint()); //ps.repaint(); boolean shift = e.isShiftDown(); boolean meta = false; boolean ctrl = false; if (Platform.isFlavor(Platform.OS.MacOSFlavor)) { // apple key has a different behaviour under osx meta = e.isMetaDown(); if (meta && shift) return; } else { ctrl = e.isControlDown(); if (shift && ctrl) return; } if (!meta && !shift && !ctrl) { jtc.clearSelection(); oldSelection.clear(); } Component[] components = jtc.getComponents(); for (int i=components.length-1;i>=0;i--) { Component c = components[i]; if (c instanceof JTModule) { JTModule mui = (JTModule) c; if (select.intersects(mui.getBounds())) { if (meta) { if (oldSelection.contains(mui)) { jtc.removeSelection(mui); } else { jtc.addSelection(mui); } } else if (ctrl) { if (oldSelection.contains(mui)) jtc.removeSelection(mui); } else { jtc.addSelection(mui); } } else { if (meta || shift || ctrl) { if (oldSelection.contains(mui)) { jtc.addSelection(mui); } else { jtc.removeSelection(mui); } } } } } } public void mouseMoved(MouseEvent e) { // if (jtcUI.isPasting) { // jtcUI.updateScrollPosition(e.getPoint()); // paintDragOver(jtcUI.getCurrentTransfer(), e.getPoint()); // } } } private void move(DNDLayer layer, Point p) { layer.setPositionFromCursor(p); } protected class PasteEventHandler implements MouseListener, MouseMotionListener, FocusListener { protected JTModuleContainerUI jtcUI; public PasteEventHandler(JTModuleContainerUI ui) { this.jtcUI = ui; } public void mouseClicked(MouseEvent e) { // no op } public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub } public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub } public void mouseReleased(MouseEvent e) { jtcUI.pasteAt(e.getPoint()); } public void mouseDragged(MouseEvent e) { // TODO Auto-generated method stub } public void mouseMoved(MouseEvent e) { DNDLayer layer = jtcUI.dndLayer; if (layer != null) move(layer, e.getPoint()); } Object previousOwner = null; public void focusGained(FocusEvent e) { // TODO Auto-generated method stub } public void focusLost(FocusEvent e) { jtcUI.abortPaste(); } } private boolean isPasting = false; PasteEventHandler handler = null; PModuleTransferDataWrapper pasteTransfer = null; public void pasteAt(Point p) { getModuleContainer().copyModules(pasteTransfer, p, false); abortPaste(); } public void abortPaste() { if (isPasting) { setPaintableSelection(null); JTLayerRoot root = getModuleContainer().getLayerRoot(); if (handler != null) { root.removeMouseListener(handler); root.removeMouseMotionListener(handler); } getModuleContainer().getLayerRoot().ignoreMouseEvents(); isPasting = false; pasteTransfer = null; } } public void startPaste(PModuleTransferDataWrapper transfer) { isPasting = true; if (handler == null) { handler = new PasteEventHandler(this); getModuleContainer().addFocusListener(handler); } pasteTransfer = transfer; // FocusManager focusManager = FocusManager.getCurrentManager(); // focusManager.addPropertyChangeListener(handler); getModuleContainer().requestFocus(); JTLayerRoot root = getModuleContainer().getLayerRoot(); root.addMouseListener(handler); root.addMouseMotionListener(handler); root.captureMouseEvents(); Point onScreen = MouseInfo.getPointerInfo().getLocation(); SwingUtilities.convertPointFromScreen(onScreen, getModuleContainer()); DNDLayer layer = setCurrentTransfer(transfer.getModules(), new Point(0, 0), transfer.getTransferImage()); layer.setIgnoresEvents(true); move(layer, onScreen); } }