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 00000000..801d2c55 Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete.png differ diff --git a/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete@2x.png b/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete@2x.png new file mode 100644 index 00000000..fe1f717e Binary files /dev/null and b/MasterPassword/Java/masterpassword-gui/src/main/resources/media/icon_delete@2x.png differ 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() ); + } }