diff --git a/src/dorkbox/systemTray/Menu.java b/src/dorkbox/systemTray/Menu.java new file mode 100644 index 0000000..115d025 --- /dev/null +++ b/src/dorkbox/systemTray/Menu.java @@ -0,0 +1,352 @@ +/* + * Copyright 2014 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.systemTray; + +import static dorkbox.systemTray.SystemTray.TIMEOUT; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import dorkbox.systemTray.util.ImageUtils; + +/** + * Represents a cross-platform menu that is displayed by the tray-icon or as a sub-menu + */ +@SuppressWarnings({"WeakerAccess", "unused"}) +public +class Menu { + protected final java.util.List menuEntries = new ArrayList(); + + /** + * Adds a spacer to the dropdown menu. When menu entries are removed, any menu spacer that ends up at the top/bottom of the drop-down + * menu, will also be removed. For example: + * + * Original Entry3 deleted Result + * + * + * Entry1 Entry1 Entry1 + * Entry2 -> Entry2 -> Entry2 + * (deleted) + * Entry3 (deleted) + */ + public + void addMenuSpacer() { + } + + /* + * Will add a new menu entry, or update one if it already exists + */ + protected + void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) { + } + + /* + * Necessary to guarantee all updates occur on the dispatch thread + */ + protected + void dispatch(Runnable runnable) { + } + + /** + * Gets the menu entry for a specified text + * + * @param menuText the menu entry text to use to find the menu entry. The first result found is returned + */ + public final + MenuEntry getMenuEntry(final String menuText) { + if (menuText == null || menuText.isEmpty()) { + return null; + } + + // Must be wrapped in a synchronized block for object visibility + synchronized (menuEntries) { + for (MenuEntry entry : menuEntries) { + String text = entry.getText(); + + // text can be null + if (menuText.equals(text)) { + return entry; + } + } + } + + return null; + } + + /** + * Gets the first menu entry, ignoring status and spacers + */ + public final + MenuEntry getFirstMenuEntry() { + return getMenuEntry(0); + } + + /** + * Gets the last menu entry, ignoring status and spacers + */ + public final + MenuEntry getLastMenuEntry() { + // Must be wrapped in a synchronized block for object visibility + synchronized (menuEntries) { + if (!menuEntries.isEmpty()) { + MenuEntry menuEntry = null; + for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) { + menuEntry = menuEntries.get(i); + } + + if (!(menuEntry instanceof MenuSpacer || menuEntry instanceof MenuStatus)) { + return menuEntry; + } + } + } + + return null; + } + + /** + * Gets the menu entry for a specified index (zero-index), ignoring status and spacers + * + * @param menuIndex the menu entry index to use to retrieve the menu entry. + */ + public final + MenuEntry getMenuEntry(final int menuIndex) { + if (menuIndex < 0) { + return null; + } + + // Must be wrapped in a synchronized block for object visibility + synchronized (menuEntries) { + if (!menuEntries.isEmpty()) { + int count = 0; + for (MenuEntry menuEntry : menuEntries) { + if (menuEntry instanceof MenuSpacer || menuEntry instanceof MenuStatus) { + continue; + } + + if (count == menuIndex) { + return menuEntry; + } + + count++; + } + } + } + + return null; + } + + + + /** + * Adds a menu entry to the tray icon with text (no image) + * + * @param menuText string of the text you want to appear + * @param callback callback that will be executed when this menu entry is clicked + */ + public final + void addMenuEntry(String menuText, SystemTrayMenuAction callback) { + addMenuEntry(menuText, (String) null, callback); + } + + /** + * Adds a menu entry to the tray icon with text + image + * + * @param menuText string of the text you want to appear + * @param imagePath the image (full path required) to use. If null, no image will be used + * @param callback callback that will be executed when this menu entry is clicked + */ + public final + void addMenuEntry(String menuText, String imagePath, SystemTrayMenuAction callback) { + if (imagePath == null) { + addMenuEntry_(menuText, null, callback); + } + else { + addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath), callback); + } + } + + /** + * Adds a menu entry to the tray icon with text + image + * + * @param menuText string of the text you want to appear + * @param imageUrl the URL of the image to use. If null, no image will be used + * @param callback callback that will be executed when this menu entry is clicked + */ + public final + void addMenuEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) { + if (imageUrl == null) { + addMenuEntry_(menuText, null, callback); + } + else { + addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl), callback); + } + } + + /** + * Adds a menu entry to the tray icon with text + image + * + * @param menuText string of the text you want to appear + * @param cacheName @param cacheName the name to use for lookup in the cache for the imageStream + * @param imageStream the InputStream of the image to use. If null, no image will be used + * @param callback callback that will be executed when this menu entry is clicked + */ + public final + void addMenuEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) { + if (imageStream == null) { + addMenuEntry_(menuText, null, callback); + } + else { + addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream), callback); + } + } + + /** + * Adds a menu entry to the tray icon with text + image + * + * @param menuText string of the text you want to appear + * @param imageStream the InputStream of the image to use. If null, no image will be used + * @param callback callback that will be executed when this menu entry is clicked + */ + public final + void addMenuEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) { + if (imageStream == null) { + addMenuEntry_(menuText, null, callback); + } + else { + addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream), callback); + } + } + + /** + * This removes a menu entry from the dropdown menu. + * + * @param menuEntry This is the menu entry to remove + */ + public final + void removeMenuEntry(final MenuEntry menuEntry) { + if (menuEntry == null) { + throw new NullPointerException("No menu entry exists for menuEntry"); + } + + // have to wait for the value + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AtomicBoolean hasValue = new AtomicBoolean(false); + + dispatch(new Runnable() { + @Override + public + void run() { + try { + synchronized (menuEntries) { + for (Iterator iterator = menuEntries.iterator(); iterator.hasNext(); ) { + final MenuEntry entry = iterator.next(); + if (entry == menuEntry) { + iterator.remove(); + + // this will also reset the menu + menuEntry.remove(); + hasValue.set(true); + break; + } + } + + // now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function. + if (!menuEntries.isEmpty()) { + if (menuEntries.get(0) instanceof MenuSpacer) { + removeMenuEntry(menuEntries.get(0)); + } + } + // now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function. + if (!menuEntries.isEmpty()) { + if (menuEntries.get(menuEntries.size()-1) instanceof MenuSpacer) { + removeMenuEntry(menuEntries.get(menuEntries.size()-1)); + } + } + } + } catch (Exception e) { + SystemTray.logger.error("Error removing menu entry from list.", e); + } finally { + countDownLatch.countDown(); + } + } + }); + + try { + if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { + throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + + "`SystemTray.TIMEOUT` to a value which better suites your environment."); + + } + } catch (InterruptedException e) { + SystemTray.logger.error("Error removing menu entry: {}", menuEntry.getText()); + } + + if (!hasValue.get()) { + throw new NullPointerException("Menu entry '" + menuEntry.getText() + "'not found in list while trying to remove it."); + } + } + + + /** + * This removes a menu entry (via the text label) from the dropdown menu. + * + * @param menuText This is the label for the menu entry to remove + */ + public final + void removeMenuEntry(final String menuText) { + // have to wait for the value + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AtomicBoolean hasValue = new AtomicBoolean(true); + + dispatch(new Runnable() { + @Override + public + void run() { + synchronized (menuEntries) { + MenuEntry menuEntry = getMenuEntry(menuText); + + if (menuEntry == null) { + hasValue.set(false); + } + else { + removeMenuEntry(menuEntry); + } + } + countDownLatch.countDown(); + } + }); + + try { + if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { + throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + + "`SystemTray.TIMEOUT` to a value which better suites your environment."); + + } + } catch (InterruptedException e) { + SystemTray.logger.error("Error removing menu entry: {}", menuText); + } + + if (!hasValue.get()) { + throw new NullPointerException("No menu entry exists for string '" + menuText + "'"); + } + } +} diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index de8787e..a71d61f 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -24,11 +24,6 @@ import java.io.FileReader; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +50,7 @@ import dorkbox.util.process.ShellProcessBuilder; */ @SuppressWarnings({"unused", "Duplicates", "DanglingJavadoc", "WeakerAccess"}) public abstract -class SystemTray { +class SystemTray extends Menu { public static final Logger logger = LoggerFactory.getLogger(SystemTray.class); public static final int TYPE_AUTO_DETECT = 0; @@ -681,39 +676,11 @@ class SystemTray { return systemTray; } - protected final java.util.List menuEntries = new ArrayList(); protected SystemTray() { } - /** - * Necessary to guarantee all updates occur on the dispatch thread - */ - protected abstract - void dispatch(Runnable runnable); - - /** - * Must be wrapped in a synchronized block for object visibility - */ - protected - MenuEntry getMenuEntry(final String menuText) { - if (menuText == null) { - return null; - } - - for (MenuEntry entry : menuEntries) { - String text = entry.getText(); - - // text can be null - if (menuText.equals(text)) { - return entry; - } - } - - return null; - } - public abstract void shutdown(); @@ -787,511 +754,5 @@ class SystemTray { void setIcon(InputStream imageStream) { setIcon_(ImageUtils.resizeAndCache(ImageUtils.TRAY_SIZE, imageStream)); } - - - /** - * Adds a menu entry to the tray icon with text (no image) - * - * @param menuText string of the text you want to appear - * @param callback callback that will be executed when this menu entry is clicked - */ - public final - void addMenuEntry(String menuText, SystemTrayMenuAction callback) { - addMenuEntry(menuText, (String) null, callback); - } - - - /** - * Adds a menu entry to the tray icon with text + image - * - * @param menuText string of the text you want to appear - * @param imagePath the image (full path required) to use. If null, no image will be used - * @param callback callback that will be executed when this menu entry is clicked - */ - public abstract - void addMenuEntry(String menuText, String imagePath, SystemTrayMenuAction callback); - - /** - * Adds a menu entry to the tray icon with text + image - * - * @param menuText string of the text you want to appear - * @param imageUrl the URL of the image to use. If null, no image will be used - * @param callback callback that will be executed when this menu entry is clicked - */ - public abstract - void addMenuEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback); - - /** - * Adds a menu entry to the tray icon with text + image - * - * @param menuText string of the text you want to appear - * @param cacheName @param cacheName the name to use for lookup in the cache for the imageStream - * @param imageStream the InputStream of the image to use. If null, no image will be used - * @param callback callback that will be executed when this menu entry is clicked - */ - public abstract - void addMenuEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback); - - /** - * Adds a menu entry to the tray icon with text + image - * - * @param menuText string of the text you want to appear - * @param imageStream the InputStream of the image to use. If null, no image will be used - * @param callback callback that will be executed when this menu entry is clicked - */ - public abstract - void addMenuEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback); - - - /** - * Updates (or changes) the menu entry's text. - * - * @param origMenuText the original menu text - * @param newMenuText the new menu text (this will replace the original menu text) - */ - public final - void updateMenuEntry(final String origMenuText, final String newMenuText) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setText(newMenuText); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with text {}", origMenuText, newMenuText); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - /** - * Updates (or changes) the menu entry's image (as a String). - * - * @param origMenuText the original menu text - * @param imagePath the new path for the image to use or null to delete the image - */ - public final - void updateMenuEntry_AsImage(final String origMenuText, final String imagePath) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setImage(imagePath); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with image {}", origMenuText, imagePath); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - /** - * Updates (or changes) the menu entry's text. - * - * @param origMenuText the original menu text - * @param imageUrl the new URL for the image to use or null to delete the image - */ - public final - void updateMenuEntry(final String origMenuText, final URL imageUrl) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - - } - else { - menuEntry.setImage(imageUrl); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with image URL {}", origMenuText, imageUrl.getPath()); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - /** - * Updates (or changes) the menu entry's text. - * - * @param cacheName the name to use for lookup in the cache for the imageStream - * @param imageStream the InputStream of the image to use or null to delete the image - */ - public final - void updateMenuEntry(final String origMenuText, final String cacheName, final InputStream imageStream) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setImage(cacheName, imageStream); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with image stream (named) {}", origMenuText, cacheName); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - /** - * Updates (or changes) the menu entry's text. - * - * @param origMenuText the original menu text - * @param imageStream the new path for the image to use or null to delete the image - */ - public final - void updateMenuEntry(final String origMenuText, final InputStream imageStream) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @SuppressWarnings("deprecation") - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setImage(imageStream); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with iamgeStream {}", origMenuText); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - /** - * Updates (or changes) the menu entry's callback. - * - * @param origMenuText the original menu text - * @param newCallback the new callback (this will replace the original callback) - */ - public final - void updateMenuEntry(final String origMenuText, final SystemTrayMenuAction newCallback) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setCallback(newCallback); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with new callback", origMenuText); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - - /** - * Updates (or changes) the menu entry's text and callback. This effectively replaces the menu entry with a new one. - * - * @param origMenuText the original menu text - * @param newMenuText the new menu text (this will replace the original menu text) - * @param newCallback the new callback (this will replace the original callback) - */ - public final - void updateMenuEntry(final String origMenuText, final String newMenuText, final SystemTrayMenuAction newCallback) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(origMenuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - menuEntry.setText(newMenuText); - menuEntry.setCallback(newCallback); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error updating menu entry: {}, with text and callback {}", origMenuText, newMenuText); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + origMenuText + "'"); - } - } - - - /** - * This removes a menu entry from the dropdown menu. - * - * @param menuEntry This is the menu entry to remove - */ - public final - void removeMenuEntry(final MenuEntry menuEntry) { - if (menuEntry == null) { - throw new NullPointerException("No menu entry exists for menuEntry"); - } - - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(false); - - dispatch(new Runnable() { - @Override - public - void run() { - try { - synchronized (menuEntries) { - for (Iterator iterator = menuEntries.iterator(); iterator.hasNext(); ) { - final MenuEntry entry = iterator.next(); - if (entry == menuEntry) { - iterator.remove(); - - // this will also reset the menu - menuEntry.remove(); - hasValue.set(true); - break; - } - } - - // now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function. - if (!menuEntries.isEmpty()) { - if (menuEntries.get(0) instanceof MenuSpacer) { - removeMenuEntry(menuEntries.get(0)); - } - } - // now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function. - if (!menuEntries.isEmpty()) { - if (menuEntries.get(menuEntries.size()-1) instanceof MenuSpacer) { - removeMenuEntry(menuEntries.get(menuEntries.size()-1)); - } - } - } - } catch (Exception e) { - logger.error("Error removing menu entry from list.", e); - } finally { - countDownLatch.countDown(); - } - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error removing menu entry: {}", menuEntry.getText()); - } - - if (!hasValue.get()) { - throw new NullPointerException("Menu entry '" + menuEntry.getText() + "'not found in list while trying to remove it."); - } - } - - - /** - * This removes a menu entry (via the text label) from the dropdown menu. - * - * @param menuText This is the label for the menu entry to remove - */ - public final - void removeMenuEntry(final String menuText) { - // have to wait for the value - final CountDownLatch countDownLatch = new CountDownLatch(1); - final AtomicBoolean hasValue = new AtomicBoolean(true); - - dispatch(new Runnable() { - @Override - public - void run() { - synchronized (menuEntries) { - MenuEntry menuEntry = getMenuEntry(menuText); - - if (menuEntry == null) { - hasValue.set(false); - } - else { - removeMenuEntry(menuEntry); - } - } - countDownLatch.countDown(); - } - }); - - try { - if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) { - throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " + - "`SystemTray.TIMEOUT` to a value which better suites your environment."); - - } - } catch (InterruptedException e) { - logger.error("Error removing menu entry: {}", menuText); - } - - if (!hasValue.get()) { - throw new NullPointerException("No menu entry exists for string '" + menuText + "'"); - } - } - - - /** - * Adds a spacer to the dropdown menu. When menu entries are removed, any menu spacer that ends up at the top/bottom of the drop-down - * menu, will also be removed. For example: - * - * Original Entry3 deleted Result - * - * - * Entry1 Entry1 Entry1 - * Entry2 -> Entry2 -> Entry2 - * (deleted) - * Entry3 (deleted) - */ - public abstract - void addMenuSpacer(); } diff --git a/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java b/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java index ad43709..5dd48a6 100644 --- a/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java +++ b/src/dorkbox/systemTray/linux/GtkTypeSystemTray.java @@ -17,8 +17,6 @@ package dorkbox.systemTray.linux; import java.io.File; -import java.io.InputStream; -import java.net.URL; import java.util.concurrent.atomic.AtomicInteger; import com.sun.jna.Pointer; @@ -28,7 +26,6 @@ import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.SystemTrayMenuAction; import dorkbox.systemTray.linux.jna.Gobject; import dorkbox.systemTray.linux.jna.Gtk; -import dorkbox.systemTray.util.ImageUtils; /** * Derived from @@ -222,7 +219,7 @@ class GtkTypeSystemTray extends SystemTray { return menu; } - private + protected void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) { // some implementations of appindicator, do NOT like having a menu added, which has no menu items yet. // see: https://bugs.launchpad.net/glipper/+bug/1203888 @@ -251,48 +248,4 @@ class GtkTypeSystemTray extends SystemTray { } }); } - - @Override - public - void addMenuEntry(String menuText, final String imagePath, final SystemTrayMenuAction callback) { - if (imagePath == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final URL imageUrl, final SystemTrayMenuAction callback) { - if (imageUrl == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final String cacheName, final InputStream imageStream, final SystemTrayMenuAction callback) { - if (imageStream == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final InputStream imageStream, final SystemTrayMenuAction callback) { - if (imageStream == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream), callback); - } - } } diff --git a/src/dorkbox/systemTray/swing/SwingSystemTray.java b/src/dorkbox/systemTray/swing/SwingSystemTray.java index d2a3f86..2f45075 100644 --- a/src/dorkbox/systemTray/swing/SwingSystemTray.java +++ b/src/dorkbox/systemTray/swing/SwingSystemTray.java @@ -25,8 +25,6 @@ import java.awt.TrayIcon; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; -import java.io.InputStream; -import java.net.URL; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.ImageIcon; @@ -265,7 +263,7 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray { /** * Will add a new menu entry, or update one if it already exists */ - private + protected void addMenuEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) { if (menuText == null) { throw new NullPointerException("Menu text cannot be null"); @@ -292,48 +290,4 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray { } }); } - - @Override - public - void addMenuEntry(String menuText, final String imagePath, final SystemTrayMenuAction callback) { - if (imagePath == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final URL imageUrl, final SystemTrayMenuAction callback) { - if (imageUrl == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final String cacheName, final InputStream imageStream, final SystemTrayMenuAction callback) { - if (imageStream == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream), callback); - } - } - - @Override - public - void addMenuEntry(final String menuText, final InputStream imageStream, final SystemTrayMenuAction callback) { - if (imageStream == null) { - addMenuEntry_(menuText, null, callback); - } - else { - addMenuEntry_(menuText, ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream), callback); - } - } }