Global hotkey, iconifying and application activation, help text.
This commit is contained in:
parent
f5c0c4d787
commit
3fc8acba70
@ -11,6 +11,7 @@ dependencies {
|
||||
implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
|
||||
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
|
||||
implementation group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0'
|
||||
implementation group: 'com.github.tulskiy', name: 'jkeymaster', version: '1.2'
|
||||
|
||||
compile project( ':masterpassword-model' )
|
||||
}
|
||||
|
@ -4,6 +4,10 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.gui.util.Platform;
|
||||
import com.lyndir.masterpassword.gui.util.Res;
|
||||
import com.lyndir.masterpassword.gui.view.MasterPasswordFrame;
|
||||
import com.tulskiy.keymaster.common.Provider;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
@ -18,9 +22,18 @@ public class GUI {
|
||||
public GUI() {
|
||||
Platform.get().installAppForegroundHandler( this::open );
|
||||
Platform.get().installAppReopenHandler( this::open );
|
||||
|
||||
KeyStroke keyStroke = KeyStroke.getKeyStroke( KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK );
|
||||
Provider.getCurrentProvider( true ).register( keyStroke, hotKey -> open() );
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Res.ui( () -> frame.setVisible( true ) );
|
||||
Res.ui( () -> {
|
||||
frame.setAlwaysOnTop( true );
|
||||
frame.setVisible( true );
|
||||
frame.setExtendedState( Frame.NORMAL );
|
||||
frame.setAlwaysOnTop( false );
|
||||
Platform.get().requestForeground();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,12 @@
|
||||
|
||||
package com.lyndir.masterpassword.gui.util;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
@ -30,9 +32,9 @@ import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.DefaultFormatterFactory;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
|
||||
/**
|
||||
@ -41,6 +43,8 @@ import javax.swing.text.DefaultFormatterFactory;
|
||||
@SuppressWarnings({ "SerializableStoresNonSerializable", "serial" })
|
||||
public abstract class Components {
|
||||
|
||||
private static final Logger logger = Logger.get( Components.class );
|
||||
|
||||
public static final float TEXT_SIZE_HEADING = 19f;
|
||||
public static final float TEXT_SIZE_CONTROL = 13f;
|
||||
public static final int SIZE_MARGIN = 12;
|
||||
@ -100,11 +104,11 @@ public abstract class Components {
|
||||
showDialog( dialog );
|
||||
|
||||
Object selectedValue = pane.getValue();
|
||||
if(selectedValue == null)
|
||||
if (selectedValue == null)
|
||||
return JOptionPane.CLOSED_OPTION;
|
||||
|
||||
Object[] options = pane.getOptions();
|
||||
if(options == null)
|
||||
if (options == null)
|
||||
return (selectedValue instanceof Integer)? (Integer) selectedValue: JOptionPane.CLOSED_OPTION;
|
||||
|
||||
int option = Arrays.binarySearch( options, selectedValue );
|
||||
@ -337,7 +341,7 @@ public abstract class Components {
|
||||
BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
|
||||
DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory();
|
||||
if (model instanceof UnsignedIntegerModel)
|
||||
formatterFactory.setDefaultFormatter( ((UnsignedIntegerModel)model).getFormatter() );
|
||||
formatterFactory.setDefaultFormatter( ((UnsignedIntegerModel) model).getFormatter() );
|
||||
((DefaultEditor) getEditor()).getTextField().setFormatterFactory( formatterFactory );
|
||||
((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
|
||||
setAlignmentX( LEFT_ALIGNMENT );
|
||||
@ -474,6 +478,24 @@ public abstract class Components {
|
||||
};
|
||||
}
|
||||
|
||||
public static JEditorPane linkLabel(@NonNls final String html) {
|
||||
return new JEditorPane( "text/html", "<html><body style='width:640'>" + html ) {
|
||||
{
|
||||
setOpaque( false );
|
||||
setEditable( false );
|
||||
addHyperlinkListener( event -> {
|
||||
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
|
||||
try {
|
||||
Platform.get().open( event.getURL().toURI() );
|
||||
}
|
||||
catch (final URISyntaxException e) {
|
||||
logger.err( e, "After triggering hyperlink: %s", event );
|
||||
}
|
||||
} );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class GradientPanel extends JPanel {
|
||||
|
||||
@Nullable
|
||||
|
@ -138,6 +138,10 @@ public abstract class Res {
|
||||
return icon( "media/icon_import.png" );
|
||||
}
|
||||
|
||||
public Icon help() {
|
||||
return icon( "media/icon_help.png" );
|
||||
}
|
||||
|
||||
public Icon export() {
|
||||
return icon( "media/icon_export.png" );
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ package com.lyndir.masterpassword.gui.util.platform;
|
||||
import com.apple.eawt.*;
|
||||
import com.apple.eio.FileManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Throwables;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
|
||||
|
||||
/**
|
||||
@ -13,7 +13,8 @@ import java.io.FileNotFoundException;
|
||||
*/
|
||||
public class ApplePlatform implements IPlatform {
|
||||
|
||||
static Application application = Preconditions.checkNotNull(
|
||||
private static final Logger logger = Logger.get( ApplePlatform.class );
|
||||
private static final Application application = Preconditions.checkNotNull(
|
||||
Application.getApplication(), "Not an Apple Java application." );
|
||||
|
||||
@Override
|
||||
@ -37,12 +38,31 @@ public class ApplePlatform implements IPlatform {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestForeground() {
|
||||
application.requestForeground( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean show(final File file) {
|
||||
try {
|
||||
return FileManager.revealInFinder( file );
|
||||
}
|
||||
catch (final FileNotFoundException ignored) {
|
||||
catch (final FileNotFoundException e) {
|
||||
logger.err( e, "While showing: %s", file );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(final URI url) {
|
||||
try {
|
||||
FileManager.openURL( url.toString() );
|
||||
return true;
|
||||
}
|
||||
catch (final IOException e) {
|
||||
logger.err( e, "While opening: %s", url );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.lyndir.masterpassword.gui.util.platform;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
|
||||
/**
|
||||
@ -18,8 +19,18 @@ public class BasePlatform implements IPlatform {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestForeground() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean show(final File file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(final URI url) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package com.lyndir.masterpassword.gui.util.platform;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
/**
|
||||
@ -12,5 +14,9 @@ public interface IPlatform {
|
||||
|
||||
boolean installAppReopenHandler(Runnable handler);
|
||||
|
||||
boolean requestForeground();
|
||||
|
||||
boolean show(File file);
|
||||
|
||||
boolean open(URI url);
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
package com.lyndir.masterpassword.gui.util.platform;
|
||||
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import java.awt.*;
|
||||
import java.awt.desktop.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
|
||||
/**
|
||||
@ -11,9 +14,12 @@ import java.io.File;
|
||||
@SuppressWarnings("Since15")
|
||||
public class JDK9Platform implements IPlatform {
|
||||
|
||||
private static final Logger logger = Logger.get( JDK9Platform.class );
|
||||
private static final Desktop desktop = Desktop.getDesktop();
|
||||
|
||||
@Override
|
||||
public boolean installAppForegroundHandler(final Runnable handler) {
|
||||
Desktop.getDesktop().addAppEventListener( new AppForegroundListener() {
|
||||
desktop.addAppEventListener( new AppForegroundListener() {
|
||||
@Override
|
||||
public void appRaisedToForeground(final AppForegroundEvent e) {
|
||||
handler.run();
|
||||
@ -28,7 +34,13 @@ public class JDK9Platform implements IPlatform {
|
||||
|
||||
@Override
|
||||
public boolean installAppReopenHandler(final Runnable handler) {
|
||||
Desktop.getDesktop().addAppEventListener( (AppReopenedListener) e -> handler.run() );
|
||||
desktop.addAppEventListener( (AppReopenedListener) e -> handler.run() );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requestForeground() {
|
||||
desktop.requestForeground( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -37,7 +49,19 @@ public class JDK9Platform implements IPlatform {
|
||||
if (!file.exists())
|
||||
return false;
|
||||
|
||||
Desktop.getDesktop().browseFileDirectory( file );
|
||||
desktop.browseFileDirectory( file );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open(final URI url) {
|
||||
try {
|
||||
desktop.browse( url );
|
||||
return true;
|
||||
}
|
||||
catch (final IOException e) {
|
||||
logger.err( e, "While opening: %s", url );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
"Add a new user to Master Password." );
|
||||
private final JButton importButton = Components.button( Res.icons().import_(), event -> importUser(),
|
||||
"Import a user from a backup file into Master Password." );
|
||||
private final JButton helpButton = Components.button( Res.icons().help(), event -> showHelp(),
|
||||
"Show information on how to use Master Password." );
|
||||
|
||||
private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS );
|
||||
private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS );
|
||||
@ -128,7 +130,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
}
|
||||
|
||||
private void addUser() {
|
||||
JTextField nameField = Components.textField( "Robert Lee Mitchell", null );
|
||||
JTextField nameField = Components.textField( "Robert Lee Mitchell", null );
|
||||
JCheckBox incognitoField = Components.checkBox( "<html>Incognito <em>(Do not save this user to disk)</em></html>" );
|
||||
if (JOptionPane.OK_OPTION != Components.showDialog( this, "Add User", new JOptionPane( Components.panel(
|
||||
BoxLayout.PAGE_AXIS,
|
||||
@ -208,7 +210,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
this, strf( "<html>Couldn't read import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ),
|
||||
"Import Failed", JOptionPane.ERROR_MESSAGE );
|
||||
}
|
||||
catch (MPMarshalException e) {
|
||||
catch (final MPMarshalException e) {
|
||||
logger.err( e, "While parsing user import file." );
|
||||
JOptionPane.showMessageDialog(
|
||||
this, strf( "<html>Couldn't parse import file:<br><pre>%s</pre></html>.", e.getLocalizedMessage() ),
|
||||
@ -216,6 +218,31 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
}
|
||||
}
|
||||
|
||||
private void showHelp() {
|
||||
JOptionPane.showMessageDialog( this, Components.linkLabel( strf(
|
||||
"<h1>Master Password</h1>"
|
||||
+ "<p>The primary goal of this application is to provide a reliable security solution that also "
|
||||
+ "makes you independent from your computer. If you lose access to this computer or your data, "
|
||||
+ "the application can regenerate all your secrets from scratch on any new device.</p>"
|
||||
+ "<h2>Opening Master Password</h2>"
|
||||
+ "<p>To use Master Password, simply open the application on your computer. "
|
||||
+ "Once running, you can bring up the user interface at any time by pressing the keys "
|
||||
+ "<strong><code>%s + %s + p</code></strong>."
|
||||
+ "<h2>Persistence</h2>"
|
||||
+ "<p>Though at the core, Master Password does not require the use of any form of data "
|
||||
+ "storage, the application does remember the names of the sites you've used in the past to "
|
||||
+ "make it easier for you to use them again in the future. All user information is saved in "
|
||||
+ "files on your computer at the following location:<br><pre>%s</pre></p>"
|
||||
+ "<p>You can read, modify, backup or place new files in this location as you see fit. "
|
||||
+ "Some people even configure this location to be synced between their different computers "
|
||||
+ "using services such as those provided by SpiderOak or Dropbox.</p>"
|
||||
+ "<hr><p><a href='https://masterpassword.app'>https://masterpassword.app</a> — by Maarten Billemont</p>",
|
||||
KeyEvent.getKeyText( KeyEvent.VK_CONTROL ),
|
||||
KeyEvent.getKeyText( KeyEvent.VK_META ),
|
||||
MPFileUserManager.get().getPath().getAbsolutePath() ) ),
|
||||
"About Master Password", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
|
||||
private enum ContentMode {
|
||||
NO_USER,
|
||||
AUTHENTICATE,
|
||||
@ -239,6 +266,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
|
||||
userToolbar.add( addButton );
|
||||
userToolbar.add( importButton );
|
||||
userToolbar.add( Box.createGlue() );
|
||||
userToolbar.add( helpButton );
|
||||
|
||||
add( Box.createGlue() );
|
||||
add( Components.heading( "Select a user to proceed." ) );
|
||||
@ -275,6 +304,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
userToolbar.add( exportButton );
|
||||
userToolbar.add( deleteButton );
|
||||
userToolbar.add( resetButton );
|
||||
userToolbar.add( Box.createGlue() );
|
||||
userToolbar.add( helpButton );
|
||||
|
||||
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
|
||||
add( Components.strut() );
|
||||
@ -461,6 +492,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
userToolbar.add( addButton );
|
||||
userToolbar.add( userButton );
|
||||
userToolbar.add( logoutButton );
|
||||
userToolbar.add( Box.createGlue() );
|
||||
userToolbar.add( helpButton );
|
||||
|
||||
siteToolbar.add( settingsButton );
|
||||
siteToolbar.add( questionsButton );
|
||||
@ -615,8 +648,14 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener,
|
||||
|
||||
Res.ui( () -> {
|
||||
Window window = SwingUtilities.windowForComponent( UserContentPanel.this );
|
||||
if (window != null)
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
||||
if (window instanceof Frame) {
|
||||
((Frame) window).setExtendedState( Frame.ICONIFIED );
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_DEACTIVATED ) );
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_ICONIFIED ) );
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_LOST_FOCUS ) );
|
||||
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSED ) );
|
||||
}
|
||||
// window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_ICONIFIED ) );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Loading…
Reference in New Issue
Block a user