Merge branch 'bug/JAL-797' into develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 28 May 2018 08:23:26 +0000 (09:23 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 28 May 2018 08:23:26 +0000 (09:23 +0100)
1  2 
src/jalview/gui/Desktop.java

@@@ -32,7 -32,6 +32,7 @@@ import jalview.io.FileFormatException
  import jalview.io.FileFormatI;
  import jalview.io.FileFormats;
  import jalview.io.FileLoader;
 +import jalview.io.FormatAdapter;
  import jalview.io.IdentifyFile;
  import jalview.io.JalviewFileChooser;
  import jalview.io.JalviewFileView;
@@@ -69,6 -68,7 +69,7 @@@ import java.awt.dnd.DropTargetEvent
  import java.awt.dnd.DropTargetListener;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
+ import java.awt.event.InputEvent;
  import java.awt.event.KeyEvent;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
@@@ -92,10 -92,13 +93,13 @@@ import java.util.concurrent.Executors
  import java.util.concurrent.Semaphore;
  
  import javax.swing.AbstractAction;
+ import javax.swing.Action;
+ import javax.swing.ActionMap;
  import javax.swing.Box;
  import javax.swing.BoxLayout;
  import javax.swing.DefaultDesktopManager;
  import javax.swing.DesktopManager;
+ import javax.swing.InputMap;
  import javax.swing.JButton;
  import javax.swing.JCheckBox;
  import javax.swing.JComboBox;
@@@ -117,8 -120,6 +121,8 @@@ import javax.swing.event.InternalFrameE
  import javax.swing.event.MenuEvent;
  import javax.swing.event.MenuListener;
  
 +import org.stackoverflowusers.file.WindowsShortcut;
 +
  /**
   * Jalview Desktop
   * 
@@@ -852,7 -853,6 +856,7 @@@ public class Desktop extends jalview.jb
      frame.setResizable(resizable);
      frame.setMaximizable(resizable);
      frame.setIconifiable(resizable);
 +    frame.setOpaque(false);
  
      if (frame.getX() < 1 && frame.getY() < 1)
      {
            menuItem.removeActionListener(menuItem.getActionListeners()[0]);
          }
          windowMenu.remove(menuItem);
 -
 -        System.gc();
        };
      });
  
        }
      });
  
+     setKeyBindings(frame);
      desktop.add(frame);
  
      windowMenu.add(menuItem);
      }
    }
  
+   /**
+    * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
+    * the window
+    * 
+    * @param frame
+    */
+   private static void setKeyBindings(JInternalFrame frame)
+   {
+     @SuppressWarnings("serial")
+     final Action closeAction = new AbstractAction()
+     {
+       @Override
+       public void actionPerformed(ActionEvent e)
+       {
+         frame.dispose();
+       }
+     };
+     /*
+      * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
+      */
+     KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
+             InputEvent.CTRL_DOWN_MASK);
+     KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
+             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+     InputMap inputMap = frame
+             .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+     String ctrlW = ctrlWKey.toString();
+     inputMap.put(ctrlWKey, ctrlW);
+     inputMap.put(cmdWKey, ctrlW);
+     ActionMap actionMap = frame.getActionMap();
+     actionMap.put(ctrlW, closeAction);
+   }
    @Override
    public void lostOwnership(Clipboard clipboard, Transferable contents)
    {
      {
        ssm.resetAll();
      }
 -    System.gc();
    }
  
    @Override
      return groovyConsole;
    }
  
 +  /**
 +   * handles the payload of a drag and drop event.
 +   * 
 +   * TODO refactor to desktop utilities class
 +   * 
 +   * @param files
 +   *          - Data source strings extracted from the drop event
 +   * @param protocols
 +   *          - protocol for each data source extracted from the drop event
 +   * @param evt
 +   *          - the drop event
 +   * @param t
 +   *          - the payload from the drop event
 +   * @throws Exception
 +   */
    public static void transferFromDropTarget(List<String> files,
            List<DataSourceType> protocols, DropTargetDropEvent evt,
            Transferable t) throws Exception
    {
  
      DataFlavor uriListFlavor = new DataFlavor(
 -            "text/uri-list;class=java.lang.String");
 +            "text/uri-list;class=java.lang.String"), urlFlavour = null;
 +    try
 +    {
 +      urlFlavour = new DataFlavor(
 +              "application/x-java-url; class=java.net.URL");
 +    } catch (ClassNotFoundException cfe)
 +    {
 +      Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
 +    }
 +
 +    if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
 +    {
 +
 +      try
 +      {
 +      java.net.URL url = (URL) t.getTransferData(urlFlavour);
 +        // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
 +        // means url may be null.
 +      if (url != null)
 +      {
 +        protocols.add(DataSourceType.URL);
 +        files.add(url.toString());
 +        Cache.log.debug("Drop handled as URL dataflavor "
 +                + files.get(files.size() - 1));
 +          return;
 +        }
 +        else
 +        {
 +          if (Platform.isAMac())
 +          {
 +            System.err.println(
 +                    "Please ignore plist error - occurs due to problem with java 8 on OSX");
 +          }
 +          ;
 +      }
 +      } catch (Throwable ex)
 +      {
 +        Cache.log.debug("URL drop handler failed.", ex);
 +      }
 +    }
      if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
      {
        // Works on Windows and MacOSX
          // fallback to text: workaround - on OSX where there's a JVM bug
          Cache.log.debug("standard URIListFlavor failed. Trying text");
          // try text fallback
 -        data = (String) t.getTransferData(
 -                new DataFlavor("text/plain;class=java.lang.String"));
 -        if (Cache.log.isDebugEnabled())
 +        DataFlavor textDf = new DataFlavor(
 +                "text/plain;class=java.lang.String");
 +        if (t.isDataFlavorSupported(textDf))
          {
 -          Cache.log.debug("fallback returned " + data);
 +          data = (String) t.getTransferData(textDf);
          }
 +
 +        Cache.log.debug("Plain text drop content returned "
 +                + (data == null ? "Null - failed" : data));
 +
        }
 -      while (protocols.size() < files.size())
 -      {
 -        Cache.log.debug("Adding missing FILE protocol for "
 -                + files.get(protocols.size()));
 -        protocols.add(DataSourceType.FILE);
 -      }
 -      for (java.util.StringTokenizer st = new java.util.StringTokenizer(
 -              data, "\r\n"); st.hasMoreTokens();)
 +      if (data != null)
        {
 -        added = true;
 -        String s = st.nextToken();
 -        if (s.startsWith("#"))
 +        while (protocols.size() < files.size())
          {
 -          // the line is a comment (as per the RFC 2483)
 -          continue;
 +          Cache.log.debug("Adding missing FILE protocol for "
 +                  + files.get(protocols.size()));
 +          protocols.add(DataSourceType.FILE);
          }
 -        java.net.URI uri = new java.net.URI(s);
 -        if (uri.getScheme().toLowerCase().startsWith("http"))
 +        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
 +                data, "\r\n"); st.hasMoreTokens();)
          {
 -          protocols.add(DataSourceType.URL);
 -          files.add(uri.toString());
 -        }
 -        else
 -        {
 -          // otherwise preserve old behaviour: catch all for file objects
 -          java.io.File file = new java.io.File(uri);
 -          protocols.add(DataSourceType.FILE);
 -          files.add(file.toString());
 +          added = true;
 +          String s = st.nextToken();
 +          if (s.startsWith("#"))
 +          {
 +            // the line is a comment (as per the RFC 2483)
 +            continue;
 +          }
 +          java.net.URI uri = new java.net.URI(s);
 +          if (uri.getScheme().toLowerCase().startsWith("http"))
 +          {
 +            protocols.add(DataSourceType.URL);
 +            files.add(uri.toString());
 +          }
 +          else
 +          {
 +            // otherwise preserve old behaviour: catch all for file objects
 +            java.io.File file = new java.io.File(uri);
 +            protocols.add(DataSourceType.FILE);
 +            files.add(file.toString());
 +          }
          }
        }
 +
        if (Cache.log.isDebugEnabled())
        {
          if (data == null || !added)
          {
 -          Cache.log.debug(
 -                  "Couldn't resolve drop data. Here are the supported flavors:");
 -          for (DataFlavor fl : t.getTransferDataFlavors())
 +
 +          if (t.getTransferDataFlavors() != null
 +                  && t.getTransferDataFlavors().length > 0)
            {
              Cache.log.debug(
 -                    "Supported transfer dataflavor: " + fl.toString());
 -            Object df = t.getTransferData(fl);
 -            if (df != null)
 +                    "Couldn't resolve drop data. Here are the supported flavors:");
 +            for (DataFlavor fl : t.getTransferDataFlavors())
              {
 -              Cache.log.debug("Retrieves: " + df);
 -            }
 -            else
 -            {
 -              Cache.log.debug("Retrieved nothing");
 +              Cache.log.debug(
 +                      "Supported transfer dataflavor: " + fl.toString());
 +              Object df = t.getTransferData(fl);
 +              if (df != null)
 +              {
 +                Cache.log.debug("Retrieves: " + df);
 +              }
 +              else
 +              {
 +                Cache.log.debug("Retrieved nothing");
 +              }
              }
            }
 +          else
 +          {
 +            Cache.log.debug("Couldn't resolve dataflavor for drop: "
 +                    + t.toString());
 +          }
 +        }
 +      }
 +    }
 +    if (Platform.isWindows())
 +
 +    {
 +      Cache.log.debug("Scanning dropped content for Windows Link Files");
 +
 +      // resolve any .lnk files in the file drop
 +      for (int f = 0; f < files.size(); f++)
 +      {
 +        String source = files.get(f).toLowerCase();
 +        if (protocols.get(f).equals(DataSourceType.FILE)
 +                && (source.endsWith(".lnk") || source.endsWith(".url")
 +                        || source.endsWith(".site")))
 +        {
 +          try {
 +            File lf = new File(files.get(f));
 +            // process link file to get a URL
 +            Cache.log.debug("Found potential link file: " + lf);
 +            WindowsShortcut wscfile = new WindowsShortcut(lf);
 +            String fullname = wscfile.getRealFilename();
 +            protocols.set(f, FormatAdapter.checkProtocol(fullname));
 +            files.set(f, fullname);
 +            Cache.log.debug("Parsed real filename " + fullname
 +                    + " to extract protocol: " + protocols.get(f));
 +          }
 +          catch (Exception ex)
 +          {
 +            Cache.log.error("Couldn't parse "+files.get(f)+" as a link file.",ex);
 +          }
          }
        }
      }
    {
      Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
    }
 +
 +  /**
 +   * Answers a (possibly empty) list of any structure viewer frames (currently
 +   * for either Jmol or Chimera) which are currently open. This may optionally
 +   * be restricted to viewers of a specified class, or viewers linked to a
 +   * specified alignment panel.
 +   * 
 +   * @param apanel
 +   *          if not null, only return viewers linked to this panel
 +   * @param structureViewerClass
 +   *          if not null, only return viewers of this class
 +   * @return
 +   */
 +  public List<StructureViewerBase> getStructureViewers(
 +          AlignmentPanel apanel,
 +          Class<? extends StructureViewerBase> structureViewerClass)
 +  {
 +    List<StructureViewerBase> result = new ArrayList<>();
 +    JInternalFrame[] frames = Desktop.instance.getAllFrames();
 +
 +    for (JInternalFrame frame : frames)
 +    {
 +      if (frame instanceof StructureViewerBase)
 +      {
 +        if (structureViewerClass == null
 +                || structureViewerClass.isInstance(frame))
 +        {
 +          if (apanel == null
 +                  || ((StructureViewerBase) frame).isLinkedWith(apanel))
 +          {
 +            result.add((StructureViewerBase) frame);
 +          }
 +        }
 +      }
 +    }
 +    return result;
 +  }
  }