Adding and deleting users and sites.
This commit is contained in:
		@@ -83,6 +83,10 @@ public abstract class Components {
 | 
			
		||||
        return box;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel panel(@Nullable final LayoutManager layout) {
 | 
			
		||||
        return panel( layout, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel panel(@Nullable final LayoutManager layout, @Nullable final Color color) {
 | 
			
		||||
        return new GradientPanel( layout, color );
 | 
			
		||||
    }
 | 
			
		||||
@@ -223,6 +227,11 @@ public abstract class Components {
 | 
			
		||||
                if (actionListener != null)
 | 
			
		||||
                    actionListener.actionPerformed( e );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean isEnabled() {
 | 
			
		||||
                return actionListener != null;
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -233,6 +242,11 @@ public abstract class Components {
 | 
			
		||||
                if (actionListener != null)
 | 
			
		||||
                    actionListener.actionPerformed( e );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean isEnabled() {
 | 
			
		||||
                return actionListener != null;
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
        iconButton.setFocusable( false );
 | 
			
		||||
 | 
			
		||||
@@ -441,6 +455,9 @@ public abstract class Components {
 | 
			
		||||
 | 
			
		||||
        public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) {
 | 
			
		||||
            super( layout );
 | 
			
		||||
            if (getLayout() == null)
 | 
			
		||||
                setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
 | 
			
		||||
            setGradientColor( gradientColor );
 | 
			
		||||
            setBackground( null );
 | 
			
		||||
            setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
@@ -459,7 +476,7 @@ public abstract class Components {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void setBackground(@Nullable final Color bg) {
 | 
			
		||||
            super.setBackground( bg );
 | 
			
		||||
            updatePaint();
 | 
			
		||||
            setOpaque( bg != null );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
@@ -469,8 +486,6 @@ public abstract class Components {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void updatePaint() {
 | 
			
		||||
            setOpaque( (getGradientColor() != null) || (getBackground() != null) );
 | 
			
		||||
 | 
			
		||||
            if (gradientColor == null) {
 | 
			
		||||
                paint = null;
 | 
			
		||||
                return;
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,10 @@ public abstract class Res {
 | 
			
		||||
            return icon( "media/icon_user.png" );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Icon lock() {
 | 
			
		||||
            return icon( "media/icon_lock.png" );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Icon settings() {
 | 
			
		||||
            return icon( "media/icon_settings.png" );
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,12 @@ package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.masterpassword.util.Utilities.*;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableSortedSet;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.CollectionListModel;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
@@ -18,7 +20,7 @@ import javax.swing.*;
 | 
			
		||||
 * @author lhunath, 2018-07-14
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("serial")
 | 
			
		||||
public class FilesPanel extends JPanel {
 | 
			
		||||
public class FilesPanel extends JPanel implements MPFileUserManager.Listener {
 | 
			
		||||
 | 
			
		||||
    private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
 | 
			
		||||
 | 
			
		||||
@@ -48,11 +50,8 @@ public class FilesPanel extends JPanel {
 | 
			
		||||
 | 
			
		||||
        // User Selection
 | 
			
		||||
        add( userField );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        // TODO: Should we use a listener here instead?
 | 
			
		||||
        usersModel.set( MPFileUserManager.get().reload() );
 | 
			
		||||
        MPFileUserManager.get().addListener( this );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean addListener(final Listener listener) {
 | 
			
		||||
@@ -75,6 +74,11 @@ public class FilesPanel extends JPanel {
 | 
			
		||||
            listener.onUserSelected( user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onFilesUpdated(final ImmutableSortedSet<MPFileUser> files) {
 | 
			
		||||
        usersModel.set( files );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface Listener {
 | 
			
		||||
 | 
			
		||||
        void onUserSelected(@Nullable MPUser<?> user);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,12 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Res;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ComponentAdapter;
 | 
			
		||||
import java.awt.event.ComponentEvent;
 | 
			
		||||
import java.awt.event.ComponentListener;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import javax.swing.border.BevelBorder;
 | 
			
		||||
 | 
			
		||||
@@ -16,57 +15,48 @@ import javax.swing.border.BevelBorder;
 | 
			
		||||
 * @author lhunath, 2018-07-14
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("serial")
 | 
			
		||||
public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener, ComponentListener {
 | 
			
		||||
public class MasterPasswordFrame extends JFrame {
 | 
			
		||||
 | 
			
		||||
    private static final Logger logger = Logger.get( MasterPasswordFrame.class );
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("FieldCanBeLocal")
 | 
			
		||||
    private final Components.GradientPanel root;
 | 
			
		||||
    private final FilesPanel               filesPanel = new FilesPanel();
 | 
			
		||||
    private final UserPanel                userPanel  = new UserPanel();
 | 
			
		||||
    private final Components.GradientPanel root        = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS );
 | 
			
		||||
    private final FilesPanel               filesPanel  = new FilesPanel();
 | 
			
		||||
    private final JPanel                   userPanel   = Components.panel( new BorderLayout( 0, 0 ) );
 | 
			
		||||
    private final UserContentPanel         userContent = new UserContentPanel();
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings("MagicNumber")
 | 
			
		||||
    public MasterPasswordFrame() {
 | 
			
		||||
        super( "Master Password" );
 | 
			
		||||
 | 
			
		||||
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );
 | 
			
		||||
        setContentPane( root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
        setContentPane( root );
 | 
			
		||||
        root.add( filesPanel );
 | 
			
		||||
        root.add( Components.strut() );
 | 
			
		||||
        root.add( Components.borderPanel(
 | 
			
		||||
        root.add( userPanel );
 | 
			
		||||
 | 
			
		||||
        userPanel.add( userContent.getUserToolbar(), BorderLayout.LINE_START );
 | 
			
		||||
        userPanel.add( userContent.getSiteToolbar(), BorderLayout.LINE_END );
 | 
			
		||||
        userPanel.add( Components.borderPanel(
 | 
			
		||||
                BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ),
 | 
			
		||||
                Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userPanel ) );
 | 
			
		||||
                Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userContent ), BorderLayout.CENTER );
 | 
			
		||||
 | 
			
		||||
        filesPanel.addListener( this );
 | 
			
		||||
        filesPanel.reload();
 | 
			
		||||
        filesPanel.addListener( userContent );
 | 
			
		||||
 | 
			
		||||
        addComponentListener( this );
 | 
			
		||||
        setPreferredSize( new Dimension( 640, 480 ) );
 | 
			
		||||
        addComponentListener( new ComponentHandler() );
 | 
			
		||||
        setPreferredSize( new Dimension( 800, 560 ) );
 | 
			
		||||
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );
 | 
			
		||||
        pack();
 | 
			
		||||
 | 
			
		||||
        setLocationRelativeTo( null );
 | 
			
		||||
        setLocationByPlatform( true );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onUserSelected(@Nullable final MPUser<?> user) {
 | 
			
		||||
        userPanel.setUser( user );
 | 
			
		||||
    }
 | 
			
		||||
    private class ComponentHandler extends ComponentAdapter {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void componentResized(final ComponentEvent e) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void componentMoved(final ComponentEvent e) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void componentShown(final ComponentEvent e) {
 | 
			
		||||
        userPanel.transferFocus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void componentHidden(final ComponentEvent e) {
 | 
			
		||||
        @Override
 | 
			
		||||
        public void componentShown(final ComponentEvent e) {
 | 
			
		||||
            MPFileUserManager.get().reload();
 | 
			
		||||
            userContent.transferFocus();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,7 @@ import com.lyndir.masterpassword.*;
 | 
			
		||||
import com.lyndir.masterpassword.gui.model.MPNewSite;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileSite;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.datatransfer.StringSelection;
 | 
			
		||||
import java.awt.datatransfer.Transferable;
 | 
			
		||||
@@ -32,44 +31,38 @@ import javax.swing.event.DocumentListener;
 | 
			
		||||
 * @author lhunath, 2018-07-14
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("SerializableStoresNonSerializable")
 | 
			
		||||
public class UserPanel extends Components.GradientPanel implements MPUser.Listener {
 | 
			
		||||
public class UserContentPanel extends JPanel implements FilesPanel.Listener, MPUser.Listener {
 | 
			
		||||
 | 
			
		||||
    private static final Logger logger = Logger.get( UserPanel.class );
 | 
			
		||||
    private static final Random  random     = new Random();
 | 
			
		||||
    private static final Logger  logger     = Logger.get( UserContentPanel.class );
 | 
			
		||||
    private static final JButton iconButton = Components.button( Res.icons().user(), null );
 | 
			
		||||
 | 
			
		||||
    private final JPanel userToolbar = Components.panel( BoxLayout.PAGE_AXIS );
 | 
			
		||||
    private final JPanel siteToolbar = Components.panel( BoxLayout.PAGE_AXIS );
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private MPUser<?> user;
 | 
			
		||||
    private MPUser<?> listeningUser;
 | 
			
		||||
 | 
			
		||||
    public UserPanel() {
 | 
			
		||||
        super( new BorderLayout( Components.margin(), Components.margin() ), null );
 | 
			
		||||
    public UserContentPanel() {
 | 
			
		||||
        userToolbar.setPreferredSize( iconButton.getPreferredSize() );
 | 
			
		||||
        siteToolbar.setPreferredSize( iconButton.getPreferredSize() );
 | 
			
		||||
 | 
			
		||||
        setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
        setBorder( Components.marginBorder() );
 | 
			
		||||
        setUser( null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUser(@Nullable final MPUser<?> user) {
 | 
			
		||||
        if ((this.user != null) && !Objects.equals( this.user, user ))
 | 
			
		||||
            this.user.removeListener( this );
 | 
			
		||||
    protected JComponent getUserToolbar() {
 | 
			
		||||
        return userToolbar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        this.user = user;
 | 
			
		||||
    protected JComponent getSiteToolbar() {
 | 
			
		||||
        return siteToolbar;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        if (this.user != null)
 | 
			
		||||
            this.user.addListener( this );
 | 
			
		||||
 | 
			
		||||
        Res.ui( () -> {
 | 
			
		||||
            removeAll();
 | 
			
		||||
            if (this.user == null)
 | 
			
		||||
                add( new NoUserPanel(), BorderLayout.CENTER );
 | 
			
		||||
 | 
			
		||||
            else {
 | 
			
		||||
                if (!this.user.isMasterKeyAvailable())
 | 
			
		||||
                    add( new AuthenticateUserPanel( this.user ), BorderLayout.CENTER );
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    add( new AuthenticatedUserPanel( this.user ), BorderLayout.CENTER );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            revalidate();
 | 
			
		||||
            transferFocus();
 | 
			
		||||
        } );
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onUserSelected(@Nullable final MPUser<?> user) {
 | 
			
		||||
        setUser( user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -82,7 +75,40 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
        setUser( user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static final class NoUserPanel extends JPanel {
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onUserInvalidated(final MPUser<?> user) {
 | 
			
		||||
        setUser( user );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setUser(@Nullable final MPUser<?> user) {
 | 
			
		||||
        Res.ui( () -> {
 | 
			
		||||
            if (listeningUser != null)
 | 
			
		||||
                listeningUser.removeListener( this );
 | 
			
		||||
            listeningUser = user;
 | 
			
		||||
 | 
			
		||||
            userToolbar.removeAll();
 | 
			
		||||
            siteToolbar.removeAll();
 | 
			
		||||
            removeAll();
 | 
			
		||||
 | 
			
		||||
            if (user == null)
 | 
			
		||||
                add( new NoUserPanel() );
 | 
			
		||||
 | 
			
		||||
            else {
 | 
			
		||||
                user.addListener( this );
 | 
			
		||||
 | 
			
		||||
                if (!user.isMasterKeyAvailable())
 | 
			
		||||
                    add( new AuthenticateUserPanel( user ) );
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                    add( new AuthenticatedUserPanel( user ) );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            revalidate();
 | 
			
		||||
            transferFocus();
 | 
			
		||||
        } );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final class NoUserPanel extends JPanel {
 | 
			
		||||
 | 
			
		||||
        private NoUserPanel() {
 | 
			
		||||
            setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
@@ -94,13 +120,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static final class AuthenticateUserPanel extends JPanel implements ActionListener, DocumentListener {
 | 
			
		||||
 | 
			
		||||
        private static final Random random = new Random();
 | 
			
		||||
    private final class AuthenticateUserPanel extends JPanel implements ActionListener, DocumentListener {
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        private final MPUser<?> user;
 | 
			
		||||
 | 
			
		||||
        private final JButton addButton    = Components.button( Res.icons().add(), event -> addUser() );
 | 
			
		||||
        private final JButton deleteButton = Components.button( Res.icons().delete(), event -> deleteUser() );
 | 
			
		||||
 | 
			
		||||
        private final JPasswordField masterPasswordField = Components.passwordField();
 | 
			
		||||
        private final JLabel         errorLabel          = Components.label();
 | 
			
		||||
        private final JLabel         identiconLabel      = Components.label( SwingConstants.CENTER );
 | 
			
		||||
@@ -112,7 +139,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
 | 
			
		||||
            this.user = user;
 | 
			
		||||
 | 
			
		||||
            userToolbar.add( addButton );
 | 
			
		||||
            userToolbar.add( deleteButton );
 | 
			
		||||
 | 
			
		||||
            add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
 | 
			
		||||
            add( Components.strut() );
 | 
			
		||||
 | 
			
		||||
            add( identiconLabel );
 | 
			
		||||
            identiconLabel.setFont( Res.fonts().emoticonsFont( Components.TEXT_SIZE_CONTROL ) );
 | 
			
		||||
            add( Box.createGlue() );
 | 
			
		||||
 | 
			
		||||
            add( Components.label( "Master Password:" ) );
 | 
			
		||||
@@ -122,14 +156,31 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
            masterPasswordField.getDocument().addDocumentListener( this );
 | 
			
		||||
            add( errorLabel );
 | 
			
		||||
            errorLabel.setForeground( Res.colors().errorFg() );
 | 
			
		||||
 | 
			
		||||
            add( Components.strut() );
 | 
			
		||||
            add( identiconLabel );
 | 
			
		||||
            identiconLabel.setFont( Res.fonts().emoticonsFont( Components.TEXT_SIZE_CONTROL ) );
 | 
			
		||||
 | 
			
		||||
            add( Box.createGlue() );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void addUser() {
 | 
			
		||||
            Object fullName = JOptionPane.showInputDialog(
 | 
			
		||||
                    this, strf( "<html>Enter your full legal name:</html>" ), "Add User",
 | 
			
		||||
                    JOptionPane.QUESTION_MESSAGE, null, null, "Robert Lee Mitchell" );
 | 
			
		||||
            if (fullName == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            setUser( MPFileUserManager.get().add( fullName.toString() ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void deleteUser() {
 | 
			
		||||
            MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
 | 
			
		||||
            if (fileUser == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
 | 
			
		||||
                    this, strf( "<html>Delete the user <strong>%s</strong>?<br><br><em>%s</em></html>",
 | 
			
		||||
                                fileUser.getFullName(), fileUser.getFile().getName() ),
 | 
			
		||||
                    "Delete User", JOptionPane.YES_NO_OPTION ))
 | 
			
		||||
                MPFileUserManager.get().delete( fileUser );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void actionPerformed(final ActionEvent event) {
 | 
			
		||||
            updateIdenticon();
 | 
			
		||||
@@ -193,21 +244,25 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static final class AuthenticatedUserPanel extends JPanel implements KeyListener {
 | 
			
		||||
    private final class AuthenticatedUserPanel extends JPanel implements KeyListener {
 | 
			
		||||
 | 
			
		||||
        public static final int SIZE_RESULT = 48;
 | 
			
		||||
 | 
			
		||||
        private final JButton userButton      = Components.button( Res.icons().user(), event -> showUserPreferences() );
 | 
			
		||||
        private final JButton logoutButton    = Components.button( Res.icons().lock(), event -> logoutUser() );
 | 
			
		||||
        private final JButton settingsButton  = Components.button( Res.icons().settings(), event -> showSiteSettings() );
 | 
			
		||||
        private final JButton questionsButton = Components.button( Res.icons().question(), null );
 | 
			
		||||
        private final JButton deleteButton    = Components.button( Res.icons().delete(), event -> deleteSite() );
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        private final MPUser<?>                      user;
 | 
			
		||||
        private final JLabel                         passwordLabel  = Components.label( SwingConstants.CENTER );
 | 
			
		||||
        private final JLabel                         passwordField  = Components.heading( SwingConstants.CENTER );
 | 
			
		||||
        private final JButton                        passwordButton =
 | 
			
		||||
                Components.button( Res.icons().settings(), event -> showSiteSettings() );
 | 
			
		||||
        private final JLabel                         queryLabel     = Components.label();
 | 
			
		||||
        private final JTextField                     queryField     = Components.textField( null, this::updateSites );
 | 
			
		||||
        private final CollectionListModel<MPSite<?>> sitesModel     =
 | 
			
		||||
        private final JLabel                         passwordLabel = Components.label( SwingConstants.CENTER );
 | 
			
		||||
        private final JLabel                         passwordField = Components.heading( SwingConstants.CENTER );
 | 
			
		||||
        private final JLabel                         queryLabel    = Components.label();
 | 
			
		||||
        private final JTextField                     queryField    = Components.textField( null, this::updateSites );
 | 
			
		||||
        private final CollectionListModel<MPSite<?>> sitesModel    =
 | 
			
		||||
                new CollectionListModel<MPSite<?>>().selection( this::showSiteResult );
 | 
			
		||||
        private final JList<MPSite<?>>               sitesList      =
 | 
			
		||||
        private final JList<MPSite<?>>               sitesList     =
 | 
			
		||||
                Components.list( sitesModel, this::getSiteDescription );
 | 
			
		||||
 | 
			
		||||
        private Future<?> updateSitesJob;
 | 
			
		||||
@@ -217,23 +272,20 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
 | 
			
		||||
            this.user = user;
 | 
			
		||||
 | 
			
		||||
            add( Components.panel(
 | 
			
		||||
                    Components.heading( user.getFullName(), SwingConstants.CENTER ),
 | 
			
		||||
                    Components.panel(
 | 
			
		||||
                            BoxLayout.LINE_AXIS,
 | 
			
		||||
                            Box.createGlue(),
 | 
			
		||||
                            Components.button( Res.icons().user(), event -> showUserPreferences() ) ) ) );
 | 
			
		||||
            userToolbar.add( userButton );
 | 
			
		||||
            userToolbar.add( logoutButton );
 | 
			
		||||
 | 
			
		||||
            siteToolbar.add( settingsButton );
 | 
			
		||||
            siteToolbar.add( questionsButton );
 | 
			
		||||
            siteToolbar.add( deleteButton );
 | 
			
		||||
            settingsButton.setEnabled( false );
 | 
			
		||||
 | 
			
		||||
            add( Components.heading( user.getFullName(), SwingConstants.CENTER ) );
 | 
			
		||||
 | 
			
		||||
            add( passwordLabel );
 | 
			
		||||
            add( Components.panel(
 | 
			
		||||
                    passwordField,
 | 
			
		||||
                    Components.panel(
 | 
			
		||||
                            BoxLayout.LINE_AXIS,
 | 
			
		||||
                            Box.createGlue(),
 | 
			
		||||
                            passwordButton ) ) );
 | 
			
		||||
            add( passwordField );
 | 
			
		||||
            passwordField.setForeground( Res.colors().highlightFg() );
 | 
			
		||||
            passwordField.setFont( Res.fonts().bigValueFont( SIZE_RESULT ) );
 | 
			
		||||
            passwordButton.setVisible( false );
 | 
			
		||||
            add( Box.createGlue() );
 | 
			
		||||
            add( Components.strut() );
 | 
			
		||||
 | 
			
		||||
@@ -269,13 +321,16 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
                    BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void showSiteSettings() {
 | 
			
		||||
            ImmutableList.Builder<Component> components = ImmutableList.builder();
 | 
			
		||||
        public void logoutUser() {
 | 
			
		||||
            user.invalidate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void showSiteSettings() {
 | 
			
		||||
            MPSite<?> site = sitesModel.getSelectedItem();
 | 
			
		||||
            if (site == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            ImmutableList.Builder<Component> components = ImmutableList.builder();
 | 
			
		||||
            components.add( Components.label( "Algorithm:" ),
 | 
			
		||||
                            Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
 | 
			
		||||
                                                 site.getAlgorithm().version(),
 | 
			
		||||
@@ -306,6 +361,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
                    BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void deleteSite() {
 | 
			
		||||
            MPSite<?> site = sitesModel.getSelectedItem();
 | 
			
		||||
            if (site == null)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            user.deleteSite( site );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private String getSiteDescription(@Nonnull final MPSite<?> site) {
 | 
			
		||||
            if (site instanceof MPNewSite)
 | 
			
		||||
                return strf( "<html><strong>%s</strong> <Add new site></html>", queryField.getText() );
 | 
			
		||||
@@ -334,7 +397,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
            MPSite<?> site = sitesModel.getSelectedItem();
 | 
			
		||||
            if (site instanceof MPNewSite) {
 | 
			
		||||
                if (JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
 | 
			
		||||
                        this, strf( "<html>Remember the site [<strong>%s</strong>]?</html>", site.getSiteName() ),
 | 
			
		||||
                        this, strf( "<html>Remember the site <strong>%s</strong>?</html>", site.getSiteName() ),
 | 
			
		||||
                        "New Site", JOptionPane.YES_NO_OPTION )) {
 | 
			
		||||
                    sitesModel.setSelectedItem( user.addSite( site.getSiteName() ) );
 | 
			
		||||
                    useSite();
 | 
			
		||||
@@ -370,7 +433,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
                Res.ui( () -> {
 | 
			
		||||
                    passwordLabel.setText( " " );
 | 
			
		||||
                    passwordField.setText( " " );
 | 
			
		||||
                    passwordButton.setVisible( false );
 | 
			
		||||
                    settingsButton.setEnabled( false );
 | 
			
		||||
                } );
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
@@ -384,7 +447,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
                    Res.ui( () -> {
 | 
			
		||||
                        passwordLabel.setText( strf( "Your password for %s:", site.getSiteName() ) );
 | 
			
		||||
                        passwordField.setText( result );
 | 
			
		||||
                        passwordButton.setVisible( true );
 | 
			
		||||
                        settingsButton.setEnabled( true );
 | 
			
		||||
                    } );
 | 
			
		||||
                }
 | 
			
		||||
                catch (final MPKeyUnavailableException | MPAlgorithmException e) {
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.7 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.6 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.7 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.5 KiB  | 
@@ -94,9 +94,9 @@ public interface MPSite<Q extends MPQuestion> extends Comparable<MPSite<?>> {
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    MPUser<?> getUser();
 | 
			
		||||
 | 
			
		||||
    void addQuestion(Q question);
 | 
			
		||||
    boolean addQuestion(Q question);
 | 
			
		||||
 | 
			
		||||
    void deleteQuestion(Q question);
 | 
			
		||||
    boolean deleteQuestion(Q question);
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    Collection<Q> getQuestions();
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,8 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
 | 
			
		||||
    void authenticate(MPMasterKey masterKey)
 | 
			
		||||
            throws MPIncorrectMasterPasswordException, MPKeyUnavailableException, MPAlgorithmException;
 | 
			
		||||
 | 
			
		||||
    void invalidate();
 | 
			
		||||
 | 
			
		||||
    boolean isMasterKeyAvailable();
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
@@ -90,7 +92,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    S addSite(S site);
 | 
			
		||||
 | 
			
		||||
    void deleteSite(S site);
 | 
			
		||||
    boolean deleteSite(MPSite<?> site);
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    Collection<S> getSites();
 | 
			
		||||
@@ -107,5 +109,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
 | 
			
		||||
        void onUserUpdated(MPUser<?> user);
 | 
			
		||||
 | 
			
		||||
        void onUserAuthenticated(MPUser<?> user);
 | 
			
		||||
 | 
			
		||||
        void onUserInvalidated(MPUser<?> user);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,8 +55,10 @@ public abstract class MPBasicQuestion extends Changeable implements MPQuestion {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setType(final MPResultType type) {
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        if (Objects.equals(this.type, type))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.type = type;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -73,8 +73,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlgorithm(final MPAlgorithm algorithm) {
 | 
			
		||||
        this.algorithm = algorithm;
 | 
			
		||||
        if (Objects.equals(this.algorithm, algorithm))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.algorithm = algorithm;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -86,8 +88,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setCounter(final UnsignedInteger counter) {
 | 
			
		||||
        this.counter = counter;
 | 
			
		||||
        if (Objects.equals(this.counter, counter))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.counter = counter;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -99,8 +103,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setResultType(final MPResultType resultType) {
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        if (Objects.equals(this.resultType, resultType))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.resultType = resultType;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -112,8 +118,10 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setLoginType(@Nullable final MPResultType loginType) {
 | 
			
		||||
        this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
 | 
			
		||||
        if (Objects.equals(this.loginType, loginType))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.loginType = ifNotNullElse( loginType, getAlgorithm().mpw_default_login_type() );
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -152,17 +160,21 @@ public abstract class MPBasicSite<U extends MPUser<?>, Q extends MPQuestion> ext
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void addQuestion(final Q question) {
 | 
			
		||||
        questions.add( question );
 | 
			
		||||
    public boolean addQuestion(final Q question) {
 | 
			
		||||
        if (!questions.add( question ))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteQuestion(final Q question) {
 | 
			
		||||
        questions.remove( question );
 | 
			
		||||
    public boolean deleteQuestion(final Q question) {
 | 
			
		||||
        if (!questions.remove( question ))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,7 @@ import com.google.common.collect.ImmutableSortedSet;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.CodeUtils;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArraySet;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
@@ -66,8 +65,10 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAvatar(final int avatar) {
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
        if (Objects.equals(this.avatar, avatar))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.avatar = avatar;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -85,8 +86,10 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setAlgorithm(final MPAlgorithm algorithm) {
 | 
			
		||||
        this.algorithm = algorithm;
 | 
			
		||||
        if (Objects.equals(this.algorithm, algorithm))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.algorithm = algorithm;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -136,6 +139,17 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
			
		||||
            listener.onUserAuthenticated( this );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void invalidate() {
 | 
			
		||||
        if (masterKey == null)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        this.masterKey = null;
 | 
			
		||||
 | 
			
		||||
        for (final Listener listener : listeners)
 | 
			
		||||
            listener.onUserInvalidated( this );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isMasterKeyAvailable() {
 | 
			
		||||
        return masterKey != null;
 | 
			
		||||
@@ -151,6 +165,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
			
		||||
        return masterKey;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    @Override
 | 
			
		||||
    public S addSite(final S site) {
 | 
			
		||||
        sites.put( site.getSiteName(), site );
 | 
			
		||||
@@ -160,10 +175,12 @@ public abstract class MPBasicUser<S extends MPBasicSite<?, ?>> extends Changeabl
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteSite(final S site) {
 | 
			
		||||
        sites.values().remove( site );
 | 
			
		||||
    public boolean deleteSite(final MPSite<?> site) {
 | 
			
		||||
        if (!sites.values().remove( site ))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ package com.lyndir.masterpassword.model.impl;
 | 
			
		||||
import com.google.common.primitives.UnsignedInteger;
 | 
			
		||||
import com.lyndir.masterpassword.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPSite;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.Instant;
 | 
			
		||||
@@ -75,8 +76,10 @@ public class MPFileSite extends MPBasicSite<MPFileUser, MPFileQuestion> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setUrl(@Nullable final String url) {
 | 
			
		||||
        this.url = url;
 | 
			
		||||
        if (Objects.equals( this.url, url))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.url = url;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +95,6 @@ public class MPFileSite extends MPBasicSite<MPFileUser, MPFileQuestion> {
 | 
			
		||||
        uses++;
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
        getUser().use();
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPUser;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import org.joda.time.Instant;
 | 
			
		||||
@@ -98,8 +99,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFormat(final MPMarshalFormat format) {
 | 
			
		||||
        this.format = format;
 | 
			
		||||
        if (Objects.equals(this.format, format))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.format = format;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -108,8 +111,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setContentMode(final MPMarshaller.ContentMode contentMode) {
 | 
			
		||||
        this.contentMode = contentMode;
 | 
			
		||||
        if (Objects.equals(this.contentMode, contentMode))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.contentMode = contentMode;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -118,8 +123,10 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDefaultType(final MPResultType defaultType) {
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
        if (Objects.equals(this.defaultType, defaultType))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.defaultType = defaultType;
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -129,7 +136,6 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
 | 
			
		||||
    public void use() {
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -159,7 +165,6 @@ public class MPFileUser extends MPBasicUser<MPFileSite> {
 | 
			
		||||
 | 
			
		||||
        if (keyID == null) {
 | 
			
		||||
            keyID = masterKey.getKeyID( getAlgorithm() );
 | 
			
		||||
 | 
			
		||||
            setChanged();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@ import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import com.lyndir.masterpassword.model.MPConstants;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArraySet;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -52,6 +52,7 @@ public class MPFileUserManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private final Collection<Listener>    listeners  = new CopyOnWriteArraySet<>();
 | 
			
		||||
    private final Map<String, MPFileUser> userByName = new HashMap<>();
 | 
			
		||||
    private final File                    path;
 | 
			
		||||
 | 
			
		||||
@@ -67,13 +68,13 @@ public class MPFileUserManager {
 | 
			
		||||
        this.path = path;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public ImmutableSortedSet<MPFileUser> reload() {
 | 
			
		||||
    public void reload() {
 | 
			
		||||
        userByName.clear();
 | 
			
		||||
 | 
			
		||||
        File[] pathFiles;
 | 
			
		||||
        if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) {
 | 
			
		||||
            logger.err( "Couldn't create directory for user files: %s", path );
 | 
			
		||||
            return getFiles();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (final File file : pathFiles)
 | 
			
		||||
@@ -90,12 +91,14 @@ public class MPFileUserManager {
 | 
			
		||||
                        logger.err( e, "Couldn't read user from: %s", file );
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
        return getFiles();
 | 
			
		||||
        fireUpdated();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MPFileUser add(final String fullName) {
 | 
			
		||||
        MPFileUser user = new MPFileUser( fullName );
 | 
			
		||||
        userByName.put( user.getFullName(), user );
 | 
			
		||||
        fireUpdated();
 | 
			
		||||
 | 
			
		||||
        return user;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -104,8 +107,8 @@ public class MPFileUserManager {
 | 
			
		||||
        File userFile = user.getFile();
 | 
			
		||||
        if (userFile.exists() && !userFile.delete())
 | 
			
		||||
            logger.err( "Couldn't delete file: %s", userFile );
 | 
			
		||||
        else
 | 
			
		||||
            userByName.values().remove( user );
 | 
			
		||||
        else if (userByName.values().remove( user ))
 | 
			
		||||
            fireUpdated();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public File getPath() {
 | 
			
		||||
@@ -115,4 +118,27 @@ public class MPFileUserManager {
 | 
			
		||||
    public ImmutableSortedSet<MPFileUser> getFiles() {
 | 
			
		||||
        return ImmutableSortedSet.copyOf( userByName.values() );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean addListener(final Listener listener) {
 | 
			
		||||
        return listeners.add( listener );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean removeListener(final Listener listener) {
 | 
			
		||||
        return listeners.remove( listener );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void fireUpdated() {
 | 
			
		||||
        if (listeners.isEmpty())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        ImmutableSortedSet<MPFileUser> files = getFiles();
 | 
			
		||||
 | 
			
		||||
        for (final Listener listener : listeners)
 | 
			
		||||
            listener.onFilesUpdated( files );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface Listener {
 | 
			
		||||
 | 
			
		||||
        void onFilesUpdated(ImmutableSortedSet<MPFileUser> files);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user