JAL-4285 Better checkFiles processing. Better dialog.
[jalview.git] / src / jalview / gui / Desktop.java
index 44bf61e..ee24c25 100644 (file)
@@ -22,6 +22,7 @@ package jalview.gui;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
+import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
@@ -51,6 +52,7 @@ import java.awt.event.WindowEvent;
 import java.awt.geom.AffineTransform;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -63,6 +65,7 @@ import java.util.Hashtable;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Vector;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -85,9 +88,12 @@ import javax.swing.JFrame;
 import javax.swing.JInternalFrame;
 import javax.swing.JLabel;
 import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
 import javax.swing.JProgressBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.SwingUtilities;
@@ -104,6 +110,7 @@ import jalview.api.AlignmentViewPanel;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
+import jalview.bin.Jalview.ExitCode;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.Sequence;
@@ -121,6 +128,7 @@ import jalview.io.FormatAdapter;
 import jalview.io.IdentifyFile;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
+import jalview.io.exceptions.ImageOutputException;
 import jalview.jbgui.GSplitFrame;
 import jalview.jbgui.GStructureViewer;
 import jalview.project.Jalview2XML;
@@ -438,7 +446,7 @@ public class Desktop extends jalview.jbgui.GDesktop
          * Send this message to stderr as the warning that follows (due to
          * reflection) also goes to stderr.
          */
-        System.err.println(
+        jalview.bin.Console.errPrintln(
                 "Linux platform only! You may have the following warning next: \"WARNING: An illegal reflective access operation has occurred\"\nThis is expected and cannot be avoided, sorry about that.");
       }
       final String awtAppClassName = "awtAppClassName";
@@ -571,15 +579,16 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       // Thread off a new instance of the file chooser - this reduces the time
-      // it
-      // takes to open it later on.
+      // it takes to open it later on.
       new Thread(new Runnable()
       {
         @Override
         public void run()
         {
           jalview.bin.Console.debug("Filechooser init thread started.");
-          String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
+          String fileFormat = FileLoader.getUseDefaultFileFormat()
+                  ? Cache.getProperty("DEFAULT_FILE_FORMAT")
+                  : null;
           JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
                   fileFormat);
           jalview.bin.Console.debug("Filechooser init thread finished.");
@@ -625,6 +634,13 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     });
     desktop.addMouseListener(ma);
+
+    if (Platform.isJS())
+    {
+      // used for jalviewjsTest
+      jalview.bin.Console.info("JALVIEWJS: CREATED DESKTOP");
+    }
+
   }
 
   /**
@@ -866,7 +882,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
       } catch (Exception ex)
       {
-        System.out.println(
+        jalview.bin.Console.outPrintln(
                 "Unable to paste alignment from system clipboard:\n" + ex);
       }
     }
@@ -1071,7 +1087,38 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     setKeyBindings(frame);
 
-    desktop.add(frame);
+    // Since the latest FlatLaf patch, we occasionally have problems showing
+    // structureViewer frames...
+    int tries = 3;
+    boolean shown = false;
+    Exception last = null;
+    do
+    {
+      try
+      {
+        desktop.add(frame);
+        shown = true;
+      } catch (IllegalArgumentException iaex)
+      {
+        last = iaex;
+        tries--;
+        jalview.bin.Console.info("Squashed IllegalArgument Exception ("
+                + tries + " left) for " + frame.getTitle(), iaex);
+        try
+        {
+          Thread.sleep(5);
+        } catch (InterruptedException iex)
+        {
+        }
+        ;
+      }
+    } while (!shown && tries > 0);
+    if (!shown)
+    {
+      jalview.bin.Console.error(
+              "Serious Problem whilst showing window " + frame.getTitle(),
+              last);
+    }
 
     windowMenu.add(menuItem);
 
@@ -1231,7 +1278,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   @Override
   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
   {
-    String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
+    String fileFormat = FileLoader.getUseDefaultFileFormat()
+            ? Cache.getProperty("DEFAULT_FILE_FORMAT")
+            : null;
     JalviewFileChooser chooser = JalviewFileChooser.forRead(
             Cache.getProperty("LAST_DIRECTORY"), fileFormat,
             BackupFiles.getEnabled());
@@ -1481,7 +1530,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     // this will run the shutdownHook but QuitHandler.getQuitResponse() should
     // not run a second time if gotQuitResponse flag has been set (i.e. user
     // confirmed quit of some kind).
-    Jalview.exit("Desktop exiting.", 0);
+    Jalview.exit("Desktop exiting.", ExitCode.OK);
   }
 
   private void storeLastKnownDimensions(String string, Rectangle jc)
@@ -1594,7 +1643,8 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
     } catch (Exception ex)
     {
-      System.err.println("Error opening help: " + ex.getMessage());
+      jalview.bin.Console
+              .errPrintln("Error opening help: " + ex.getMessage());
     }
   }
 
@@ -1825,7 +1875,8 @@ public class Desktop extends jalview.jbgui.GDesktop
     boolean autoSave = projectFile != null && !saveAs
             && BackupFiles.getEnabled();
 
-    // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
+    // jalview.bin.Console.outPrintln("autoSave="+autoSave+",
+    // projectFile='"+projectFile+"',
     // saveAs="+saveAs+", Backups
     // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
 
@@ -3158,7 +3209,15 @@ public class Desktop extends jalview.jbgui.GDesktop
     String title = "View of desktop";
     ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
             title);
-    exporter.doExport(of, this, width, height, title);
+    try
+    {
+      exporter.doExport(of, this, width, height, title);
+    } catch (ImageOutputException ioex)
+    {
+      jalview.bin.Console.error(
+              "Unexpected error whilst writing Jalview desktop snapshot as EPS",
+              ioex);
+    }
   }
 
   /**
@@ -3366,7 +3425,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         {
           if (Platform.isAMacAndNotJS())
           {
-            System.err.println(
+            jalview.bin.Console.errPrintln(
                     "Please ignore plist error - occurs due to problem with java 8 on OSX");
           }
         }
@@ -3598,4 +3657,149 @@ public class Desktop extends jalview.jbgui.GDesktop
       jalview.bin.Console.debug(Cache.getStackTraceString(e));
     }
   }
+
+  /**
+   * closes the current instance window, disposes and forgets about it.
+   */
+  public static void closeDesktop()
+  {
+    if (Desktop.instance != null)
+    {
+      Desktop.instance.closeAll_actionPerformed(null);
+      Desktop.instance.setVisible(false);
+      Desktop us = Desktop.instance;
+      Desktop.instance = null;
+      // call dispose in a separate thread - try to avoid indirect deadlocks
+      new Thread(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          ExecutorService dex = us.dialogExecutor;
+          if (dex != null)
+          {
+            dex.shutdownNow();
+            us.dialogExecutor = null;
+            us.block.drainPermits();
+          }
+          us.dispose();
+        }
+      }).start();
+    }
+  }
+
+  /**
+   * checks if any progress bars are being displayed in any of the windows
+   * managed by the desktop
+   * 
+   * @return
+   */
+  public boolean operationsAreInProgress()
+  {
+    JInternalFrame[] frames = getAllFrames();
+    for (JInternalFrame frame : frames)
+    {
+      if (frame instanceof IProgressIndicator)
+      {
+        if (((IProgressIndicator) frame).operationInProgress())
+        {
+          return true;
+        }
+      }
+    }
+    return operationInProgress();
+  }
+
+  /**
+   * keep track of modal JvOptionPanes open as modal dialogs for AlignFrames.
+   * The way the modal JInternalFrame is made means it cannot be a child of an
+   * AlignFrame, so closing the AlignFrame might leave the modal open :(
+   */
+  private static Map<AlignFrame, JInternalFrame> alignFrameModalMap = new HashMap<>();
+
+  protected static void addModal(AlignFrame af, JInternalFrame jif)
+  {
+    alignFrameModalMap.put(af, jif);
+  }
+
+  protected static void closeModal(AlignFrame af)
+  {
+    if (!alignFrameModalMap.containsKey(af))
+    {
+      return;
+    }
+    JInternalFrame jif = alignFrameModalMap.get(af);
+    if (jif != null)
+    {
+      try
+      {
+        jif.setClosed(true);
+      } catch (PropertyVetoException e)
+      {
+        e.printStackTrace();
+      }
+    }
+    alignFrameModalMap.remove(af);
+  }
+
+  public void nonBlockingDialog(String title, String message, String button,
+          int type, boolean scrollable, boolean modal)
+  {
+    nonBlockingDialog(32, 2, title, message, null, button, type, scrollable,
+            modal);
+  }
+
+  public void nonBlockingDialog(int width, int height, String title,
+          String message, String boxtext, String button, int type,
+          boolean scrollable, boolean modal)
+  {
+    if (type < 0)
+    {
+      type = JvOptionPane.WARNING_MESSAGE;
+    }
+    JLabel jl = new JLabel(message);
+
+    JTextArea jta = new JTextArea(height, width);
+    // jta.setLineWrap(true);
+    jta.setEditable(false);
+    jta.setWrapStyleWord(true);
+    jta.setAutoscrolls(true);
+    jta.setText(boxtext);
+
+    JScrollPane jsp = scrollable
+            ? new JScrollPane(jta, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)
+            : null;
+
+    JvOptionPane jvp = JvOptionPane.newOptionDialog(this);
+
+    JPanel jp = new JPanel();
+    jp.setLayout(new BoxLayout(jp, BoxLayout.Y_AXIS));
+
+    if (message != null)
+    {
+      jl.setAlignmentX(Component.LEFT_ALIGNMENT);
+      jp.add(jl);
+    }
+    if (boxtext != null)
+    {
+      if (scrollable)
+      {
+        jsp.setAlignmentX(Component.LEFT_ALIGNMENT);
+        jp.add(jsp);
+      }
+      else
+      {
+        jta.setAlignmentX(Component.LEFT_ALIGNMENT);
+        jp.add(jta);
+      }
+    }
+
+    jvp.setResponseHandler(JOptionPane.YES_OPTION, () -> {
+    });
+    jvp.showDialogOnTopAsync(this, jp, title, JOptionPane.YES_OPTION, type,
+            null, new Object[]
+            { button }, button, modal, null, false);
+  }
+
 }