Code polish, documentation, cleanup for API changes

This commit is contained in:
nathan 2016-10-24 01:43:32 +02:00
parent daff5a8e48
commit 599dabf4ef
45 changed files with 638 additions and 957 deletions

View File

@ -34,5 +34,7 @@
<orderEntry type="module" module-name="JavaLauncher-Util" />
<orderEntry type="library" name="jna" level="application" />
<orderEntry type="library" name="SWT" level="project" />
<orderEntry type="library" name="asm" level="application" />
<orderEntry type="library" name="javassist" level="application" />
</component>
</module>

View File

@ -13,92 +13,104 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray;
import java.awt.event.ActionListener;
import dorkbox.systemTray.util.MenuCheckboxHook;
import dorkbox.systemTray.peer.CheckboxPeer;
/**
* This represents a common menu-checkbox entry, that is cross platform in nature
*/
@SuppressWarnings("unused")
public
class Checkbox extends Entry {
private volatile boolean isChecked = false;
private volatile String text;
private volatile ActionListener callback;
private boolean isChecked = false;
private String text;
private ActionListener callback;
private volatile boolean enabled = true;
private volatile char mnemonicKey;
private boolean enabled = true;
private char mnemonicKey;
public
Checkbox() {
this(null, null);
}
public
Checkbox(final String text) {
this(text, null);
}
public
Checkbox(final String text, final ActionListener callback) {
this.text = text;
this.callback = callback;
}
public
Checkbox(final String text) {
this(text, null);
}
/**
* @param hook the platform specific implementation for all actions for this type
* @param peer the platform specific implementation for all actions for this type
* @param parent the parent of this menu, null if the parent is the system tray
* @param systemTray the system tray (which is the object that sits in the system tray)
*/
public synchronized
void bind(final MenuCheckboxHook hook, final Menu parent, final SystemTray systemTray) {
super.bind(hook, parent, systemTray);
void bind(final CheckboxPeer peer, final Menu parent, final SystemTray systemTray) {
super.bind(peer, parent, systemTray);
hook.setEnabled(this);
hook.setText(this);
hook.setCallback(this);
hook.setShortcut(this);
hook.setChecked(this);
peer.setEnabled(this);
peer.setText(this);
peer.setCallback(this);
peer.setShortcut(this);
peer.setChecked(this);
}
/**
* @return true if this checkbox is selected, false if not.
*/
public synchronized
boolean getChecked() {
return isChecked;
}
/**
* Sets the checked status for this entry
*
* @param checked true to show the checkbox, false to hide it
*/
public
public synchronized
void setChecked(boolean checked) {
this.isChecked = checked;
if (hook != null) {
((MenuCheckboxHook) hook).setChecked(this);
if (peer != null) {
((CheckboxPeer) peer).setChecked(this);
}
}
/**
* @return true if this checkbox is selected, false if not.
*/
public final
boolean getChecked() {
return isChecked;
}
/**
* Gets the callback assigned to this menu entry
*/
public
public synchronized
ActionListener getCallback() {
return callback;
}
/**
* Sets a callback for a menu entry. This is the action that occurs when one clicks the menu entry
*
* @param callback the callback to set. If null, the callback is safely removed.
*/
public synchronized
void setCallback(final ActionListener callback) {
this.callback = callback;
if (peer != null) {
((CheckboxPeer) peer).setCallback(this);
}
}
/**
* @return true if this item is enabled, or false if it is disabled.
*/
public
public synchronized
boolean getEnabled() {
return this.enabled;
}
@ -106,19 +118,19 @@ class Checkbox extends Entry {
/**
* Enables, or disables the entry.
*/
public
public synchronized
void setEnabled(final boolean enabled) {
this.enabled = enabled;
if (hook != null) {
((MenuCheckboxHook) hook).setEnabled(this);
if (peer != null) {
((CheckboxPeer) peer).setEnabled(this);
}
}
/**
* @return the text label that the menu entry has assigned
*/
public final
public synchronized
String getText() {
return text;
}
@ -128,54 +140,41 @@ class Checkbox extends Entry {
*
* @param text the new text to set
*/
public
public synchronized
void setText(final String text) {
this.text = text;
if (hook != null) {
((MenuCheckboxHook) hook).setText(this);
}
}
/**
* Sets a callback for a menu entry. This is the action that occurs when one clicks the menu entry
*
* @param callback the callback to set. If null, the callback is safely removed.
*/
public
void setCallback(final ActionListener callback) {
this.callback = callback;
if (hook != null) {
((MenuCheckboxHook) hook).setCallback(this);
if (peer != null) {
((CheckboxPeer) peer).setText(this);
}
}
/**
* Gets the shortcut key for this menu entry (Mnemonic) which is what menu entry uses to be "selected" via the keyboard while the
* menu is displayed.
*
* <p>
* Mnemonics are case-insensitive, and if the character defined by the mnemonic is found within the text, the first occurrence
* of it will be underlined.
*/
public
public synchronized
char getShortcut() {
return this.mnemonicKey;
}
/**
* Sets a menu entry shortcut key (Mnemonic) so that menu entry can be "selected" via the keyboard while the menu is displayed.
*
* <p>
* Mnemonics are case-insensitive, and if the character defined by the mnemonic is found within the text, the first occurrence
* of it will be underlined.
*
* @param key this is the key to set as the mnemonic
*/
public
public synchronized
void setShortcut(final char key) {
this.mnemonicKey = key;
if (hook != null) {
((MenuCheckboxHook) hook).setShortcut(this);
if (peer != null) {
((CheckboxPeer) peer).setShortcut(this);
}
}
}

View File

@ -13,12 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray;
import java.util.concurrent.atomic.AtomicInteger;
import dorkbox.systemTray.util.EntryHook;
import dorkbox.systemTray.peer.EntryPeer;
/**
* This represents a common menu-entry, that is cross platform in nature
@ -32,7 +31,7 @@ class Entry {
private Menu parent;
private SystemTray systemTray;
protected volatile EntryHook hook;
protected volatile EntryPeer peer;
public
Entry() {
@ -42,16 +41,16 @@ class Entry {
// called internally when an entry/menu is attached
/**
* @param hook the platform specific implementation for all actions for this type
* @param peer the platform specific implementation for all actions for this type
* @param parent the parent of this menu, null if the parent is the system tray
* @param systemTray the system tray (which is the object that sits in the system tray)
*/
public synchronized
void bind(final EntryHook hook, final Menu parent, final SystemTray systemTray) {
void bind(final EntryPeer peer, final Menu parent, final SystemTray systemTray) {
this.parent = parent;
this.systemTray = systemTray;
this.hook = hook;
this.peer = peer;
}
// END methods for hooking into the system tray, menu's, and entries.
@ -78,12 +77,12 @@ class Entry {
*/
public synchronized
void remove() {
if (hook != null) {
hook.remove();
if (peer != null) {
peer.remove();
this.parent = null;
this.systemTray = null;
hook = null;
peer = null;
}
}

View File

@ -24,8 +24,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import dorkbox.systemTray.util.MenuHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.peer.MenuPeer;
/**
* Represents a cross-platform menu that is displayed by the tray-icon or as a sub-menu
@ -33,7 +32,7 @@ import dorkbox.systemTray.util.Status;
@SuppressWarnings("unused")
public
class Menu extends MenuItem {
protected final List<Entry> menuEntries = new ArrayList<Entry>();
final List<Entry> menuEntries = new ArrayList<Entry>();
public
Menu() {
@ -100,19 +99,17 @@ class Menu extends MenuItem {
}
/**
* @param hook the platform specific implementation for all actions for this type
* @param peer the platform specific implementation for all actions for this type
* @param parent the parent of this menu, null if the parent is the system tray
* @param systemTray the system tray (which is the object that sits in the system tray)
*/
public synchronized
void bind(final MenuHook hook, final Menu parent, final SystemTray systemTray) {
super.bind(hook, parent, systemTray);
void bind(final MenuPeer peer, final Menu parent, final SystemTray systemTray) {
super.bind(peer, parent, systemTray);
synchronized (menuEntries) {
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
final Entry menuEntry = menuEntries.get(i);
hook.add(this, menuEntry, i);
}
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
final Entry menuEntry = menuEntries.get(i);
peer.add(this, menuEntry, i);
}
}
@ -135,22 +132,20 @@ class Menu extends MenuItem {
/**
* Adds a menu entry, separator, or sub-menu to this menu.
*/
public final
public synchronized
<T extends Entry> T add(final T entry, int index) {
synchronized (menuEntries) {
if (index == -1) {
menuEntries.add(entry);
} else {
if (!menuEntries.isEmpty() && menuEntries.get(0) instanceof Status) {
// the "status" menu entry is ALWAYS first
index++;
}
menuEntries.add(index, entry);
if (index == -1) {
menuEntries.add(entry);
} else {
if (!menuEntries.isEmpty() && menuEntries.get(0) instanceof Status) {
// the "status" menu entry is ALWAYS first
index++;
}
menuEntries.add(index, entry);
}
if (hook != null) {
((MenuHook) hook).add(this, entry, index);
if (peer != null) {
((MenuPeer) peer).add(this, entry, index);
}
return entry;
@ -167,18 +162,16 @@ class Menu extends MenuItem {
/**
* Gets the last menu entry or sub-menu, ignoring status and separators
*/
public final
public synchronized
Entry getLast() {
// Must be wrapped in a synchronized block for object visibility
synchronized (menuEntries) {
if (!menuEntries.isEmpty()) {
Entry entry;
for (int i = menuEntries.size()-1; i >= 0; i--) {
entry = menuEntries.get(i);
if (!menuEntries.isEmpty()) {
Entry entry;
for (int i = menuEntries.size()-1; i >= 0; i--) {
entry = menuEntries.get(i);
if (!(entry instanceof Separator || entry instanceof Status)) {
return entry;
}
if (!(entry instanceof Separator || entry instanceof Status)) {
return entry;
}
}
}
@ -191,27 +184,25 @@ class Menu extends MenuItem {
*
* @param menuIndex the menu entry index to use to retrieve the menu entry.
*/
public final
public synchronized
Entry get(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 (Entry entry : menuEntries) {
if (entry instanceof Separator || entry instanceof Status) {
continue;
}
if (count == menuIndex) {
return entry;
}
count++;
if (!menuEntries.isEmpty()) {
int count = 0;
for (Entry entry : menuEntries) {
if (entry instanceof Separator || entry instanceof Status) {
continue;
}
if (count == menuIndex) {
return entry;
}
count++;
}
}
@ -223,19 +214,30 @@ class Menu extends MenuItem {
*
* @param entry This is the menu entry to remove
*/
public final
public synchronized
void remove(final Entry entry) {
// null is passed in when a sub-menu is removing itself from us (because they have already called "remove" and have also
// removed themselves from the menuEntries)
if (entry != null) {
synchronized (menuEntries) {
for (Iterator<Entry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
final Entry entry__ = iterator.next();
if (entry__ == entry) {
iterator.remove();
entry.remove();
break;
}
for (Iterator<Entry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
final Entry entry__ = iterator.next();
if (entry__ == entry) {
iterator.remove();
entry.remove();
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 dorkbox.systemTray.Separator) {
remove(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 dorkbox.systemTray.Separator) {
remove(menuEntries.get(menuEntries.size() - 1));
}
}
}
@ -244,16 +246,14 @@ class Menu extends MenuItem {
/**
* This removes all menu entries from this menu
*/
public final
public synchronized
void removeAll() {
synchronized (menuEntries) {
// have to make copy because we are deleting all of them, and sub-menus remove themselves from parents
ArrayList<Entry> menuEntriesCopy = new ArrayList<Entry>(this.menuEntries);
for (Entry entry : menuEntriesCopy) {
entry.remove();
}
menuEntries.clear();
// have to make copy because we are deleting all of them, and sub-menus remove themselves from parents
ArrayList<Entry> menuEntriesCopy = new ArrayList<Entry>(this.menuEntries);
for (Entry entry : menuEntriesCopy) {
entry.remove();
}
menuEntries.clear();
}

View File

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray;
import java.awt.Image;
@ -22,8 +21,8 @@ import java.io.File;
import java.io.InputStream;
import java.net.URL;
import dorkbox.systemTray.peer.MenuItemPeer;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.systemTray.util.MenuItemHook;
/**
* This represents a common menu-entry, that is cross platform in nature
@ -31,13 +30,13 @@ import dorkbox.systemTray.util.MenuItemHook;
@SuppressWarnings({"unused", "SameParameterValue", "WeakerAccess"})
public
class MenuItem extends Entry {
private volatile String text;
private volatile File imageFile;
private volatile ActionListener callback;
private String text;
private File imageFile;
private ActionListener callback;
// default enabled is always true
private volatile boolean enabled = true;
private volatile char mnemonicKey;
private boolean enabled = true;
private char mnemonicKey;
public
MenuItem() {
@ -113,27 +112,27 @@ class MenuItem extends Entry {
}
/**
* @param hook the platform specific implementation for all actions for this type
* @param peer the platform specific implementation for all actions for this type
* @param parent the parent of this menu, null if the parent is the system tray
* @param systemTray the system tray (which is the object that sits in the system tray)
*/
public synchronized
void bind(final MenuItemHook hook, final Menu parent, final SystemTray systemTray) {
super.bind(hook, parent, systemTray);
void bind(final MenuItemPeer peer, final Menu parent, final SystemTray systemTray) {
super.bind(peer, parent, systemTray);
hook.setImage(this);
hook.setEnabled(this);
hook.setText(this);
hook.setCallback(this);
hook.setShortcut(this);
peer.setImage(this);
peer.setEnabled(this);
peer.setText(this);
peer.setCallback(this);
peer.setShortcut(this);
}
private
private synchronized
void setImage_(final File imageFile) {
this.imageFile = imageFile;
if (hook != null) {
((MenuItemHook) hook).setImage(this);
if (peer != null) {
((MenuItemPeer) peer).setImage(this);
}
}
@ -142,7 +141,7 @@ class MenuItem extends Entry {
* <p>
* This file can also be a cached file, depending on how the image was assigned to this entry.
*/
public
public synchronized
File getImage() {
return imageFile;
}
@ -150,7 +149,7 @@ class MenuItem extends Entry {
/**
* Gets the callback assigned to this menu entry
*/
public
public synchronized
ActionListener getCallback() {
return callback;
}
@ -158,7 +157,7 @@ class MenuItem extends Entry {
/**
* @return true if this item is enabled, or false if it is disabled.
*/
public
public synchronized
boolean getEnabled() {
return this.enabled;
}
@ -166,19 +165,19 @@ class MenuItem extends Entry {
/**
* Enables, or disables the entry.
*/
public
public synchronized
void setEnabled(final boolean enabled) {
this.enabled = enabled;
if (hook != null) {
((MenuItemHook) hook).setEnabled(this);
if (peer != null) {
((MenuItemPeer) peer).setEnabled(this);
}
}
/**
* @return the text label that the menu entry has assigned
*/
public final
public synchronized
String getText() {
return text;
}
@ -188,12 +187,12 @@ class MenuItem extends Entry {
*
* @param text the new text to set
*/
public
public synchronized
void setText(final String text) {
this.text = text;
if (hook != null) {
((MenuItemHook) hook).setText(this);
if (peer != null) {
((MenuItemPeer) peer).setText(this);
}
}
@ -315,7 +314,7 @@ class MenuItem extends Entry {
/**
* @return true if this menu entry has an image assigned to it, or is just text.
*/
public
public synchronized
boolean hasImage() {return imageFile != null;}
/**
@ -323,12 +322,12 @@ class MenuItem extends Entry {
*
* @param callback the callback to set. If null, the callback is safely removed.
*/
public
public synchronized
void setCallback(final ActionListener callback) {
this.callback = callback;
if (hook != null) {
((MenuItemHook) hook).setCallback(this);
if (peer != null) {
((MenuItemPeer) peer).setCallback(this);
}
}
@ -339,7 +338,7 @@ class MenuItem extends Entry {
* Mnemonics are case-insensitive, and if the character defined by the mnemonic is found within the text, the first occurrence
* of it will be underlined.
*/
public
public synchronized
char getShortcut() {
return this.mnemonicKey;
}
@ -352,19 +351,19 @@ class MenuItem extends Entry {
*
* @param key this is the key to set as the mnemonic
*/
public
public synchronized
void setShortcut(final char key) {
this.mnemonicKey = key;
if (hook != null) {
((MenuItemHook) hook).setShortcut(this);
if (peer != null) {
((MenuItemPeer) peer).setShortcut(this);
}
}
@Override
public synchronized
void remove() {
if (hook != null) {
if (peer != null) {
setImage_(null);
setText(null);
setCallback(null);

View File

@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray;
/**

View File

@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray;
package dorkbox.systemTray.util;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.StatusPeer;
/**
* This represents a common menu-status entry, that is cross platform in nature
@ -32,21 +29,21 @@ class Status extends Entry {
}
/**
* @param hook the platform specific implementation for all actions for this type
* @param peer the platform specific implementation for all actions for this type
* @param parent the parent of this menu, null if the parent is the system tray
* @param systemTray the system tray (which is the object that sits in the system tray)
*/
public synchronized
void bind(final MenuStatusHook hook, final Menu parent, final SystemTray systemTray) {
super.bind(hook, parent, systemTray);
void bind(final StatusPeer peer, final Menu parent, final SystemTray systemTray) {
super.bind(peer, parent, systemTray);
hook.setText(this);
peer.setText(this);
}
/**
* @return the text label that the menu entry has assigned
*/
public final
public synchronized
String getText() {
return text;
}
@ -56,12 +53,12 @@ class Status extends Entry {
*
* @param text the new text to set
*/
public
public synchronized
void setText(final String text) {
this.text = text;
if (hook != null) {
((MenuStatusHook) hook).setText(this);
if (peer != null) {
((StatusPeer) peer).setText(this);
}
}
}

View File

@ -1,10 +1,20 @@
/*
* 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 dorkbox.systemTray.util.Status;
/**
*
*/
public
class Tray extends Menu {
@ -22,7 +32,7 @@ class Tray extends Menu {
/**
* Gets the 'status' string assigned to the system tray
*/
public final
public synchronized
String getStatus() {
return statusText;
}
@ -32,37 +42,35 @@ class Tray extends Menu {
*
* @param statusText the text you want displayed, null if you want to remove the 'status' string
*/
public final
public synchronized
void setStatus(final String statusText) {
this.statusText = statusText;
synchronized (menuEntries) {
// status is ALWAYS at 0 index...
Entry menuEntry = null;
if (!menuEntries.isEmpty()) {
menuEntry = menuEntries.get(0);
}
if (menuEntry instanceof Status) {
// set the text or delete...
if (statusText == null) {
// delete
remove(menuEntry);
}
else {
// set text
((Status) menuEntry).setText(statusText);
}
} else {
// create a new one
Status status = new Status();
status.setText(statusText);
// status is ALWAYS at 0 index...
Entry menuEntry = null;
if (!menuEntries.isEmpty()) {
menuEntry = menuEntries.get(0);
}
if (menuEntry instanceof Status) {
// set the text or delete...
if (statusText == null) {
// delete
remove(menuEntry);
}
else {
// set text
((Status) menuEntry).setText(statusText);
}
} else {
// create a new one
Status status = new Status();
status.setText(statusText);
// status is ALWAYS at 0 index...
// also calls the hook to add it, so we don't need anything special
add(status, 0);
}
// also calls the hook to add it, so we don't need anything special
add(status, 0);
}
}
}

View File

@ -1,170 +0,0 @@
/*
* 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.nativeUI;
import java.awt.MenuItem;
import java.awt.MenuShortcut;
import java.awt.PopupMenu;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.swingUI.SwingUI;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
abstract
class AwtEntry extends Entry implements SwingUI {
private final AwtMenu parent;
final MenuItem _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.
AwtEntry(final AwtMenu parent, final MenuItem menuItem) {
this.parent = parent;
this._native = menuItem;
parent._native.add(menuItem);
}
// public
// Menu getParent() {
// return parent;
// }
/**
* must always be called in the EDT thread
*/
abstract
void renderText(final String text);
/**
* Not always called on the EDT thread
*/
abstract
void setImage_(final File imageFile);
/**
* Enables, or disables the sub-menu entry.
*/
public
void setEnabled(final boolean enabled) {
_native.setEnabled(enabled);
}
public
void setShortcut(final char key) {
if (!(_native instanceof PopupMenu)) {
// yikes...
final int vKey = SystemTrayFixes.getVirtualKey(key);
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
_native.setShortcut(new MenuShortcut(vKey));
}
});
}
}
public
String getText() {
return text;
}
public
void setText(final String newText) {
this.text = newText;
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
renderText(newText);
}
});
}
public
void setImage(final File imageFile) {
if (imageFile == null) {
setImage_(null);
}
else {
// setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile));
}
}
public final
void setImage(final String imagePath) {
if (imagePath == null) {
setImage_(null);
}
else {
// setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
}
}
public final
void setImage(final URL imageUrl) {
if (imageUrl == null) {
setImage_(null);
}
else {
// setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
}
}
public final
void setImage(final String cacheName, final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
// setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
}
}
public final
void setImage(final InputStream imageStream) {
if (imageStream == null) {
setImage_(null);
}
else {
// setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
}
}
public final
void remove() {
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
removePrivate();
parent._native.remove(_native);
}
});
}
// called when this item is removed. Necessary to cleanup/remove itself
abstract
void removePrivate();
}

View File

@ -24,14 +24,14 @@ import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.util.MenuHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.peer.MenuPeer;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both
@SuppressWarnings("ForLoopReplaceableByForEach")
class AwtMenu implements MenuHook {
class AwtMenu implements MenuPeer {
volatile java.awt.Menu _native;
private final AwtMenu parent;

View File

@ -21,11 +21,11 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.MenuItemHook;
import dorkbox.systemTray.peer.MenuItemPeer;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
class AwtMenuItem implements MenuItemHook {
class AwtMenuItem implements MenuItemPeer {
private final AwtMenu parent;
private final MenuItem _native = new java.awt.MenuItem();
@ -116,6 +116,7 @@ class AwtMenuItem implements MenuItemHook {
});
}
@SuppressWarnings("Duplicates")
@Override
public
void remove() {

View File

@ -21,18 +21,17 @@ import java.awt.event.ActionListener;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.MenuCheckboxHook;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
class AwtMenuItemCheckbox implements MenuCheckboxHook {
class AwtMenuItemCheckbox implements CheckboxPeer {
private final AwtMenu parent;
private final java.awt.CheckboxMenuItem _native = new java.awt.CheckboxMenuItem();
private volatile ActionListener swingCallback;
// this is ALWAYS called on the EDT.
AwtMenuItemCheckbox(final AwtMenu parent) {
this.parent = parent;

View File

@ -17,10 +17,10 @@ package dorkbox.systemTray.nativeUI;
import java.awt.MenuItem;
import dorkbox.systemTray.util.EntryHook;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.util.SwingUtil;
class AwtMenuItemSeparator implements EntryHook {
class AwtMenuItemSeparator implements EntryPeer {
private final AwtMenu parent;
private final MenuItem _native = new MenuItem("-");

View File

@ -20,11 +20,11 @@ import static java.awt.Font.DIALOG;
import java.awt.Font;
import java.awt.MenuItem;
import dorkbox.systemTray.util.MenuStatusHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.peer.StatusPeer;
import dorkbox.util.SwingUtil;
class AwtMenuItemStatus implements MenuStatusHook {
class AwtMenuItemStatus implements StatusPeer {
private final AwtMenu parent;
private final MenuItem _native = new MenuItem();

View File

@ -21,11 +21,11 @@ import com.sun.jna.Pointer;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.EntryHook;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.systemTray.util.ImageUtils;
abstract
class GtkMenuBaseItem implements EntryHook {
class GtkBaseMenuItem implements EntryPeer {
private static File transparentIcon = null;
// these are necessary BECAUSE GTK menus look funky as hell when there are some menu entries WITH icons and some WITHOUT
private volatile boolean hasLegitImage = true;
@ -33,7 +33,7 @@ class GtkMenuBaseItem implements EntryHook {
// these have to be volatile, because they can be changed from any thread
private volatile Pointer spacerImage;
GtkMenuBaseItem() {
GtkBaseMenuItem() {
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
if (transparentIcon == null) {
transparentIcon = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE);
@ -45,7 +45,6 @@ class GtkMenuBaseItem implements EntryHook {
return hasLegitImage;
}
public
void setLegitImage(boolean isLegit) {
hasLegitImage = isLegit;
}

View File

@ -15,7 +15,6 @@
*/
package dorkbox.systemTray.nativeUI;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
@ -27,13 +26,13 @@ import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.MenuHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.peer.MenuPeer;
class GtkMenu extends GtkMenuBaseItem implements MenuHook {
class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
// this is a list (that mirrors the actual list) BECAUSE we have to create/delete the entire menu in GTK every time something is changed
private final List<GtkMenuBaseItem> menuEntries = new LinkedList<GtkMenuBaseItem>();
private final List<GtkBaseMenuItem> menuEntries = new LinkedList<GtkBaseMenuItem>();
private final GtkMenu parent;
volatile Pointer _nativeMenu; // must ONLY be created at the end of delete!
@ -55,7 +54,7 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
this.parent = parent;
if (parent != null) {
_nativeEntry = Gtk.gtk_image_menu_item_new_with_mnemonic(""); // is what is added to the parent menu
_nativeEntry = Gtk.gtk_image_menu_item_new_with_mnemonic(""); // is what is added to the parent menu (so images work)
} else {
_nativeEntry = null;
}
@ -66,7 +65,7 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
}
private
void add(final GtkMenuBaseItem item, final int index) {
void add(final GtkBaseMenuItem item, final int index) {
if (index > 0) {
menuEntries.add(index, item);
} else {
@ -98,7 +97,7 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
// have to remove all other menu entries
synchronized (menuEntries) {
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
final GtkMenuBaseItem menuEntry__ = menuEntries.get(i);
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
menuEntry__.onDeleteMenu(_nativeMenu);
}
@ -136,13 +135,13 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
// now add back other menu entries
synchronized (menuEntries) {
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
final GtkMenuBaseItem menuEntry__ = menuEntries.get(i);
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
hasImages |= menuEntry__.hasImage();
}
for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
// the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images
final GtkMenuBaseItem menuEntry__ = menuEntries.get(i);
final GtkBaseMenuItem menuEntry__ = menuEntries.get(i);
menuEntry__.onCreateMenu(_nativeMenu, hasImages);
if (menuEntry__ instanceof GtkMenu) {
@ -173,10 +172,10 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
synchronized (menuEntries) {
// a copy is made because sub-menus remove themselves from parents when .remove() is called. If we don't
// do this, errors will be had because indices don't line up anymore.
ArrayList<GtkMenuBaseItem> menuEntriesCopy = new ArrayList<GtkMenuBaseItem>(this.menuEntries);
ArrayList<GtkBaseMenuItem> menuEntriesCopy = new ArrayList<GtkBaseMenuItem>(this.menuEntries);
for (int i = 0, menuEntriesSize = menuEntriesCopy.size(); i < menuEntriesSize; i++) {
final GtkMenuBaseItem menuEntry__ = menuEntriesCopy.get(i);
final GtkBaseMenuItem menuEntry__ = menuEntriesCopy.get(i);
menuEntry__.remove();
}
this.menuEntries.clear();
@ -354,7 +353,7 @@ class GtkMenu extends GtkMenuBaseItem implements MenuHook {
// called when a child removes itself from the parent menu. Does not work for sub-menus
public
void remove(final GtkMenuBaseItem item) {
void remove(final GtkBaseMenuItem item) {
synchronized (menuEntries) {
menuEntries.remove(item);
}

View File

@ -25,9 +25,9 @@ import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.jna.linux.GCallback;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.MenuItemHook;
import dorkbox.systemTray.peer.MenuItemPeer;
class GtkMenuItem extends GtkMenuBaseItem implements MenuItemHook, GCallback {
class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final NativeLong nativeLong;

View File

@ -26,10 +26,10 @@ import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.jna.linux.GCallback;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.systemTray.util.MenuCheckboxHook;
class GtkMenuItemCheckbox extends GtkMenuBaseItem implements MenuCheckboxHook, GCallback {
class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCallback {
private static File transparentIcon = null;
@SuppressWarnings({"FieldCanBeLocal", "unused"})

View File

@ -18,9 +18,9 @@ package dorkbox.systemTray.nativeUI;
import com.sun.jna.Pointer;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.EntryHook;
import dorkbox.systemTray.peer.EntryPeer;
class GtkMenuItemSeparator extends GtkMenuBaseItem implements EntryHook {
class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
private final GtkMenu parent;
private final Pointer _native = Gtk.gtk_separator_menu_item_new();

View File

@ -17,13 +17,13 @@ package dorkbox.systemTray.nativeUI;
import com.sun.jna.Pointer;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.MenuStatusHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.peer.StatusPeer;
// you might wonder WHY this extends MenuEntryItem -- the reason is that an AppIndicator "status" will be offset from everyone else,
// where a GtkStatusIconTray + SwingUI will have everything lined up. (with or without icons). This is to normalize how it looks
class GtkMenuItemStatus extends GtkMenuBaseItem implements MenuStatusHook {
class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
private final GtkMenu parent;
private final Pointer _native = Gtk.gtk_image_menu_item_new_with_mnemonic("");

View File

@ -23,9 +23,8 @@ package dorkbox.systemTray.nativeUI;
* the system (with the exception of Windows, whose native menu looks absolutely terrible).
* <p>
* Noticeable differences that are limitations for the NativeUI only:
* - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
* - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
* - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
* - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
*/
public
interface NativeUI
{}
interface NativeUI {}

View File

@ -157,13 +157,13 @@ class _AppIndicatorNativeTray extends Tray implements NativeUI {
@Override
public
void setText(final MenuItem menuItem) {
// no op
// no op.
}
@Override
public
void setShortcut(final MenuItem menuItem) {
// no op
// no op.
}
@Override

View File

@ -38,7 +38,7 @@ import dorkbox.util.SwingUtil;
* http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6453521
* https://stackoverflow.com/questions/331407/java-trayicon-using-image-with-transparent-background/3882028#3882028
*
* Also, on linux, this WILL NOT CLOSE properly -- there is a frame handle that keeps the JVM open
* Also, on linux, this WILL NOT CLOSE properly -- there is a frame handle that keeps the JVM open. MacOS does not have this problem.
*/
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "WeakerAccess"})
public
@ -157,7 +157,7 @@ class _AwtTray extends Tray implements NativeUI {
@Override
public
void setText(final MenuItem menuItem) {
// no op
// no op.
}
@Override

View File

@ -0,0 +1,35 @@
/*
* Copyright 2016 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.peer;
import dorkbox.systemTray.Checkbox;
/**
* Internal component used to bind the API to the implementation
*/
public
interface CheckboxPeer extends EntryPeer {
void setEnabled(Checkbox menuItem);
void setText(Checkbox menuItem);
void setCallback(Checkbox menuItem);
void setShortcut(Checkbox menuItem);
void setChecked(Checkbox checkbox);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2016 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.peer;
/**
* Internal component used to bind the API to the implementation
*/
public
interface EntryPeer {
void remove();
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2016 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.peer;
import dorkbox.systemTray.MenuItem;
/**
* Internal component used to bind the API to the implementation
*/
public
interface MenuItemPeer extends EntryPeer {
void setImage(MenuItem menuItem);
void setEnabled(MenuItem menuItem);
void setText(MenuItem menuItem);
void setCallback(MenuItem menuItem);
void setShortcut(MenuItem menuItem);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2016 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.peer;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
/**
* Internal component used to bind the API to the implementation
*/
public
interface MenuPeer extends MenuItemPeer {
void add(Menu parentMenu, Entry entry, int index);
}

View File

@ -0,0 +1,26 @@
/*
* Copyright 2016 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.peer;
import dorkbox.systemTray.Status;
/**
* Internal component used to bind the API to the implementation
*/
public
interface StatusPeer extends EntryPeer {
void setText(Status menuItem);
}

View File

@ -15,7 +15,6 @@
*/
package dorkbox.systemTray.swingUI;
import java.io.File;
import javax.swing.ImageIcon;
@ -26,15 +25,14 @@ import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.MenuHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.peer.MenuPeer;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both (and duplicate code)
@SuppressWarnings("ForLoopReplaceableByForEach")
class SwingMenu implements MenuHook {
class SwingMenu implements MenuPeer {
final JComponent _native;
private final SwingMenu parent;
@ -89,12 +87,11 @@ class SwingMenu implements MenuHook {
@Override
public
void setImage(final MenuItem menuItem) {
final File imageFile = menuItem.getImage();
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
File imageFile = menuItem.getImage();
if (imageFile != null) {
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
((AdjustedJMenu) _native).setIcon(origIcon);
@ -179,41 +176,4 @@ class SwingMenu implements MenuHook {
}
});
}
// NOT ALWAYS CALLED ON EDT
protected
void remove__(final Object menuEntry) {
try {
// synchronized (menuEntries) {
// // null is passed in when a sub-menu is removing itself from us (because they have already called "remove" and have also
// // removed themselves from the menuEntries)
// if (menuEntry != null) {
// for (Iterator<Entry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
// final Entry entry = iterator.next();
// if (entry == menuEntry) {
// iterator.remove();
// entry.remove();
// 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 dorkbox.systemTray.Separator) {
// remove(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 dorkbox.systemTray.Separator) {
// remove(menuEntries.get(menuEntries.size() - 1));
// }
// }
// }
} catch (Exception e) {
SystemTray.logger.error("Error removing entry from menu.", e);
}
}
}

View File

@ -24,11 +24,11 @@ import javax.swing.JMenuItem;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.util.MenuItemHook;
import dorkbox.systemTray.peer.MenuItemPeer;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
class SwingMenuItem implements MenuItemHook {
class SwingMenuItem implements MenuItemPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();
@ -44,12 +44,11 @@ class SwingMenuItem implements MenuItemHook {
@Override
public
void setImage(final MenuItem menuItem) {
final File imageFile = menuItem.getImage();
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
File imageFile = menuItem.getImage();
if (imageFile != null) {
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
_native.setIcon(origIcon);

View File

@ -24,12 +24,12 @@ import javax.swing.JMenuItem;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.systemTray.util.MenuCheckboxHook;
import dorkbox.systemTray.util.SystemTrayFixes;
import dorkbox.util.SwingUtil;
class SwingMenuItemCheckbox implements MenuCheckboxHook {
class SwingMenuItemCheckbox implements CheckboxPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();

View File

@ -17,10 +17,10 @@ package dorkbox.systemTray.swingUI;
import javax.swing.JSeparator;
import dorkbox.systemTray.util.EntryHook;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.util.SwingUtil;
class SwingMenuItemSeparator implements EntryHook {
class SwingMenuItemSeparator implements EntryPeer {
private final SwingMenu parent;
private final JSeparator _native = new JSeparator(JSeparator.HORIZONTAL);

View File

@ -19,11 +19,11 @@ import java.awt.Font;
import javax.swing.JMenuItem;
import dorkbox.systemTray.util.MenuStatusHook;
import dorkbox.systemTray.util.Status;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.peer.StatusPeer;
import dorkbox.util.SwingUtil;
class SwingMenuItemStatus implements MenuStatusHook {
class SwingMenuItemStatus implements StatusPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();

View File

@ -22,9 +22,8 @@ package dorkbox.systemTray.swingUI;
* one loses the native L&F of the system (with the exception of Windows, whose native menu looks absolutely terrible).
* <p>
* Noticeable differences that are limitations for the NativeUI only:
* - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
* - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
* - AppIndicator Status entries must be plain text (they are not bold as they are everywhere else).
* - MacOS cannot have images in their menu or sub-menu's -- only plain text is possible
*/
public
interface SwingUI
{}
interface SwingUI {}

View File

@ -40,7 +40,7 @@ import dorkbox.util.ScreenUtil;
/**
* This custom popup is required if we want to be able to show images on the menu,
*
* This is our "golden standard" since we have 100% control over it.
* This is our "golden standard" since we have 100% control over it on all platforms
*/
class TrayPopup extends JPopupMenu {
private static final long serialVersionUID = 1L;

View File

@ -41,15 +41,15 @@ import dorkbox.util.SwingUtil;
* <p/>
* This is the "old" way to do it, and does not work with some desktop environments. This is a hybrid class, because we want to show the
* swing menu popup INSTEAD of GTK menu popups. The "golden standard" is our swing menu popup, since we have 100% control over it.
*
* http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
* https://github.com/djdeath/glib/blob/master/gobject/gobject.c
*/
@SuppressWarnings("Duplicates")
public
class _GtkStatusIconTray extends Tray implements SwingUI {
private volatile Pointer trayIcon;
// http://code.metager.de/source/xref/gnome/Platform/gtk%2B/gtk/deprecated/gtkstatusicon.c
// https://github.com/djdeath/glib/blob/master/gobject/gobject.c
// have to save these in a field to prevent GC on the objects (since they go out-of-scope from java)
private final List<Object> gtkCallbacks = new ArrayList<Object>();

View File

@ -1,9 +0,0 @@
package dorkbox.systemTray.util;
/**
*
*/
public
interface EntryHook {
void remove();
}

View File

@ -1,243 +0,0 @@
/*
* 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.util;
import dorkbox.systemTray.Menu;
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both
@SuppressWarnings("ForLoopReplaceableByForEach")
public abstract
class MenuBase extends Menu {
// /**
// * Called in the EDT/GTK dispatch threads
// *
// * @param systemTray the system tray (which is the object that sits in the system tray)
// * @param parent the parent of this menu, null if the parent is the system tray
// */
// public
// MenuBase(final SystemTray systemTray, final Menu parent) {
// setSystemTray(systemTray);
// setParent(parent);
// }
protected abstract
void dispatch(final Runnable runnable);
protected abstract
void dispatchAndWait(final Runnable runnable);
/**
* Will add a new menu entry
* NOT ALWAYS CALLED ON DISPATCH
*/
// protected abstract
// Entry addEntry_(final String menuText, final File imagePath, final ActionListener callback);
/**
* Will add a new checkbox menu entry
* NOT ALWAYS CALLED ON DISPATCH
*/
// protected abstract
// Checkbox addCheckbox_(final String menuText, final ActionListener callback);
/**
* Will add a new sub-menu entry
* NOT ALWAYS CALLED ON DISPATCH
*/
// protected abstract
// Menu addMenu_(final String menuText, final File imagePath);
// // public here so that Swing/Gtk/AppIndicator can override this
// protected abstract
// void setImage_(final File imageFile);
// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however.
// public
// Entry addWidget(final JComponent widget) {
// if (widget == null) {
// throw new NullPointerException("Widget cannot be null");
// }
//
// final AtomicReference<Entry> value = new AtomicReference<Entry>();
//
// dispatchAndWait(new Runnable() {
// @Override
// public
// void run() {
// synchronized (menuEntries) {
// // must always be called on the EDT
// Entry entry = new EntryWidget(MenuImpl.this, widget);
// value.set(entry);
// menuEntries.add(entry);
// }
// }
// });
//
// return value.get();
// }
// public final
// Entry get(final String menuText) {
// if (menuText == null || menuText.isEmpty()) {
// return null;
// }
//
// // Must be wrapped in a synchronized block for object visibility
// synchronized (menuEntries) {
// for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) {
// final Entry entry = menuEntries.get(i);
//
// if (entry instanceof Separator || entry instanceof Status) {
// continue;
// }
//
//// String text = entry.getText();
//
// // text can be null
//// if (menuText.equals(text)) {
//// return entry;
//// }
// }
// }
//
// return null;
// }
// /**
// * This removes a menu entry from the dropdown menu.
// *
// * @param entry This is the menu entry to remove
// */
// @Override
// public final
// void remove(final Entry entry) {
// if (entry == null) {
// throw new NullPointerException("No menu entry exists for entry");
// }
//
// dispatchAndWait(new Runnable() {
// @Override
// public
// void run() {
// remove__(entry);
// }
// });
// }
//
// /**
// * This removes a sub-menu entry from the dropdown menu.
// *
// * @param menu This is the menu entry to remove
// */
// @Override
// public final
// void remove(final Menu menu) {
// final Menu parent = getParent();
// if (parent == null) {
// // we are the system tray menu, so we just remove from ourselves
// dispatchAndWait(new Runnable() {
// @Override
// public
// void run() {
// remove__(menu);
// }
// });
// } else {
// final Menu _this = this;
// // we are a submenu
// dispatchAndWait(new Runnable() {
// @Override
// public
// void run() {
// ((MenuBase) parent).remove__(_this);
// }
// });
// }
// }
// /**
// * This removes a menu entry or sub-menu (via the text label) from the dropdown menu.
// *
// * @param menuText This is the label for the menu entry or sub-menu to remove
// */
// public final
// void remove(final String menuText) {
// dispatchAndWait(new Runnable() {
// @Override
// public
// void run() {
// synchronized (menuEntries) {
// Entry entry = get(menuText);
//
// if (entry != null) {
// remove(entry);
// }
// }
// }
// });
// }
//
// @Override
// public final
// void removeAll() {
// dispatch(new Runnable() {
// @Override
// public
// void run() {
// synchronized (menuEntries) {
// // have to make copy because we are deleting all of them, and sub-menus remove themselves from parents
// ArrayList<Entry> menuEntriesCopy = new ArrayList<Entry>(MenuBase.this.menuEntries);
// for (Entry entry : menuEntriesCopy) {
// entry.remove();
// }
// menuEntries.clear();
// }
// }
// });
// }
}

View File

@ -1,20 +0,0 @@
package dorkbox.systemTray.util;
import dorkbox.systemTray.Checkbox;
/**
*
*/
public
interface MenuCheckboxHook extends EntryHook {
void setEnabled(Checkbox menuItem);
void setText(Checkbox menuItem);
void setCallback(Checkbox menuItem);
void setShortcut(Checkbox menuItem);
void setChecked(Checkbox checkbox);
}

View File

@ -1,12 +0,0 @@
package dorkbox.systemTray.util;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
/**
*
*/
public
interface MenuHook extends MenuItemHook {
void add(Menu parentMenu, Entry entry, int index);
}

View File

@ -1,19 +0,0 @@
package dorkbox.systemTray.util;
import dorkbox.systemTray.MenuItem;
/**
*
*/
public
interface MenuItemHook extends EntryHook {
void setImage(MenuItem menuItem);
void setEnabled(MenuItem menuItem);
void setText(MenuItem menuItem);
void setCallback(MenuItem menuItem);
void setShortcut(MenuItem menuItem);
}

View File

@ -1,9 +0,0 @@
package dorkbox.systemTray.util;
/**
*
*/
public
interface MenuStatusHook extends EntryHook {
void setText(Status menuItem);
}

View File

@ -21,8 +21,9 @@ import java.awt.event.ActionListener;
import java.net.URL;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
/**
@ -51,7 +52,6 @@ class TestTray {
}
private SystemTray systemTray;
private ActionListener callbackGreen;
private ActionListener callbackGray;
public
@ -61,23 +61,32 @@ class TestTray {
throw new RuntimeException("Unable to load SystemTray!");
}
// final JPopupMenu popupMenu = new JPopupMenu();
// JMenu submenu2 = new JMenu("SubMenu1");
// submenu2.add("asdf");
// submenu2.add("asdf");
//
// // Add submenu to popup menu
// popupMenu.add(submenu2);
systemTray.setImage(LT_GRAY_TRAIN);
systemTray.setStatus("No Mail");
callbackGreen = new ActionListener() {
callbackGray = new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
final Entry entry = (Entry) e.getSource();
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.getMenu().remove(entry);
System.err.println("POW");
}
};
Menu mainMenu = systemTray.getMenu();
MenuItem greenEntry = new MenuItem("Green Mail", new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus("Some Mail!");
systemTray.setImage(GREEN_TRAIN);
@ -86,76 +95,79 @@ class TestTray {
entry.setText("Delete Mail");
// systemTray.remove(menuEntry);
}
};
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
mainMenu.add(greenEntry);
callbackGray = new ActionListener() {
Checkbox checkbox = new Checkbox("Euro € Mail", new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
final Entry entry = (Entry) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.remove(entry);
System.err.println("POW");
}
};
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
// case does not matter
menuEntry.setShortcut('G');
final Checkbox menuCheckbox = this.systemTray.addCheckbox("Euro € Mail", callbackGreen);
// case does not matter
// menuCheckbox.setShortcut('€');
this.systemTray.addSeparator();
final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
submenu.addEntry("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
submenu.setEnabled(false);
System.err.println("Am i checked? " + ((Checkbox) e.getSource()).getChecked());
}
});
checkbox.setShortcut('€');
mainMenu.add(checkbox);
mainMenu.add(new Separator());
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
mainMenu.add(submenu);
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
MenuItem source = (MenuItem) e.getSource();
source.getParent().setEnabled(false);
}
});
submenu.add(disableMenu);
// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however.
// submenu.addEntry("Add widget", GREEN_BUS, new Action() {
// submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
// @Override
// public
// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
// void actionPerformed(final ActionEvent e) {
// MenuItem source = (MenuItem) e.getSource();
// JProgressBar progressBar = new JProgressBar(0, 100);
// progressBar.setValue(new Random().nextInt(101));
// progressBar.setStringPainted(true);
// systemTray.addWidget(progressBar);
// source.getSystemTray().add(progressBar);
// }
// });
submenu.addEntry("Hide tray", LT_GRAY_BUS, new ActionListener() {
// }));
submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
systemTray.setEnabled(false);
}
});
submenu.addEntry("Remove menu", BLACK_FIRE, new ActionListener() {
}));
submenu.add(new MenuItem("Remove menu", BLACK_FIRE, new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
submenu.remove();
MenuItem source = (MenuItem) e.getSource();
source.getParent().remove();
}
});
}));
systemTray.addEntry("Quit", new ActionListener() {
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
systemTray.shutdown();
//System.exit(0); not necessary if all non-daemon threads have stopped.
}
}).setShortcut('q'); // case does not matter
})).setShortcut('q'); // case does not matter
}
}

View File

@ -19,8 +19,10 @@ package dorkbox;
import java.awt.event.ActionListener;
import java.net.URL;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
import javafx.application.Application;
import javafx.application.Platform;
@ -95,11 +97,29 @@ class TestTrayJavaFX extends Application {
systemTray.setImage(LT_GRAY_TRAIN);
systemTray.setStatus("No Mail");
callbackGreen = new ActionListener() {
callbackGray = new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
final Entry entry = (Entry) e.getSource();
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.getMenu().remove(entry);
System.err.println("POW");
}
};
Menu mainMenu = systemTray.getMenu();
MenuItem greenEntry = new MenuItem("Green Mail", new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus("Some Mail!");
systemTray.setImage(GREEN_TRAIN);
@ -108,69 +128,73 @@ class TestTrayJavaFX extends Application {
entry.setText("Delete Mail");
// systemTray.remove(menuEntry);
}
};
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
mainMenu.add(greenEntry);
callbackGray = new ActionListener() {
Checkbox checkbox = new Checkbox("Euro € Mail", new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
final Entry entry = (Entry) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.remove(entry);
System.err.println("POW");
}
};
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
// case does not matter
menuEntry.setShortcut('G');
menuEntry = this.systemTray.addEntry("Euro € Mail", GREEN_MAIL, callbackGreen);
// case does not matter
menuEntry.setShortcut('€');
this.systemTray.addSeparator();
final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
submenu.addEntry("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
submenu.setEnabled(false);
System.err.println("Am i checked? " + ((Checkbox) e.getSource()).getChecked());
}
});
checkbox.setShortcut('€');
mainMenu.add(checkbox);
mainMenu.add(new Separator());
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
mainMenu.add(submenu);
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
MenuItem source = (MenuItem) e.getSource();
source.getParent().setEnabled(false);
}
});
submenu.add(disableMenu);
// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however.
// submenu.addEntry("Add widget", GREEN_BUS, new Action() {
// submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
// @Override
// public
// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
// void actionPerformed(final ActionEvent e) {
// MenuItem source = (MenuItem) e.getSource();
// JProgressBar progressBar = new JProgressBar(0, 100);
// progressBar.setValue(new Random().nextInt(101));
// progressBar.setStringPainted(true);
// systemTray.addWidget(progressBar);
// source.getSystemTray().add(progressBar);
// }
// });
submenu.addEntry("Hide tray", LT_GRAY_BUS, new ActionListener() {
// }));
submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
systemTray.setEnabled(false);
}
});
submenu.addEntry("Remove menu", BLACK_FIRE, new ActionListener() {
}));
submenu.add(new MenuItem("Remove menu", BLACK_FIRE, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
submenu.remove();
MenuItem source = (MenuItem) e.getSource();
source.getParent().remove();
}
});
}));
systemTray.addEntry("Quit", new ActionListener() {
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
@ -178,6 +202,6 @@ class TestTrayJavaFX extends Application {
Platform.exit(); // necessary to close javaFx
//System.exit(0); not necessary if all non-daemon threads have stopped.
}
}).setShortcut('q'); // case does not matter
})).setShortcut('q'); // case does not matter
}
}

View File

@ -16,6 +16,7 @@
package dorkbox;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
@ -24,8 +25,10 @@ import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.SystemTray;
/**
@ -79,11 +82,29 @@ class TestTraySwt {
systemTray.setImage(LT_GRAY_TRAIN);
systemTray.setStatus("No Mail");
callbackGreen = new ActionListener() {
callbackGray = new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
final Entry entry = (Entry) e.getSource();
void actionPerformed(final ActionEvent e) {
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.getMenu().remove(entry);
System.err.println("POW");
}
};
Menu mainMenu = systemTray.getMenu();
MenuItem greenEntry = new MenuItem("Green Mail", new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
final MenuItem entry = (MenuItem) e.getSource();
systemTray.setStatus("Some Mail!");
systemTray.setImage(GREEN_TRAIN);
@ -92,84 +113,86 @@ class TestTraySwt {
entry.setText("Delete Mail");
// systemTray.remove(menuEntry);
}
};
});
greenEntry.setImage(GREEN_MAIL);
// case does not matter
greenEntry.setShortcut('G');
mainMenu.add(greenEntry);
callbackGray = new ActionListener() {
Checkbox checkbox = new Checkbox("Euro € Mail", new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
final Entry entry = (Entry) e.getSource();
systemTray.setStatus(null);
systemTray.setImage(BLACK_TRAIN);
entry.setCallback(null);
// systemTray.setStatus("Mail Empty");
systemTray.remove(entry);
System.err.println("POW");
}
};
Entry menuEntry = this.systemTray.addEntry("Green Mail", GREEN_MAIL, callbackGreen);
// case does not matter
menuEntry.setShortcut('G');
menuEntry = this.systemTray.addEntry("Euro € Mail", GREEN_MAIL, callbackGreen);
// case does not matter
menuEntry.setShortcut('€');
this.systemTray.addSeparator();
final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
submenu.addEntry("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
submenu.setEnabled(false);
void actionPerformed(final ActionEvent e) {
System.err.println("Am i checked? " + ((Checkbox) e.getSource()).getChecked());
}
});
checkbox.setShortcut('€');
mainMenu.add(checkbox);
mainMenu.add(new Separator());
Menu submenu = new Menu("Options", BLUE_CAMPING);
submenu.setShortcut('t');
mainMenu.add(submenu);
MenuItem disableMenu = new MenuItem("Disable menu", BLACK_BUS, new ActionListener() {
@Override
public
void actionPerformed(final ActionEvent e) {
MenuItem source = (MenuItem) e.getSource();
source.getParent().setEnabled(false);
}
});
submenu.add(disableMenu);
// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however.
// submenu.addEntry("Add widget", GREEN_BUS, new Action() {
// submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
// @Override
// public
// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
// void actionPerformed(final ActionEvent e) {
// MenuItem source = (MenuItem) e.getSource();
// JProgressBar progressBar = new JProgressBar(0, 100);
// progressBar.setValue(new Random().nextInt(101));
// progressBar.setStringPainted(true);
// systemTray.addWidget(progressBar);
// source.getSystemTray().add(progressBar);
// }
// });
submenu.addEntry("Hide tray", LT_GRAY_BUS, new ActionListener() {
// }));
submenu.add(new MenuItem("Hide tray", LT_GRAY_BUS, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
void actionPerformed(final ActionEvent e) {
systemTray.setEnabled(false);
}
});
submenu.addEntry("Remove menu", BLACK_FIRE, new ActionListener() {
}));
submenu.add(new MenuItem("Remove menu", BLACK_FIRE, new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
submenu.remove();
void actionPerformed(final ActionEvent e) {
MenuItem source = (MenuItem) e.getSource();
source.getParent().remove();
}
});
}));
systemTray.addEntry("Quit", new ActionListener() {
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
public
void actionPerformed(final java.awt.event.ActionEvent e) {
void actionPerformed(final ActionEvent e) {
systemTray.shutdown();
// necessary to shut down SWT
display.asyncExec(new Runnable() {
public void run() {
shell.dispose();
}
});
//System.exit(0); not necessary if all non-daemon threads have stopped.
}
}).setShortcut('q'); // case does not matter
})).setShortcut('q'); // case does not matter
shell.pack();