From 6f2dd84a31edf868f587a00a3316278e64d6cfe6 Mon Sep 17 00:00:00 2001 From: "Michael Ziminsky (Z)" Date: Thu, 25 Oct 2018 13:57:39 -0700 Subject: [PATCH 1/3] Java Client: Separate user and application preferences --- .../lyndir/masterpassword/gui/util/Res.java | 4 +++ .../gui/view/UserContentPanel.java | 31 ++++++++++++------ .../gui/src/main/resources/media/prefs.png | Bin 0 -> 2827 bytes .../gui/src/main/resources/media/prefs@2x.png | Bin 0 -> 7909 bytes 4 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 platform-independent/java/gui/src/main/resources/media/prefs.png create mode 100644 platform-independent/java/gui/src/main/resources/media/prefs@2x.png diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java index 2f9e7671..f75a519b 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java @@ -165,6 +165,10 @@ public abstract class Res { return icon( "media/icon_key.png" ); } + public Icon prefs() { + return icon( "media/prefs.png" ); + } + public Icon avatar(final int index) { return icon( strf( "media/avatar-%d.png", index % avatars() ) ); } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java index 21714b15..25bb3b2b 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java @@ -55,6 +55,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, "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 JButton prefsButton = Components.button( Res.icons().prefs(), event -> showAppPreferences(), + "Show application preferences." ); private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS ); private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS ); @@ -248,6 +250,17 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, "About Master Password", JOptionPane.INFORMATION_MESSAGE ); } + private void showAppPreferences() { + Component checkForUpdates = Components.checkBox("Check For Updates", MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates); + + Component stayResident = Components.checkBox(strf("Stay Resident (reactivate with %s+%s)", + InputEvent.getModifiersExText(MPGuiConstants.ui_hotkey.getModifiers()), + KeyEvent.getKeyText(MPGuiConstants.ui_hotkey.getKeyCode())), + MPGuiConfig.get().stayResident(), MPGuiConfig.get()::setStayResident); + + Components.showDialog(this, "Application Preferences", new JOptionPane(Components.panel(BoxLayout.PAGE_AXIS, checkForUpdates, stayResident))); + } + private enum ContentMode { NO_USER, AUTHENTICATE, @@ -274,6 +287,9 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, userToolbar.add( Box.createGlue() ); userToolbar.add( helpButton ); + siteToolbar.add(Box.createGlue()); + siteToolbar.add(prefsButton); + add( Box.createGlue() ); add( Components.heading( "Select a user to proceed." ) ); add( Box.createGlue() ); @@ -312,6 +328,9 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, userToolbar.add( Box.createGlue() ); userToolbar.add( helpButton ); + siteToolbar.add(Box.createGlue()); + siteToolbar.add(prefsButton); + add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); add( Components.strut() ); @@ -512,6 +531,8 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, siteToolbar.add( editButton ); siteToolbar.add( keyButton ); siteToolbar.add( deleteButton ); + siteToolbar.add(Box.createGlue()); + siteToolbar.add(prefsButton); settingsButton.setEnabled( false ); questionsButton.setEnabled( false ); editButton.setEnabled( false ); @@ -586,16 +607,6 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, components.add( Components.checkBox( "Hide Passwords", user.getPreferences().isHidePasswords(), user.getPreferences()::setHidePasswords ) ); - components.add( new JSeparator() ); - - components.add( Components.checkBox( "Check For Updates", - MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates ) ); - - components.add( Components.checkBox( strf( "Stay Resident (reactivate with %s+%s)", - InputEvent.getModifiersExText( MPGuiConstants.ui_hotkey.getModifiers() ), - KeyEvent.getKeyText( MPGuiConstants.ui_hotkey.getKeyCode() ) ), - MPGuiConfig.get().stayResident(), MPGuiConfig.get()::setStayResident ) ); - Components.showDialog( this, user.getFullName(), new JOptionPane( Components.panel( BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) ); } diff --git a/platform-independent/java/gui/src/main/resources/media/prefs.png b/platform-independent/java/gui/src/main/resources/media/prefs.png new file mode 100644 index 0000000000000000000000000000000000000000..9717aea77d81c0f052f505d651e078eb4d8cfd0f GIT binary patch literal 2827 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 d|Hb;><8Kf3d<;Xn3w{6q002ovPDHLkV1f> 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 P00000NkvXXu0mjfd4WTw literal 0 HcmV?d00001 From 2794ea08e4096b31a33b1e3791b695c9677b76c7 Mon Sep 17 00:00:00 2001 From: "Michael Ziminsky (Z)" Date: Thu, 25 Oct 2018 18:19:09 -0700 Subject: [PATCH 2/3] Java Client: Add new user preference for default login type --- .../masterpassword/gui/view/UserContentPanel.java | 7 ++++++- .../masterpassword/model/MPUserPreferences.java | 4 ++++ .../masterpassword/model/impl/MPBasicSite.java | 4 ++-- .../model/impl/MPBasicUserPreferences.java | 12 ++++++++++++ .../lyndir/masterpassword/model/impl/MPFileUser.java | 9 +++++---- .../model/impl/MPFileUserPreferences.java | 12 +++++++++++- .../masterpassword/model/impl/MPFlatMarshaller.java | 1 + .../model/impl/MPFlatUnmarshaller.java | 7 +++++-- .../lyndir/masterpassword/model/impl/MPJSONFile.java | 9 ++++----- 9 files changed, 50 insertions(+), 15 deletions(-) diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java index 25bb3b2b..8ba63405 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java @@ -604,6 +604,11 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, user.getPreferences().getDefaultType(), user.getPreferences()::setDefaultType ), Components.strut() ); + components.add( Components.label( "Default Login Type:" ), + Components.comboBox( MPResultType.values(), MPResultType::getLongName, + user.getPreferences().getDefaultLoginType(), user.getPreferences()::setDefaultLoginType), + Components.strut() ); + components.add( Components.checkBox( "Hide Passwords", user.getPreferences().isHidePasswords(), user.getPreferences()::setHidePasswords ) ); @@ -639,7 +644,7 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, components.add( Components.label( "Login Type:" ), Components.comboBox( MPResultType.values(), type -> - getTypeDescription( type, user.getAlgorithm().mpw_default_login_type() ), + getTypeDescription( type, user.getPreferences().getDefaultLoginType() ), site.getLoginType(), site::setLoginType ), Components.strut() ); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserPreferences.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserPreferences.java index d7bab23f..5749335c 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserPreferences.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/MPUserPreferences.java @@ -13,6 +13,10 @@ public interface MPUserPreferences { void setDefaultType(@Nullable MPResultType defaultType); + MPResultType getDefaultLoginType(); + + void setDefaultLoginType(@Nullable MPResultType defaultLoginType); + boolean isHidePasswords(); void setHidePasswords(boolean hidePasswords); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java index 9b646d15..e4e177c4 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicSite.java @@ -56,7 +56,7 @@ public abstract class MPBasicSite, Q extends MPQuestion> ext this.algorithm = (algorithm != null)? algorithm: this.user.getAlgorithm(); this.counter = (counter != null)? counter: this.algorithm.mpw_default_counter(); this.resultType = (resultType != null)? resultType: this.user.getPreferences().getDefaultType(); - this.loginType = (loginType != null)? loginType: this.algorithm.mpw_default_login_type(); + this.loginType = (loginType != null)? loginType: this.user.getPreferences().getDefaultLoginType(); } // - Meta @@ -125,7 +125,7 @@ public abstract class MPBasicSite, Q extends MPQuestion> ext if (this.loginType == loginType) return; - this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() ); + this.loginType = ifNotNullElse( loginType, this.user.getPreferences().getDefaultLoginType() ); setChanged(); } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUserPreferences.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUserPreferences.java index a32363f7..b69d3410 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUserPreferences.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPBasicUserPreferences.java @@ -14,6 +14,8 @@ public class MPBasicUserPreferences> implements MPUserP @Nullable private MPResultType defaultType; + @Nullable + private MPResultType defaultLoginType; private boolean hidePasswords; public MPBasicUserPreferences(final U user) { @@ -34,6 +36,16 @@ public class MPBasicUserPreferences> implements MPUserP this.defaultType = defaultType; } + @Override + public MPResultType getDefaultLoginType() { + return (defaultLoginType != null) ? defaultLoginType : user.getAlgorithm().mpw_default_login_type(); + } + + @Override + public void setDefaultLoginType(@Nullable final MPResultType defaultLoginType) { + this.defaultLoginType = defaultLoginType; + } + @Override public boolean isHidePasswords() { return hidePasswords; diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java index d05ece6d..57081991 100755 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUser.java @@ -62,18 +62,19 @@ public class MPFileUser extends MPBasicUser { } public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final File location) { - this( fullName, keyID, algorithm, 0, null, new Instant(), false, + this( fullName, keyID, algorithm, 0, null, null, new Instant(), false, MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.DEFAULT, location ); } public MPFileUser(final String fullName, @Nullable final byte[] keyID, final MPAlgorithm algorithm, final int avatar, - @Nullable final MPResultType defaultType, final ReadableInstant lastUsed, final boolean hidePasswords, - final MPMarshaller.ContentMode contentMode, final MPMarshalFormat format, final File location) { + @Nullable final MPResultType defaultType, @Nullable final MPResultType defaultLoginType, + final ReadableInstant lastUsed, final boolean hidePasswords, final MPMarshaller.ContentMode contentMode, + final MPMarshalFormat format, final File location) { super( avatar, fullName, algorithm ); this.keyID = (keyID != null)? keyID.clone(): null; this.lastUsed = lastUsed; - this.preferences = new MPFileUserPreferences( this, defaultType, hidePasswords ); + this.preferences = new MPFileUserPreferences( this, defaultType, defaultLoginType, hidePasswords); this.format = format; this.contentMode = contentMode; diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserPreferences.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserPreferences.java index b800f0c6..acf1a666 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserPreferences.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserPreferences.java @@ -10,10 +10,11 @@ import javax.annotation.Nullable; */ public class MPFileUserPreferences extends MPBasicUserPreferences { - public MPFileUserPreferences(final MPFileUser user, @Nullable final MPResultType defaultType, final boolean hidePasswords) { + public MPFileUserPreferences(final MPFileUser user, @Nullable final MPResultType defaultType, @Nullable final MPResultType defaultLoginType, final boolean hidePasswords) { super( user ); setDefaultType( defaultType ); + setDefaultLoginType(defaultLoginType); setHidePasswords( hidePasswords ); } @@ -26,6 +27,15 @@ public class MPFileUserPreferences extends MPBasicUserPreferences { getUser().setChanged(); } + @Override + public void setDefaultLoginType(@Nullable final MPResultType defaultLoginType) { + if (getDefaultLoginType() == defaultLoginType) + return; + + super.setDefaultLoginType(defaultLoginType); + getUser().setChanged(); + } + @Override public void setHidePasswords(final boolean hidePasswords) { if (Objects.equals( isHidePasswords(), hidePasswords )) diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java index b3cca47c..aac53f3b 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatMarshaller.java @@ -57,6 +57,7 @@ public class MPFlatMarshaller implements MPMarshaller { content.append( "# Key ID: " ).append( user.exportKeyID() ).append( '\n' ); content.append( "# Algorithm: " ).append( user.getAlgorithm().version().toInt() ).append( '\n' ); content.append( "# Default Type: " ).append( user.getPreferences().getDefaultType().getType() ).append( '\n' ); + content.append( "# Default Login Type: " ).append( user.getPreferences().getDefaultLoginType().getType() ).append( '\n' ); content.append( "# Passwords: " ).append( user.getContentMode().name() ).append( '\n' ); content.append( "##\n" ); content.append( "#\n" ); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java index 9d6e290f..8a6e0bc9 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFlatUnmarshaller.java @@ -56,11 +56,12 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { int mpVersion = 0, avatar = 0; boolean clearContent = false, headerStarted = false; MPResultType defaultType = null; + MPResultType defaultLoginType = null; Instant date = null; //noinspection HardcodedLineSeparator for (final String line : CharStreams.readLines( reader )) - // Header delimitor. + // Header delimiter. if (line.startsWith( "##" )) { if (!headerStarted) // Starts the header. @@ -68,7 +69,7 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { else if ((fullName != null) && (keyID != null)) // Ends the header. return new MPFileUser( - fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(), avatar, defaultType, + fullName, keyID, MPAlgorithm.Version.fromInt( mpVersion ).getAlgorithm(), avatar, defaultType, defaultLoginType, date, false, clearContent? MPMarshaller.ContentMode.VISIBLE: MPMarshaller.ContentMode.PROTECTED, MPMarshalFormat.Flat, file ); @@ -93,6 +94,8 @@ public class MPFlatUnmarshaller implements MPUnmarshaller { clearContent = "visible".equalsIgnoreCase( value ); else if ("Default Type".equalsIgnoreCase( name )) defaultType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) ); + else if ("Default Login Type".equalsIgnoreCase( name )) + defaultLoginType = MPResultType.forType( ConversionUtils.toIntegerNN( value ) ); else if ("Date".equalsIgnoreCase( name )) date = MPModelConstants.dateTimeFormatter.parseDateTime( value ).toInstant(); } diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java index 5cb3b85a..0a1eee23 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPJSONFile.java @@ -20,11 +20,6 @@ package com.lyndir.masterpassword.model.impl; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.core.util.Separators; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.primitives.UnsignedInteger; import com.lyndir.lhunath.opal.system.CodeUtils; import com.lyndir.masterpassword.*; @@ -66,6 +61,7 @@ public class MPJSONFile extends MPJSONAnyObject { user._ext_mpw = new User.Ext() { { default_type = modelUser.getPreferences().getDefaultType(); + default_login_type = modelUser.getPreferences().getDefaultLoginType(); hide_passwords = modelUser.getPreferences().isHidePasswords(); } }; @@ -133,6 +129,7 @@ public class MPJSONFile extends MPJSONAnyObject { return new MPFileUser( user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar, (user._ext_mpw != null)? user._ext_mpw.default_type: null, + (user._ext_mpw != null)? user._ext_mpw.default_login_type : null, (user.last_used != null)? MPModelConstants.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(), (user._ext_mpw != null) && user._ext_mpw.hide_passwords, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE, @@ -211,6 +208,8 @@ public class MPJSONFile extends MPJSONAnyObject { @Nullable MPResultType default_type; + @Nullable + MPResultType default_login_type; boolean hide_passwords; } } From 8aaf3aec3cf86081ef9b4a9898147526fad19a75 Mon Sep 17 00:00:00 2001 From: "Michael Ziminsky (Z)" Date: Thu, 25 Oct 2018 23:03:00 -0700 Subject: [PATCH 3/3] Java Client: "Stay Resident" option improvements - Reworded "Stay Resident" option to be more clear about what it does - Added new option to keep user logged in when closing/hiding window - Made it possible to force exit by holding down ALT key while closing --- .../masterpassword/gui/MPGuiConfig.java | 10 +++++ .../masterpassword/gui/MPGuiConstants.java | 2 + .../gui/view/MasterPasswordFrame.java | 39 ++++++++++++------- .../gui/view/UserContentPanel.java | 11 +++++- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java index b58c5a86..926d7e0b 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConfig.java @@ -35,6 +35,7 @@ public class MPGuiConfig extends MPConfig { Boolean checkForUpdates; Boolean stayResident; + Boolean logoutOnClose; public boolean checkForUpdates() { return (checkForUpdates != null)? checkForUpdates: @@ -55,4 +56,13 @@ public class MPGuiConfig extends MPConfig { this.stayResident = stayResident; setChanged(); } + + public boolean logoutOnClose() { + return (logoutOnClose != null)? logoutOnClose: true; + } + + public void setLogoutOnClose(final boolean logoutOnClose) { + this.logoutOnClose = logoutOnClose; + setChanged(); + } } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConstants.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConstants.java index 54b48735..924167df 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConstants.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/MPGuiConstants.java @@ -11,4 +11,6 @@ import javax.swing.*; public final class MPGuiConstants { public static final KeyStroke ui_hotkey = KeyStroke.getKeyStroke( KeyEvent.VK_P, InputEvent.CTRL_DOWN_MASK | InputEvent.META_DOWN_MASK ); + + public static final int FORCE_CLOSE_KEY = KeyEvent.VK_ALT; } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java index 50cc607d..643ad381 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java @@ -2,6 +2,7 @@ package com.lyndir.masterpassword.gui.view; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.masterpassword.gui.MPGuiConfig; +import com.lyndir.masterpassword.gui.MPGuiConstants; import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.gui.util.Res; import com.lyndir.masterpassword.model.impl.MPFileUserManager; @@ -19,8 +20,6 @@ public class MasterPasswordFrame extends JFrame { private static final Logger logger = Logger.get( MasterPasswordFrame.class ); - private final UserContentPanel userContent; - @SuppressWarnings("MagicNumber") public MasterPasswordFrame() { super( "Master Password" ); @@ -32,14 +31,16 @@ public class MasterPasswordFrame extends JFrame { root.add( Components.strut() ); root.add( userPanel = Components.panel( new BorderLayout( 0, 0 ) ) ); + final UserContentPanel userContent = new UserContentPanel(); userPanel.add( Components.borderPanel( BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ), - Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userContent = new UserContentPanel() ), BorderLayout.CENTER ); + Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userContent), BorderLayout.CENTER ); userPanel.add( userContent.getUserToolbar(), BorderLayout.LINE_START ); userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END ); - addComponentListener( new ComponentHandler() ); - addWindowListener( new WindowHandler() ); + final WindowHandler windowHandler = new WindowHandler(); + addWindowListener(windowHandler); + KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(windowHandler); setPreferredSize( new Dimension( 800, 560 ) ); setDefaultCloseOperation( DISPOSE_ON_CLOSE ); pack(); @@ -48,22 +49,30 @@ public class MasterPasswordFrame extends JFrame { setLocationByPlatform( true ); } - private class ComponentHandler extends ComponentAdapter { - @Override - public void componentShown(final ComponentEvent e) { - MPFileUserManager.get().reload(); - userContent.transferFocus(); - } - } + private static class WindowHandler extends WindowAdapter implements KeyEventDispatcher { - - private static class WindowHandler extends WindowAdapter { + private boolean forceClose = false; @Override public void windowClosed(final WindowEvent e) { - if (!MPGuiConfig.get().stayResident()) + if (!MPGuiConfig.get().stayResident() || forceClose) { + System.exit( 0 ); + } + else if (MPGuiConfig.get().logoutOnClose()) { + + MPFileUserManager.get().reload(); + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent e) { + + if (e.getKeyCode() == MPGuiConstants.FORCE_CLOSE_KEY) + forceClose = (e.getID() == KeyEvent.KEY_PRESSED); + + return false; } } } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java index 8ba63405..fcff949d 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserContentPanel.java @@ -253,12 +253,19 @@ public class UserContentPanel extends JPanel implements MasterPassword.Listener, private void showAppPreferences() { Component checkForUpdates = Components.checkBox("Check For Updates", MPGuiConfig.get().checkForUpdates(), MPGuiConfig.get()::setCheckForUpdates); - Component stayResident = Components.checkBox(strf("Stay Resident (reactivate with %s+%s)", + JCheckBox stayResident = Components.checkBox(strf("Stay running in background when closed (reactivate with %s+%s)", InputEvent.getModifiersExText(MPGuiConstants.ui_hotkey.getModifiers()), KeyEvent.getKeyText(MPGuiConstants.ui_hotkey.getKeyCode())), MPGuiConfig.get().stayResident(), MPGuiConfig.get()::setStayResident); + stayResident.setToolTipText(strf("Hold %s while closing to force exit", KeyEvent.getKeyText(MPGuiConstants.FORCE_CLOSE_KEY))); - Components.showDialog(this, "Application Preferences", new JOptionPane(Components.panel(BoxLayout.PAGE_AXIS, checkForUpdates, stayResident))); + Component logoutOnClose = Components.checkBox("Logout current user on close", MPGuiConfig.get().logoutOnClose(), MPGuiConfig.get()::setLogoutOnClose); + + stayResident.addItemListener(e -> logoutOnClose.setEnabled(e.getStateChange() == ItemEvent.SELECTED)); + logoutOnClose.setEnabled(stayResident.isSelected()); + + Components.showDialog(this, "Application Preferences", + new JOptionPane(Components.panel(BoxLayout.PAGE_AXIS, checkForUpdates, stayResident, logoutOnClose))); } private enum ContentMode {