refactored embedded menu machinery
authorjprocter <Jim Procter>
Sat, 27 Oct 2007 13:26:07 +0000 (13:26 +0000)
committerjprocter <Jim Procter>
Sat, 27 Oct 2007 13:26:07 +0000 (13:26 +0000)
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/EmbmenuFrame.java [new file with mode: 0644]

index 7e706d5..22537f6 100755 (executable)
@@ -33,8 +33,8 @@ import jalview.io.*;
 import jalview.schemes.*;
 
 public class AlignFrame
-    extends Frame implements ActionListener,
-    ItemListener, KeyListener, MouseListener
+    extends EmbmenuFrame implements ActionListener,
+    ItemListener, KeyListener
 {
   public AlignmentPanel alignPanel;
   public AlignViewport viewport;
@@ -2465,16 +2465,6 @@ public class AlignFrame
   MenuItem documentation = new MenuItem();
   MenuItem about = new MenuItem();
    CheckboxMenuItem seqLimits = new CheckboxMenuItem();
-  Panel embeddedMenu;
-  Label embeddedEdit;
-  Label embeddedSelect;
-  Label embeddedView;
-  Label embeddedFormat;
-  Label embeddedColour;
-  Label embeddedFile;
-  Label embeddedHelp;
-  Label embeddedCalculate;
-  FlowLayout flowLayout1;
 
   private void jbInit()
       throws Exception
@@ -2789,7 +2779,22 @@ public class AlignFrame
     selectMenu.add(invertColSel);
     selectMenu.add(deleteGroups);
   }
+  MenuItem featureSettings = new MenuItem();
+  CheckboxMenuItem sequenceFeatures = new CheckboxMenuItem();
+  MenuItem annotationColour = new MenuItem();
+  MenuItem invertColSel = new MenuItem();
+  Menu menu1 = new Menu();
+  MenuItem showColumns = new MenuItem();
+  MenuItem showSeqs = new MenuItem();
+  Menu menu2 = new Menu();
+  MenuItem hideColumns = new MenuItem();
+  MenuItem hideSequences = new MenuItem();
+  Menu formatMenu = new Menu();
+  Menu selectMenu = new Menu();
+  MenuItem newView = new MenuItem();
+
   /**
+   * Attach the alignFrame panels after embedding menus, if necessary.
    * This used to be called setEmbedded, but is now creates the 
    * dropdown menus in a platform independent manner
    * to avoid OSX/Mac menu appendage daftness.
@@ -2798,47 +2803,12 @@ public class AlignFrame
    */
   public void createAlignFrameWindow(boolean reallyEmbedded, String title)
   {
-    embeddedMenu = new Panel();
-    embeddedEdit = new Label("Edit");
-    embeddedSelect = new Label("Select");
-    embeddedView = new Label("View");
-    embeddedFormat = new Label("Format");
-    embeddedColour = new Label("Colour");
-    embeddedFile = new Label("File");
-    embeddedHelp = new Label("Help");
-    embeddedCalculate = new Label("Calculate");
-    flowLayout1 = new FlowLayout();
-    embeddedMenu.setBackground(Color.lightGray);
-    embeddedMenu.setLayout(flowLayout1);
-    embeddedEdit.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedSelect.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedView.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedFormat.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedColour.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedFile.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedHelp.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedCalculate.setFont(new java.awt.Font("Arial", Font.PLAIN, 10));
-    embeddedMenu.add(embeddedFile);
-    embeddedMenu.add(embeddedEdit);
-    embeddedMenu.add(embeddedSelect);
-    embeddedMenu.add(embeddedView);
-    embeddedMenu.add(embeddedFormat);
-    embeddedMenu.add(embeddedColour);
-    embeddedMenu.add(embeddedCalculate);
-    embeddedMenu.add(embeddedHelp);
-    flowLayout1.setAlignment(FlowLayout.LEFT);
-    flowLayout1.setHgap(2);
-    flowLayout1.setVgap(0);
-    embeddedFile.addMouseListener(this);
-    embeddedEdit.addMouseListener(this);
-    embeddedSelect.addMouseListener(this);
-    embeddedView.addMouseListener(this);
-    embeddedFormat.addMouseListener(this);
-    embeddedColour.addMouseListener(this);
-    embeddedCalculate.addMouseListener(this);
-    embeddedHelp.addMouseListener(this);
     if (reallyEmbedded)
     {
+      //////
+      // Explicly build the embedded menu panel for the on-page applet
+      //
+      embeddedMenu = makeEmbeddedPopupMenu(alignFrameMenuBar, "Arial", Font.PLAIN, 10, false); // use our own fonts.
       // view cannot be closed if its actually on the page
       fileMenu.remove(closeMenuItem);
       fileMenu.remove(3); // Remove Seperator
@@ -2852,17 +2822,14 @@ public class AlignFrame
       viewport.applet.add(alignPanel, BorderLayout.CENTER);
       viewport.applet.validate();
     } else {
-      if (new jalview.util.Platform().isAMac())
-      {
-        // TODO: try to modify the embeddedMenu display so it looks like a real toolbar menu
-        // TODO: modify click/mouse handler for embeddedMenu so it behaves more like a real pulldown menu toolbar
-        setMenuBar(null);// alignFramMenuBar);
-        // add the components to the AlignFrame area.
-        add(embeddedMenu, BorderLayout.NORTH);
+      ////////
+      // test and embed menu bar if necessary.
+      //
+      if (embedMenuIfNeeded(alignPanel)) {
+        // adjust for status bar height too
         alignPanel.setSize(getSize().width,
               getSize().height
-                - embeddedMenu.HEIGHT - statusBar.HEIGHT);
-        
+                - statusBar.HEIGHT);
       }
       add(statusBar, BorderLayout.SOUTH);
       add(alignPanel, BorderLayout.CENTER);
@@ -2871,94 +2838,4 @@ public class AlignFrame
                                          DEFAULT_HEIGHT);
     }
   }
-
-  PopupMenu filePopup, editPopup, searchPopup,
-  viewPopup, formatPopup, colourPopup, calculatePopup, helpPopup;
-  MenuItem featureSettings = new MenuItem();
-  CheckboxMenuItem sequenceFeatures = new CheckboxMenuItem();
-  MenuItem annotationColour = new MenuItem();
-  MenuItem invertColSel = new MenuItem();
-  Menu menu1 = new Menu();
-  MenuItem showColumns = new MenuItem();
-  MenuItem showSeqs = new MenuItem();
-  Menu menu2 = new Menu();
-  MenuItem hideColumns = new MenuItem();
-  MenuItem hideSequences = new MenuItem();
-  Menu formatMenu = new Menu();
-  Menu selectMenu = new Menu();
-  MenuItem newView = new MenuItem();
-
-  public void mousePressed(MouseEvent evt)
-  {
-    PopupMenu popup = null;
-    Label source = (Label) evt.getSource();
-    if (source == embeddedFile)
-    {
-      popup = filePopup = genPopupMenu(filePopup, fileMenu);
-    }
-    else if (source == embeddedEdit)
-    {
-      popup = editPopup = genPopupMenu(editPopup, editMenu);
-    }
-    else if (source == embeddedSelect)
-    {
-      popup = searchPopup = genPopupMenu(searchPopup, selectMenu);
-    }
-    else if (source == embeddedView)
-    {
-      popup = viewPopup = genPopupMenu(viewPopup, viewMenu);
-    }
-    else if (source == embeddedFormat)
-    {
-      popup = formatPopup = genPopupMenu(formatPopup, formatMenu);
-    }
-    else if (source == embeddedColour)
-    {
-      popup = colourPopup = genPopupMenu(colourPopup, colourMenu);
-    }
-    else if (source == embeddedCalculate)
-    {
-      popup = calculatePopup = genPopupMenu(calculatePopup, calculateMenu);
-    }
-    else if (source == embeddedHelp)
-    {
-      popup = helpPopup = genPopupMenu(helpPopup, helpMenu);
-    }
-
-    embeddedMenu.add(popup);
-    popup.show(embeddedMenu,
-               source.getBounds().x,
-               source.getBounds().y + source.getBounds().getSize().height);
-  }
-
-  PopupMenu genPopupMenu(PopupMenu popup, Menu original)
-  {
-    if (popup != null)
-    {
-      return popup;
-    }
-    popup = new PopupMenu();
-    int m, mSize = original.getItemCount();
-    for (m = 0; m < mSize; m++)
-    {
-      popup.add(original.getItem(m));
-      mSize--;
-      m--;
-    }
-
-    return popup;
-  }
-
-  public void mouseClicked(MouseEvent evt)
-  {}
-
-  public void mouseReleased(MouseEvent evt)
-  {}
-
-  public void mouseEntered(MouseEvent evt)
-  {}
-
-  public void mouseExited(MouseEvent evt)
-  {}
-
 }
diff --git a/src/jalview/appletgui/EmbmenuFrame.java b/src/jalview/appletgui/EmbmenuFrame.java
new file mode 100644 (file)
index 0000000..23f6aa2
--- /dev/null
@@ -0,0 +1,261 @@
+package jalview.appletgui;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Container;\r
+import java.awt.FlowLayout;\r
+import java.awt.Font;\r
+import java.awt.Frame;\r
+import java.awt.GraphicsConfiguration;\r
+import java.awt.HeadlessException;\r
+import java.awt.Label;\r
+import java.awt.Menu;\r
+import java.awt.MenuBar;\r
+import java.awt.Panel;\r
+import java.awt.PopupMenu;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemListener;\r
+import java.awt.event.MouseEvent;\r
+import java.awt.event.MouseListener;\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+/**\r
+ * This class implements a pattern form embedding toolbars as a panel with popups\r
+ * for situations where the system menu bar is either invisible or inappropriate.\r
+ * It was derived from the code for embedding the jalview applet alignFrame as\r
+ * a component on the web-page, which requires the local alignFrame menu to be\r
+ * attached to that panel rather than placed on the parent (which isn't allowed\r
+ * anyhow).\r
+ * TODO: try to modify the embeddedMenu display so it looks like a real toolbar menu\r
+ * TODO: modify click/mouse handler for embeddedMenu so it behaves more like a real pulldown menu toolbar\r
+ * \r
+ * @author Jim Procter and Andrew Waterhouse\r
+ *\r
+ */\r
+public class EmbmenuFrame extends Frame implements MouseListener\r
+{\r
+  /**\r
+   * map from labels to popup menus for the embedded menubar\r
+   */\r
+  protected Hashtable embeddedPopup;\r
+  /**\r
+   * the embedded menu is built on this and should be \r
+   * added to the frame at the appropriate position.\r
+   * \r
+   */\r
+  protected Panel embeddedMenu;\r
+\r
+  public EmbmenuFrame() throws HeadlessException\r
+  {\r
+    super();\r
+  }\r
+  public EmbmenuFrame(String title) throws HeadlessException\r
+  {\r
+    super(title);\r
+  }\r
+  /**\r
+   * Check if the applet is running on a platform that requires the Frame menuBar to be embedded,\r
+   * and if so, embeds it.\r
+   * @param tobeAdjusted the panel that is to be reduced to make space for the embedded menu bar\r
+   * @return true if menuBar was embedded and tobeAdjusted's height modified\r
+   */\r
+  protected boolean embedMenuIfNeeded(Panel tobeAdjusted)\r
+  {\r
+    MenuBar topMenuBar = getMenuBar();\r
+    if (topMenuBar==null)\r
+    {\r
+      return false;\r
+    }\r
+    // DEBUG Hint: can test embedded menus by inserting true here.\r
+    if (new jalview.util.Platform().isAMac())\r
+    {\r
+      // Build the embedded menu panel\r
+      embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, "Arial", Font.PLAIN, 10, true); // try to pickup system font.\r
+      setMenuBar(null);\r
+      // add the components to the TreePanel area.\r
+      add(embeddedMenu, BorderLayout.NORTH);\r
+      tobeAdjusted.setSize(getSize().width,\r
+              getSize().height\r
+              - embeddedMenu.HEIGHT);        \r
+      return true;\r
+    }\r
+    return false;\r
+  }\r
+  /**\r
+   * move all menus on menuBar onto embeddedMenu.\r
+   * embeddedPopup is used to store the popups for each menu removed from the menuBar and added\r
+   * to the panel.\r
+   * NOTE: it is up to the caller to remove menuBar from the Frame if it is already attached.\r
+   * @param menuBar\r
+   * @param fn\r
+   * @param fstyle\r
+   * @param fsz\r
+   * @param overrideFonts true if we take the menuBar fonts in preference to the supplied defaults\r
+   * @return the embedded menu instance to be added to the frame.\r
+   */\r
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
+          int fstyle, int fsz, boolean overrideFonts)\r
+  {\r
+    return makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz, overrideFonts, false);\r
+  }\r
+  /**\r
+   * Create or add elements to the embedded menu from menuBar. This removes all menu from\r
+   * menuBar and it is up to the caller to remove the now useless menuBar from the Frame if it is already attached. \r
+   * @param menuBar\r
+   * @param fn\r
+   * @param fstyle\r
+   * @param fsz\r
+   * @param overrideFonts \r
+   * @param append true means existing menu will be emptied before adding new elements\r
+   * @return\r
+   */\r
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
+          int fstyle, int fsz, boolean overrideFonts, boolean append)\r
+  {\r
+    if (!append)\r
+    {\r
+      if (embeddedPopup != null)\r
+      {\r
+        embeddedPopup.clear(); // TODO: check if j1.1\r
+      } \r
+      if (embeddedMenu!=null)\r
+      {\r
+        embeddedMenu.removeAll(); \r
+      }\r
+    }\r
+    if (embeddedPopup==null)\r
+    {\r
+      embeddedPopup = new Hashtable();\r
+    }\r
+    \r
+    embeddedMenu = makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz, overrideFonts, embeddedPopup, new Panel(), this);\r
+    return embeddedMenu;\r
+  }\r
+  /**\r
+   * Generic method to move elements from menubar onto embeddedMenu using the existing or the supplied font, \r
+   * and adds binding from panel to attached menus in embeddedPopup\r
+   * This removes all menu from\r
+   * menuBar and it is up to the caller to remove the now useless menuBar from the Frame if it is already attached.\r
+   * @param menuBar must be non-null\r
+   * @param fn\r
+   * @param fstyle\r
+   * @param fsz\r
+   * @param overrideFonts\r
+   * @param embeddedPopup must be non-null\r
+   * @param embeddedMenu if null, a new panel will be created and returned\r
+   * @param clickHandler - usually the instance of EmbmenuFrame that holds references to embeddedPopup and embeddedMenu\r
+   * @return the panel instance for convenience.\r
+   */\r
+  protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
+          int fstyle, int fsz, boolean overrideFonts, Hashtable embeddedPopup, Panel embeddedMenu, MouseListener clickHandler)\r
+  {\r
+    if (embeddedPopup==null)\r
+    {\r
+      throw new Error("Implementation error - embeddedPopup must be non-null");\r
+    }\r
+    if (overrideFonts)\r
+    {\r
+      Font mbf = menuBar.getFont();\r
+      if (mbf!=null)\r
+      {\r
+        fn = mbf.getName();\r
+        fstyle = mbf.getStyle();\r
+        fsz = mbf.getSize();\r
+      }\r
+    }\r
+    if (embeddedMenu==null)\r
+      embeddedMenu = new Panel();\r
+    FlowLayout flowLayout1 = new FlowLayout();\r
+    embeddedMenu.setBackground(Color.lightGray);\r
+    embeddedMenu.setLayout(flowLayout1);\r
+    // loop thru\r
+    for (int mbi = 0,nMbi=menuBar.getMenuCount(); mbi<nMbi; mbi++)\r
+    {\r
+      Menu mi = menuBar.getMenu(mbi);\r
+      Label elab = new Label(mi.getLabel());\r
+      elab.setFont(new java.awt.Font(fn, fstyle, fsz));\r
+      // add the menu entries\r
+      PopupMenu popup = new PopupMenu();\r
+      int m, mSize = mi.getItemCount();\r
+      for (m = 0; m < mSize; m++)\r
+      {\r
+        popup.add(mi.getItem(m));\r
+        mSize--;\r
+        m--;\r
+      }\r
+      embeddedPopup.put(elab, popup);\r
+      embeddedMenu.add(elab);\r
+      elab.addMouseListener(clickHandler);\r
+    }\r
+    flowLayout1.setAlignment(FlowLayout.LEFT);\r
+    flowLayout1.setHgap(2);\r
+    flowLayout1.setVgap(0);\r
+    return embeddedMenu;\r
+  }\r
+\r
+  public void mousePressed(MouseEvent evt)\r
+  {\r
+    PopupMenu popup = null;\r
+    Label source = (Label) evt.getSource();\r
+    popup = getPopupMenu(source);\r
+    if (popup!=null)\r
+    {\r
+      embeddedMenu.add(popup);\r
+      popup.show(embeddedMenu,\r
+               source.getBounds().x,\r
+               source.getBounds().y + source.getBounds().getSize().height);\r
+    }\r
+  }\r
+\r
+  /**\r
+   * get the menu for source from the hash.\r
+   * @param source what was clicked on.\r
+   */\r
+  PopupMenu getPopupMenu(Label source)\r
+  {\r
+    return (PopupMenu) embeddedPopup.get(source);\r
+  }\r
+\r
+  public void mouseClicked(MouseEvent evt)\r
+  {}\r
+\r
+  public void mouseReleased(MouseEvent evt)\r
+  {}\r
+\r
+  public void mouseEntered(MouseEvent evt)\r
+  {}\r
+\r
+  public void mouseExited(MouseEvent evt)\r
+  {}\r
+\r
+  /**\r
+   * called to clear the GUI resources taken up for embedding\r
+   * and remove any self references so we can be garbage collected.\r
+   */\r
+  public void destroyMenus()\r
+  {\r
+    if (embeddedPopup!=null)\r
+    {\r
+      Enumeration e = embeddedPopup.keys();\r
+      while (e.hasMoreElements())\r
+      {\r
+        Label lb = (Label) e.nextElement();\r
+        lb.removeMouseListener(this);\r
+      }\r
+      embeddedPopup.clear();\r
+    }\r
+    if (embeddedMenu!=null)\r
+    {\r
+      embeddedMenu.removeAll();\r
+    }\r
+  }\r
+  /**\r
+   * calls destroyMenus()\r
+   */\r
+  public void finalize() throws Throwable {\r
+    destroyMenus();\r
+    embeddedPopup=null;\r
+    embeddedMenu=null;\r
+    super.finalize();\r
+  }\r
+}
\ No newline at end of file