Skip to content

Commit

Permalink
Issue 25: The feature "minimize to tray" is implemented
Browse files Browse the repository at this point in the history
Created separate ActionListeners for About and Exit actions;
Created TrayPopup for the icon tray popup menu (it utilizes the actions mentioned above);
Created MainWindow listener that is responsible for the window hiding + restoration from/to the system tray;
Slightly formatted Javadocs of existing classes;

Not available:
No configuration for the feature (it is always enabled if the platform allows this);
  • Loading branch information
Vest committed May 13, 2015
1 parent 653e4b4 commit 3367f11
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 86 deletions.
32 changes: 20 additions & 12 deletions src/main/java/com/nilhcem/fakesmtp/gui/MainFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import com.nilhcem.fakesmtp.core.ArgsHandler;
import com.nilhcem.fakesmtp.core.Configuration;
import com.nilhcem.fakesmtp.core.exception.UncaughtExceptionHandler;
import com.nilhcem.fakesmtp.gui.listeners.MainWindowListener;
import com.nilhcem.fakesmtp.model.UIModel;
import com.nilhcem.fakesmtp.server.SMTPServerHandler;
import org.slf4j.LoggerFactory;

import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;

/**
Expand All @@ -20,47 +20,52 @@
* @author Nilhcem
* @since 1.0
*/
public final class MainFrame extends WindowAdapter {
public final class MainFrame {

private final JFrame mainFrame = new JFrame(Configuration.INSTANCE.get("application.title"));
private final MenuBar menu = new MenuBar();
private final MenuBar menu = new MenuBar(this);
private final MainPanel panel = new MainPanel(menu);

/**
* Creates the main window and makes it visible.
* <p>
* First, assigns the main panel to the default uncaught exception handler to display exceptions in this panel.<br><br>
* Before creating the main window, the application will have to set some elements, such as:
* </p>
* </p>
* <ul>
* <li>The minimum and default size;</li>
* <li>The menu bar and the main panel;</li>
* <li>An icon image;</li>
* <li>A shutdown hook to stop the server, once the main window is closed.</li>
* </ul><br>
* <p>
* <p>
* The icon of the application is a modified version from the one provided in "{@code WebAppers.com}"
* <i>(Creative Commons Attribution 3.0 License)</i>.
* </p>
* </p>
*/
public MainFrame() {
((UncaughtExceptionHandler) Thread.getDefaultUncaughtExceptionHandler()).setParentComponent(panel.get());
Dimension frameSize = new Dimension(Integer.parseInt(Configuration.INSTANCE.get("application.min.width")),
Integer.parseInt(Configuration.INSTANCE.get("application.min.height")));

Image iconImage = Toolkit.getDefaultToolkit().getImage(getClass().
getResource(Configuration.INSTANCE.get("application.icon.path")));

MainWindowListener windowListener = new MainWindowListener(this);

mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
mainFrame.addWindowListener(this); // for catching windowClosing event
mainFrame.addWindowListener(windowListener); // for catching windowClosing event
mainFrame.setSize(frameSize);
mainFrame.setMinimumSize(frameSize);

mainFrame.setJMenuBar(menu.get());
mainFrame.getContentPane().add(panel.get());
mainFrame.setLocationRelativeTo(null); // Center main frame
mainFrame.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().
getResource(Configuration.INSTANCE.get("application.icon.path"))));
mainFrame.setIconImage(iconImage);

// Add shutdown hook to stop server if enabled
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
SMTPServerHandler.INSTANCE.stopServer();
}
Expand All @@ -86,11 +91,11 @@ public void run() {
mainFrame.setVisible(true);
}

@Override
public void windowClosing(WindowEvent e) {
public void close() {
// Save configuration
Configuration.INSTANCE.set("smtp.default.port", panel.getPortText().get().getText());
Configuration.INSTANCE.set("emails.default.dir", panel.getSaveMsgTextField().get().getText());

try {
Configuration.INSTANCE.saveToUserProfile();
} catch (IOException ex) {
Expand All @@ -100,6 +105,9 @@ public void windowClosing(WindowEvent e) {
if (SMTPServerHandler.INSTANCE.getSmtpServer().isRunning()) {
SMTPServerHandler.INSTANCE.getSmtpServer().stop();
}

mainFrame.dispose();

System.exit(0);
}
}
84 changes: 10 additions & 74 deletions src/main/java/com/nilhcem/fakesmtp/gui/MenuBar.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package com.nilhcem.fakesmtp.gui;

import com.nilhcem.fakesmtp.core.ArgsHandler;
import com.nilhcem.fakesmtp.core.Configuration;
import com.nilhcem.fakesmtp.core.I18n;
import com.nilhcem.fakesmtp.gui.listeners.AboutActionListener;
import com.nilhcem.fakesmtp.gui.listeners.ExitActionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URI;
import java.util.Observable;

/**
Expand All @@ -31,13 +24,18 @@ public final class MenuBar extends Observable {

private final I18n i18n = I18n.INSTANCE;
private final JMenuBar menuBar = new JMenuBar();
private final MainFrame mainFrame;

private static final Logger LOGGER = LoggerFactory.getLogger(MenuBar.class);

/**
* Creates the menu bar and the different menus (file / edit / help).
*
* @param mainFrame MainFrame class required for the closing action.
*/
public MenuBar() {
public MenuBar(final MainFrame mainFrame) {
this.mainFrame = mainFrame;

menuBar.add(createFileMenu());
menuBar.add(createEditMenu());
menuBar.add(createHelpMenu());
Expand Down Expand Up @@ -66,12 +64,7 @@ private JMenu createFileMenu() {

JMenuItem exit = new JMenuItem(i18n.get("menubar.exit"));
exit.setMnemonic(i18n.get("menubar.mnemo.exit").charAt(0));
exit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
exit.addActionListener(new ExitActionListener(mainFrame));

fileMenu.add(exit);
return fileMenu;
Expand Down Expand Up @@ -121,66 +114,9 @@ private JMenu createHelpMenu() {

JMenuItem about = new JMenuItem(i18n.get("menubar.about"));
about.setMnemonic(i18n.get("menubar.mnemo.about").charAt(0));
about.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// for copying style
JLabel label = new JLabel();
Font font = label.getFont();

// create some css from the label's font
StringBuffer style = new StringBuffer("font-family:")
.append(font.getFamily()).append(";font-weight:");
if (font.isBold()) {
style.append("bold");
} else {
style.append("normal");
}
style.append(";font-size:").append(font.getSize()).append("pt;");

// html content
String link = i18n.get("menubar.about.dialog.link");
JEditorPane ep = new JEditorPane("text/html",
String.format("<html><body style=\"%s\">%s<br /><a href=\"%s\">%s</a></body></html>",
style, i18n.get("menubar.about.dialog"), link, link));

// handle link events
ep.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
MenuBar.launchUrl(e.getURL().toString());
}
}
});
ep.setEditable(false);
ep.setBackground(label.getBackground());

// show
JOptionPane.showMessageDialog(menuBar.getParent(), ep, String.format(i18n.get("menubar.about.title"),
Configuration.INSTANCE.get("application.name")), JOptionPane.INFORMATION_MESSAGE);
}
});
about.addActionListener(new AboutActionListener(menuBar.getParent()));

helpMenu.add(about);
return helpMenu;
}

/**
* Opens a web browser to launch the url specified in parameters.
*
* @param url the URL to launch.
*/
private static void launchUrl(String url) {
if (Desktop.isDesktopSupported()) {
try {
Desktop desktop = Desktop.getDesktop();
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(new URI(url));
}
} catch (Exception e) {
LOGGER.error("", e);
}
}
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/nilhcem/fakesmtp/gui/TrayPopup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.nilhcem.fakesmtp.gui;

import com.nilhcem.fakesmtp.core.I18n;
import com.nilhcem.fakesmtp.gui.listeners.AboutActionListener;
import com.nilhcem.fakesmtp.gui.listeners.ExitActionListener;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Provides the popup menu for the SystemTray icon.
*
* @author Vest
* @since 2.1
*/
public class TrayPopup {

private final I18n i18n = I18n.INSTANCE;
private static final Logger LOGGER = LoggerFactory.getLogger(TrayPopup.class);

private final PopupMenu popup = new PopupMenu();

/**
* The popup menu used by the icon in the system tray.
*
* @param mainFrame MainFrame class.
*/
public TrayPopup(final MainFrame mainFrame) {
// Create a popup menu components
final MenuItem aboutItem = new MenuItem(i18n.get("menubar.about"));
aboutItem.addActionListener(new AboutActionListener(null));

final MenuItem exitItem = new MenuItem(i18n.get("menubar.exit"));
exitItem.addActionListener(new ExitActionListener(mainFrame));

popup.add(aboutItem);
popup.addSeparator();
popup.add(exitItem);
}

/**
* Returns the PopupMenu object.
*
* @return the PopupMenu object.
*/
public PopupMenu get() {
return popup;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package com.nilhcem.fakesmtp.gui.listeners;

import com.nilhcem.fakesmtp.core.Configuration;
import com.nilhcem.fakesmtp.core.I18n;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URI;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Implements the About action.
*
* @author Vest
* @since 2.1
*/
public class AboutActionListener implements ActionListener {

private final I18n i18n = I18n.INSTANCE;
private final Container parent;

private static final Logger LOGGER = LoggerFactory.getLogger(AboutActionListener.class);

/**
* @param parent The parent container that is used for the About dialog
* window
*/
public AboutActionListener(final Container parent) {
this.parent = parent;
}

@Override
public void actionPerformed(ActionEvent e) {
// for copying style
final JLabel label = new JLabel();
final Font font = label.getFont();

// create some css from the label's font
final StringBuffer style = new StringBuffer("font-family:")
.append(font.getFamily())
.append(";font-weight:");
if (font.isBold()) {
style.append("bold");
} else {
style.append("normal");
}
style.append(";font-size:")
.append(font.getSize())
.append("pt;");

// html content
final String link = i18n.get("menubar.about.dialog.link");
final JEditorPane ep = new JEditorPane("text/html",
String.format("<html><body style=\"%s\">%s<br /><a href=\"%s\">%s</a></body></html>",
style, i18n.get("menubar.about.dialog"), link, link));

// handle link events
ep.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
AboutActionListener.launchUrl(e.getURL().toString());
}
}
});
ep.setEditable(false);
ep.setBackground(label.getBackground());

// show
JOptionPane.showMessageDialog(parent, ep, String.format(i18n.get("menubar.about.title"),
Configuration.INSTANCE.get("application.name")), JOptionPane.INFORMATION_MESSAGE);
}

/**
* Opens a web browser to launch the URL specified in parameters.
*
* @param url the URL to launch.
*/
private static void launchUrl(String url) {
if (Desktop.isDesktopSupported()) {
try {
Desktop desktop = Desktop.getDesktop();
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(new URI(url));
}
} catch (final Exception e) {
LOGGER.error("", e);
}
}
}
}
Loading

0 comments on commit 3367f11

Please sign in to comment.