From b354daab68fda14d80114743ef686cf487396169 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 29 Sep 2016 03:00:29 +0200 Subject: [PATCH] WIP swing submenus --- src/dorkbox/systemTray/swing/SwingMenu.java | 163 +++++++++++++++++- .../systemTray/swing/SwingMenuEntry.java | 12 +- .../systemTray/swing/SwingMenuEntryItem.java | 5 +- .../swing/SwingMenuEntrySpacer.java | 4 +- .../swing/SwingMenuEntryStatus.java | 4 +- .../systemTray/swing/SwingSystemTray.java | 13 +- .../swing/SwingSystemTrayMenuPopup.java | 11 +- 7 files changed, 182 insertions(+), 30 deletions(-) diff --git a/src/dorkbox/systemTray/swing/SwingMenu.java b/src/dorkbox/systemTray/swing/SwingMenu.java index c28e8c2..3b38951 100644 --- a/src/dorkbox/systemTray/swing/SwingMenu.java +++ b/src/dorkbox/systemTray/swing/SwingMenu.java @@ -17,17 +17,30 @@ package dorkbox.systemTray.swing; import java.io.File; +import java.io.InputStream; +import java.net.URL; + +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; import dorkbox.systemTray.Menu; import dorkbox.systemTray.MenuEntry; import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.SystemTrayMenuAction; +import dorkbox.systemTray.util.ImageUtils; import dorkbox.util.SwingUtil; +// this is a weird composite class, because it must be a Menu, but ALSO a MenuEntry -- so it has both public -class SwingMenu extends Menu { +class SwingMenu extends Menu implements MenuEntry { - volatile SwingSystemTrayMenuPopup _native; + volatile JComponent _native; + + // this have to be volatile, because they can be changed from any thread + private volatile String text; + private volatile boolean hasLegitIcon = false; /** * @param systemTray @@ -43,7 +56,11 @@ class SwingMenu extends Menu { @Override public void run() { - _native = new SwingSystemTrayMenuPopup(); + _native = new JMenu(); + + if (parent != null) { + ((SwingMenu) parent)._native.add(_native); + } } }); } @@ -92,9 +109,16 @@ class SwingMenu extends Menu { } else { // must always be called on the EDT - menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback); - menuEntry.setText(menuText); - menuEntry.setImage(imagePath); + if (!menuText.equals("AAAAAAAA")) { + menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback); + menuEntry.setText(menuText); + menuEntry.setImage(imagePath); + } else { + menuEntry = new SwingMenu(getSystemTray(), SwingMenu.this); + menuEntry.setText(menuText); + menuEntry.setImage(imagePath); + ((SwingMenu) menuEntry).addMenuEntry("asdasdasd", null, null, null); + } menuEntries.add(menuEntry); } @@ -102,4 +126,131 @@ class SwingMenu extends Menu { } }); } + + + + + + + // always called in the EDT + void renderText(final String text) { + ((JMenuItem) _native).setText(text); + } + + @SuppressWarnings("Duplicates") + void setImage_(final File imageFile) { + hasLegitIcon = imageFile != null; + + SwingUtil.invokeLater(new Runnable() { + @Override + public + void run() { + if (imageFile != null) { + ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath()); + ((JMenuItem) _native).setIcon(origIcon); + } + else { + ((JMenuItem) _native).setIcon(null); + } + } + }); + } + + + @Override + public + String getText() { + return text; + } + + @Override + public + void setText(final String newText) { + text = newText; + SwingUtil.invokeLater(new Runnable() { + @Override + public + void run() { + renderText(newText); + } + }); + } + + @Override + public + void setImage(final File imageFile) { + if (imageFile == null) { + setImage_(null); + } + else { + setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile)); + } + } + + @Override + public final + void setImage(final String imagePath) { + if (imagePath == null) { + setImage_(null); + } + else { + setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath)); + } + } + + @Override + public final + void setImage(final URL imageUrl) { + if (imageUrl == null) { + setImage_(null); + } + else { + setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl)); + } + } + + @Override + public final + void setImage(final String cacheName, final InputStream imageStream) { + if (imageStream == null) { + setImage_(null); + } + else { + setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream)); + } + } + + @Override + public final + void setImage(final InputStream imageStream) { + if (imageStream == null) { + setImage_(null); + } + else { + setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream)); + } + } + + @Override + public + boolean hasImage() { + return hasLegitIcon; + } + + @Override + public + void setCallback(final SystemTrayMenuAction callback) { + } + + @Override + public final + void remove() { + SwingUtil.invokeAndWait(new Runnable() { + @Override + public + void run() { + ((SwingMenu) getParent())._native.remove(_native); + } + }); + } } diff --git a/src/dorkbox/systemTray/swing/SwingMenuEntry.java b/src/dorkbox/systemTray/swing/SwingMenuEntry.java index 0dd10d3..2238079 100644 --- a/src/dorkbox/systemTray/swing/SwingMenuEntry.java +++ b/src/dorkbox/systemTray/swing/SwingMenuEntry.java @@ -30,24 +30,24 @@ abstract class SwingMenuEntry implements MenuEntry { private final int id = Menu.MENU_ID_COUNTER.getAndIncrement(); - private final SwingMenu parentMenu; + private final SwingMenu parent; final JComponent _native; // this have to be volatile, because they can be changed from any thread private volatile String text; // this is ALWAYS called on the EDT. - SwingMenuEntry(final SwingMenu parentMenu, final JComponent menuItem) { - this.parentMenu = parentMenu; + SwingMenuEntry(final SwingMenu parent, final JComponent menuItem) { + this.parent = parent; this._native = menuItem; - parentMenu._native.add(menuItem); + parent._native.add(menuItem); } @Override public Menu getParent() { - return parentMenu; + return parent; } /** @@ -143,7 +143,7 @@ class SwingMenuEntry implements MenuEntry { public void run() { removePrivate(); - parentMenu._native.remove(_native); + parent._native.remove(_native); } }); } diff --git a/src/dorkbox/systemTray/swing/SwingMenuEntryItem.java b/src/dorkbox/systemTray/swing/SwingMenuEntryItem.java index 30a9111..b95501a 100644 --- a/src/dorkbox/systemTray/swing/SwingMenuEntryItem.java +++ b/src/dorkbox/systemTray/swing/SwingMenuEntryItem.java @@ -33,8 +33,8 @@ class SwingMenuEntryItem extends SwingMenuEntry { private volatile SystemTrayMenuAction callback; // this is ALWAYS called on the EDT. - SwingMenuEntryItem(final SwingMenu parentMenu, final SystemTrayMenuAction callback) { - super(parentMenu, new AdjustedJMenuItem()); + SwingMenuEntryItem(final SwingMenu parent, final SystemTrayMenuAction callback) { + super(parent, new AdjustedJMenuItem()); this.callback = callback; @@ -86,6 +86,7 @@ class SwingMenuEntryItem extends SwingMenuEntry { ((JMenuItem) _native).setText(text); } + @SuppressWarnings("Duplicates") @Override void setImage_(final File imageFile) { hasLegitIcon = imageFile != null; diff --git a/src/dorkbox/systemTray/swing/SwingMenuEntrySpacer.java b/src/dorkbox/systemTray/swing/SwingMenuEntrySpacer.java index 59d9acc..176de3a 100644 --- a/src/dorkbox/systemTray/swing/SwingMenuEntrySpacer.java +++ b/src/dorkbox/systemTray/swing/SwingMenuEntrySpacer.java @@ -25,8 +25,8 @@ import dorkbox.systemTray.SystemTrayMenuAction; class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer { // this is ALWAYS called on the EDT. - SwingMenuEntrySpacer(final SwingMenu parentMenu) { - super(parentMenu, new JSeparator(JSeparator.HORIZONTAL)); + SwingMenuEntrySpacer(final SwingMenu parent) { + super(parent, new JSeparator(JSeparator.HORIZONTAL)); } // called in the EDT thread diff --git a/src/dorkbox/systemTray/swing/SwingMenuEntryStatus.java b/src/dorkbox/systemTray/swing/SwingMenuEntryStatus.java index e675616..7442e85 100644 --- a/src/dorkbox/systemTray/swing/SwingMenuEntryStatus.java +++ b/src/dorkbox/systemTray/swing/SwingMenuEntryStatus.java @@ -26,8 +26,8 @@ import dorkbox.systemTray.SystemTrayMenuAction; class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus { // this is ALWAYS called on the EDT. - SwingMenuEntryStatus(final SwingMenu parentMenu, final String label) { - super(parentMenu, new JMenuItem()); + SwingMenuEntryStatus(final SwingMenu parent, final String label) { + super(parent, new JMenuItem()); setText(label); } diff --git a/src/dorkbox/systemTray/swing/SwingSystemTray.java b/src/dorkbox/systemTray/swing/SwingSystemTray.java index b4b7bee..a712f55 100644 --- a/src/dorkbox/systemTray/swing/SwingSystemTray.java +++ b/src/dorkbox/systemTray/swing/SwingSystemTray.java @@ -47,8 +47,6 @@ class SwingSystemTray extends SwingMenu { volatile SystemTray tray; volatile TrayIcon trayIcon; - volatile boolean isActive = false; - /** * Creates a new system tray handler class. */ @@ -56,6 +54,9 @@ class SwingSystemTray extends SwingMenu { SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) { super(systemTray, null); + // have to reassign our type... + _native = new SwingSystemTrayMenuPopup(); + ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING); SwingUtil.invokeAndWait(new Runnable() { @@ -143,10 +144,8 @@ class SwingSystemTray extends SwingMenu { final Image trayImage = new ImageIcon(iconFile.getAbsolutePath()).getImage(); trayImage.flush(); - if (!isActive) { + if (trayIcon == null) { // here we init. everything - isActive = true; - trayIcon = new TrayIcon(trayImage); // appindicators DO NOT support anything other than PLAIN gtk-menus @@ -185,7 +184,7 @@ class SwingSystemTray extends SwingMenu { // voodoo to get this to popup to have the correct parent // from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881 - _native.setInvoker(_native); + ((SwingSystemTrayMenuPopup) _native).setInvoker(_native); _native.setLocation(x, y); _native.setVisible(true); _native.setFocusable(true); @@ -194,7 +193,7 @@ class SwingSystemTray extends SwingMenu { }); try { - SwingSystemTray.this.tray.add(trayIcon); + tray.add(trayIcon); } catch (AWTException e) { dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e); } diff --git a/src/dorkbox/systemTray/swing/SwingSystemTrayMenuPopup.java b/src/dorkbox/systemTray/swing/SwingSystemTrayMenuPopup.java index 96602c6..e35a9dc 100644 --- a/src/dorkbox/systemTray/swing/SwingSystemTrayMenuPopup.java +++ b/src/dorkbox/systemTray/swing/SwingSystemTrayMenuPopup.java @@ -42,7 +42,7 @@ class SwingSystemTrayMenuPopup extends JPopupMenu { private DelayTimer timer; - protected volatile Point previousLocation = null; + protected volatile Point mouseClickLocation = null; // protected boolean mouseStillOnMenu; // private JDialog hiddenDialog; @@ -77,9 +77,9 @@ class SwingSystemTrayMenuPopup extends JPopupMenu { } // has the mouse pointer moved > delta pixels from it's original location (when the tray icon was clicked)? - else if (previousLocation != null && - location.x >= previousLocation.x - MOVEMENT_DELTA && location.x < previousLocation.x + MOVEMENT_DELTA && - location.y >= previousLocation.y - MOVEMENT_DELTA && location.y < previousLocation.y + MOVEMENT_DELTA) { + else if (mouseClickLocation != null && + location.x >= mouseClickLocation.x - MOVEMENT_DELTA && location.x < mouseClickLocation.x + MOVEMENT_DELTA && + location.y >= mouseClickLocation.y - MOVEMENT_DELTA && location.y < mouseClickLocation.y + MOVEMENT_DELTA) { // restart the timer SwingSystemTrayMenuPopup.this.timer.delay(POPUP_HIDE_DELAY); @@ -125,10 +125,11 @@ class SwingSystemTrayMenuPopup extends JPopupMenu { @Override public void setVisible(boolean makeVisible) { + // only allow java to close this popup if our timer closed it this.timer.cancel(); if (makeVisible) { - previousLocation = MouseInfo.getPointerInfo().getLocation(); + mouseClickLocation = MouseInfo.getPointerInfo().getLocation(); // if the mouse isn't inside the popup in x seconds, close the popup this.timer.delay(POPUP_HIDE_DELAY);