From 6819a2ace54ed11445b407cc7747aa8da794423f Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Wed, 1 Apr 2015 08:09:48 -0400 Subject: [PATCH] Delete users + window resize fix. [ADDED] Java: support for deleting users. [FIXED] Java: Password window resizing issue. --- .../gui/ModelAuthenticationPanel.java | 33 +++++++++++++-- .../masterpassword/gui/PasswordFrame.java | 12 +++--- .../com/lyndir/masterpassword/gui/Res.java | 4 ++ .../src/main/resources/media/icon_delete.png | Bin 0 -> 1372 bytes .../main/resources/media/icon_delete@2x.png | Bin 0 -> 3338 bytes .../model/MPUserFileManager.java | 39 +++++++++++++++++- .../masterpassword/model/MPUserManager.java | 10 ++++- 7 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete.png create mode 100644 MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete@2x.png diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java index 0f37d704..d7bbaf79 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/ModelAuthenticationPanel.java @@ -1,5 +1,7 @@ package com.lyndir.masterpassword.gui; +import static com.lyndir.lhunath.opal.system.util.StringUtils.strf; + import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.*; @@ -53,14 +55,14 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite userField.setFont( Res.valueFont().deriveFont( 12f ) ); userField.addItemListener( this ); userField.addActionListener( this ); - userField.setEditor(new MetalComboBoxEditor() { + userField.setEditor( new MetalComboBoxEditor() { @Override protected JTextField createEditorComponent() { JTextField editorComponents = Components.textField(); - editorComponents.setForeground(Color.red); + editorComponents.setForeground( Color.red ); return editorComponents; } - }); + } ); add( userField ); add( Components.stud() ); @@ -128,13 +130,36 @@ public class ModelAuthenticationPanel extends AuthenticationPanel implements Ite } ); setToolTipText( "Add a new user to the list." ); } + }, new JButton( Res.iconDelete() ) { + { + addActionListener( new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + ModelUser deleteUser = getSelectedUser(); + if (deleteUser == null) + return; + + if (JOptionPane.showConfirmDialog( ModelAuthenticationPanel.this, // + strf( "Are you sure you want to delete the user and sites remembered for:\n%s.", + deleteUser.getFullName() ), // + "Delete User", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE ) == JOptionPane.CANCEL_OPTION) + return; + + MPUserFileManager.get().deleteUser( deleteUser.getModel() ); + userField.setModel( new DefaultComboBoxModel<>( readConfigUsers() ) ); + updateUser( true ); + } + } ); + setToolTipText( "Delete the selected user." ); + } }, new JButton( Res.iconQuestion() ) { { addActionListener( new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { JOptionPane.showMessageDialog( ModelAuthenticationPanel.this, // - "Reads users and sites from the directory at ~/.mpw.d.", // + strf( "Reads users and sites from the directory at:\n%s", + MPUserFileManager.get().getPath().getAbsolutePath() ), // "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java index 54ea2583..aa03a72a 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/PasswordFrame.java @@ -42,7 +42,8 @@ public class PasswordFrame extends JFrame implements DocumentListener { this.user = user; setDefaultCloseOperation( DISPOSE_ON_CLOSE ); - setContentPane( root = Components.gradientPanel( new BorderLayout( 20, 20 ), Res.colors().frameBg() ) ); + setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) ); + root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) ); root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); // Site @@ -50,7 +51,7 @@ public class PasswordFrame extends JFrame implements DocumentListener { sitePanel.setOpaque( true ); sitePanel.setBackground( Res.colors().controlBg() ); sitePanel.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) ); - add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ), BorderLayout.CENTER ); + root.add( Components.borderPanel( sitePanel, BorderFactory.createRaisedBevelBorder(), Res.colors().frameBg() ) ); // User sitePanel.add( Components.label( strf( "Generating passwords for: %s", user.getFullName() ), SwingConstants.CENTER ) ); @@ -163,12 +164,13 @@ public class PasswordFrame extends JFrame implements DocumentListener { // Tip tipLabel = Components.label( " ", SwingConstants.CENTER ); tipLabel.setAlignmentX( Component.CENTER_ALIGNMENT ); - JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, passwordField, tipLabel ); + JPanel passwordContainer = Components.boxLayout( BoxLayout.PAGE_AXIS, maskPasswordField, Box.createGlue(), passwordField, Box.createGlue(), tipLabel ); passwordContainer.setOpaque( true ); passwordContainer.setBackground( Color.white ); passwordContainer.setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 ) ); - add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ), - BorderLayout.SOUTH ); + root.add( Box.createVerticalStrut( 8 ) ); + root.add( Components.borderPanel( passwordContainer, BorderFactory.createLoweredSoftBevelBorder(), Res.colors().frameBg() ), + BorderLayout.SOUTH ); pack(); setMinimumSize( new Dimension( Math.max( 600, getPreferredSize().width ), Math.max( 300, getPreferredSize().height ) ) ); diff --git a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java index a76b1a9f..a13011d5 100644 --- a/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java +++ b/MasterPassword/Java/masterpassword-gui/src/main/java/com/lyndir/masterpassword/gui/Res.java @@ -85,6 +85,10 @@ public abstract class Res { return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) ); } + public static Icon iconDelete() { + return new RetinaIcon( Resources.getResource( "media/icon_delete@2x.png" ) ); + } + public static Icon iconQuestion() { return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) ); } diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete.png b/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..801d2c5587f4298bca743d1ec15a9117e549457b GIT binary patch literal 1372 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 Ua87NTQ~&?~07*qoM6N<$f+JOI=>Px# literal 0 HcmV?d00001 diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java index d3c5b749..df0d0873 100644 --- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java +++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserFileManager.java @@ -5,6 +5,7 @@ import com.google.common.collect.*; import com.google.common.io.CharSink; import com.lyndir.lhunath.opal.system.logging.Logger; import java.io.*; +import java.util.SortedSet; import javax.annotation.Nullable; @@ -15,7 +16,7 @@ public class MPUserFileManager extends MPUserManager { @SuppressWarnings("UnusedDeclaration") private static final Logger logger = Logger.get( MPUserFileManager.class ); - private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" ); + private static final File mpwd = new File( System.getProperty( "user.home" ), ".mpw.d" ); private static final MPUserFileManager instance; static { @@ -70,19 +71,53 @@ public class MPUserFileManager extends MPUserManager { } ).filter( Predicates.notNull() ); } + @Override + public void addUser(final MPUser user) { + super.addUser( user ); + save(); + } + + @Override + public void deleteUser(final MPUser user) { + super.deleteUser( user ); + save(); + } + + /** + * Write the current user state to disk. + */ public void save() { + // Save existing users. for (final MPUser user : getUsers()) try { new CharSink() { @Override public Writer openStream() throws IOException { - return new FileWriter( new File(userFilesDirectory, user.getFullName() + ".mpsites" ) ); + return new FileWriter( new File( userFilesDirectory, user.getFullName() + ".mpsites" ) ); } }.write( MPSiteMarshaller.marshallSafe( user ).getExport() ); } catch (IOException e) { logger.err( e, "Unable to save sites for user: %s", user ); } + + // Remove deleted users. + for (File userFile : userFilesDirectory.listFiles( new FilenameFilter() { + @Override + public boolean accept(final File dir, final String name) { + return name.endsWith( ".mpsites" ); + } + } )) + if (getUserNamed( userFile.getName().replaceFirst( "\\.mpsites$", "" ) ) == null) + if (!userFile.delete()) + logger.err( "Couldn't delete file: %s", userFile ); + } + + /** + * @return The location on the file system where the user models are stored. + */ + public File getPath() { + return mpwd; } } diff --git a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java index 807d727c..246be31e 100644 --- a/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java +++ b/MasterPassword/Java/masterpassword-model/src/main/java/com/lyndir/masterpassword/model/MPUserManager.java @@ -18,14 +18,22 @@ public abstract class MPUserManager { protected MPUserManager(final Iterable users) { for (MPUser user : users) - addUser( user ); + usersByName.put( user.getFullName(), user ); } public SortedSet getUsers() { return FluentIterable.from( usersByName.values() ).toSortedSet( Ordering.natural() ); } + public MPUser getUserNamed(String fullName) { + return usersByName.get( fullName ); + } + public void addUser(final MPUser user) { usersByName.put( user.getFullName(), user ); } + + public void deleteUser(final MPUser user) { + usersByName.remove( user.getFullName() ); + } }