User preferences.
This commit is contained in:
		@@ -41,7 +41,7 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 16: pg^VMAUBk5x3p%HP%i4=
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedMaximum( "maximum", "20 characters, contains symbols.", //
 | 
			
		||||
    GeneratedMaximum( "maximum", "Maximum Security", "pg^VMAUBk5x3p%HP%i4=", "20 characters, contains symbols.", //
 | 
			
		||||
                      ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
 | 
			
		||||
                                        new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
 | 
			
		||||
                      MPResultTypeClass.Template, 0x0 ),
 | 
			
		||||
@@ -49,7 +49,7 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 17: BiroYena8:Kixa
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedLong( "long", "Copy-friendly, 14 characters, contains symbols.", //
 | 
			
		||||
    GeneratedLong( "long", "Long Password", "BiroYena8:Kixa", "Copy-friendly, 14 characters, contains symbols.", //
 | 
			
		||||
                   ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
 | 
			
		||||
                                     new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
 | 
			
		||||
                                     new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
 | 
			
		||||
@@ -66,7 +66,7 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 18: BirSuj0-
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedMedium( "medium", "Copy-friendly, 8 characters, contains symbols.", //
 | 
			
		||||
    GeneratedMedium( "medium", "Medium Password", "BirSuj0-", "Copy-friendly, 8 characters, contains symbols.", //
 | 
			
		||||
                     ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
 | 
			
		||||
                                       new MPTemplate( "CvcCvcno" ) ), //
 | 
			
		||||
                     MPResultTypeClass.Template, 0x2 ),
 | 
			
		||||
@@ -74,14 +74,14 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 19: Bir8
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedShort( "short", "Copy-friendly, 4 characters, no symbols.", //
 | 
			
		||||
    GeneratedShort( "short", "Short Password", "Bir8", "Copy-friendly, 4 characters, no symbols.", //
 | 
			
		||||
                    ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
 | 
			
		||||
                    MPResultTypeClass.Template, 0x3 ),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 20: pO98MoD0
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedBasic( "basic", "8 characters, no symbols.", //
 | 
			
		||||
    GeneratedBasic( "basic", "Basic Password", "pO98MoD0", "8 characters, no symbols.", //
 | 
			
		||||
                    ImmutableList.of( new MPTemplate( "aaanaaan" ),
 | 
			
		||||
                                      new MPTemplate( "aannaaan" ),
 | 
			
		||||
                                      new MPTemplate( "aaannaaa" ) ), //
 | 
			
		||||
@@ -90,21 +90,21 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 21: 2798
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedPIN( "pin", "4 numbers.", //
 | 
			
		||||
    GeneratedPIN( "pin", "PIN Code", "2798", "4 numbers.", //
 | 
			
		||||
                  ImmutableList.of( new MPTemplate( "nnnn" ) ), //
 | 
			
		||||
                  MPResultTypeClass.Template, 0x5 ),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 30: birsujano
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedName( "name", "9 letter name.", //
 | 
			
		||||
    GeneratedName( "name", "Name", "birsujano", "9 letter name.", //
 | 
			
		||||
                   ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
 | 
			
		||||
                   MPResultTypeClass.Template, 0xE ),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 31: bir yennoquce fefi
 | 
			
		||||
     */
 | 
			
		||||
    GeneratedPhrase( "phrase", "20 character sentence.", //
 | 
			
		||||
    GeneratedPhrase( "phrase", "Phrase", "bir yennoquce fefi", "20 character sentence.", //
 | 
			
		||||
                     ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
 | 
			
		||||
                                       new MPTemplate( "cvc cvccvcvcv cvcv" ),
 | 
			
		||||
                                       new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
 | 
			
		||||
@@ -113,37 +113,44 @@ public enum MPResultType {
 | 
			
		||||
    /**
 | 
			
		||||
     * 1056: Custom saved password.
 | 
			
		||||
     */
 | 
			
		||||
    StoredPersonal( "personal", "AES-encrypted, exportable.", //
 | 
			
		||||
    StoredPersonal( "personal", "Saved Password", null, "AES-encrypted, exportable.", //
 | 
			
		||||
                    ImmutableList.<MPTemplate>of(), //
 | 
			
		||||
                    MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 2081: Custom saved password that should not be exported from the device.
 | 
			
		||||
     */
 | 
			
		||||
    StoredDevicePrivate( "device", "AES-encrypted, not exported.", //
 | 
			
		||||
    StoredDevicePrivate( "device", "Private Password", null, "AES-encrypted, not exported.", //
 | 
			
		||||
                         ImmutableList.<MPTemplate>of(), //
 | 
			
		||||
                         MPResultTypeClass.Stateful, 0x1, MPSiteFeature.DevicePrivate ),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 4160: Derive a unique binary key.
 | 
			
		||||
     */
 | 
			
		||||
    DeriveKey( "key", "Encryption key.", //
 | 
			
		||||
    DeriveKey( "key", "Binary Key", null, "Encryption key.", //
 | 
			
		||||
               ImmutableList.<MPTemplate>of(), //
 | 
			
		||||
               MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );
 | 
			
		||||
 | 
			
		||||
    static final Logger logger = Logger.get( MPResultType.class );
 | 
			
		||||
 | 
			
		||||
    private final String                      shortName;
 | 
			
		||||
    private final String                      longName;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private final String                      sample;
 | 
			
		||||
    private final String                      description;
 | 
			
		||||
    private final List<MPTemplate>            templates;
 | 
			
		||||
    private final MPResultTypeClass           typeClass;
 | 
			
		||||
    private final int                         typeIndex;
 | 
			
		||||
    private final ImmutableSet<MPSiteFeature> typeFeatures;
 | 
			
		||||
 | 
			
		||||
    MPResultType(final String shortName, final String description, final List<MPTemplate> templates,
 | 
			
		||||
    MPResultType(final String shortName, final String longName, @Nullable final String sample, final String description,
 | 
			
		||||
                 final List<MPTemplate> templates,
 | 
			
		||||
                 final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
 | 
			
		||||
 | 
			
		||||
        this.shortName = shortName;
 | 
			
		||||
        this.longName = longName;
 | 
			
		||||
        this.sample = sample;
 | 
			
		||||
        this.description = description;
 | 
			
		||||
        this.templates = templates;
 | 
			
		||||
        this.typeClass = typeClass;
 | 
			
		||||
@@ -160,6 +167,15 @@ public enum MPResultType {
 | 
			
		||||
        return shortName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getLongName() {
 | 
			
		||||
        return longName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    public String getSample() {
 | 
			
		||||
        return sample;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getDescription() {
 | 
			
		||||
 | 
			
		||||
        return description;
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package com.lyndir.masterpassword.gui.util;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import javax.swing.event.ListSelectionEvent;
 | 
			
		||||
@@ -14,17 +15,24 @@ import javax.swing.event.ListSelectionListener;
 | 
			
		||||
@SuppressWarnings("serial")
 | 
			
		||||
public class CollectionListModel<E> extends AbstractListModel<E> implements ComboBoxModel<E>, ListSelectionListener {
 | 
			
		||||
 | 
			
		||||
    private final List<E>  model = new LinkedList<>();
 | 
			
		||||
    private final List<E>     model = new LinkedList<>();
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private       E        selectedItem;
 | 
			
		||||
    private       JList<E> list;
 | 
			
		||||
    private       E           selectedItem;
 | 
			
		||||
    private       JList<E>    list;
 | 
			
		||||
    @Nullable
 | 
			
		||||
    private       Consumer<E> selectionConsumer;
 | 
			
		||||
 | 
			
		||||
    public CollectionListModel() {
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    public static <E> CollectionListModel<E> copy(final E... elements) {
 | 
			
		||||
        return copy( Arrays.asList( elements ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CollectionListModel(final Collection<E> model) {
 | 
			
		||||
        this.model.addAll( model );
 | 
			
		||||
        fireIntervalAdded( this, 0, model.size() );
 | 
			
		||||
    public static <E> CollectionListModel<E> copy(final Collection<E> elements) {
 | 
			
		||||
        CollectionListModel<E> model = new CollectionListModel<>();
 | 
			
		||||
        model.model.addAll( elements );
 | 
			
		||||
        model.fireIntervalAdded( model, 0, model.model.size() );
 | 
			
		||||
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -79,11 +87,14 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
 | 
			
		||||
    public synchronized void setSelectedItem(@Nullable final Object newSelectedItem) {
 | 
			
		||||
        if (!Objects.equals( selectedItem, newSelectedItem ) && model.contains( newSelectedItem )) {
 | 
			
		||||
            selectedItem = (E) newSelectedItem;
 | 
			
		||||
            fireContentsChanged( this, -1, -1 );
 | 
			
		||||
 | 
			
		||||
            fireContentsChanged( this, -1, -1 );
 | 
			
		||||
            //noinspection ObjectEquality
 | 
			
		||||
            if ((list != null) && (list.getModel() == this))
 | 
			
		||||
                list.setSelectedValue( selectedItem, true );
 | 
			
		||||
 | 
			
		||||
            if (selectionConsumer != null)
 | 
			
		||||
                selectionConsumer.accept( selectedItem );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -103,6 +114,17 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
 | 
			
		||||
        this.list.setModel( this );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CollectionListModel<E> selection(@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
 | 
			
		||||
        this.selectionConsumer = null;
 | 
			
		||||
        setSelectedItem( selectedItem );
 | 
			
		||||
 | 
			
		||||
        this.selectionConsumer = selectionConsumer;
 | 
			
		||||
        if (this.selectionConsumer != null)
 | 
			
		||||
            this.selectionConsumer.accept( selectedItem );
 | 
			
		||||
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public synchronized void valueChanged(final ListSelectionEvent event) {
 | 
			
		||||
        //noinspection ObjectEquality
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ package com.lyndir.masterpassword.gui.util;
 | 
			
		||||
 | 
			
		||||
import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.function.Function;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
@@ -30,15 +32,20 @@ import javax.swing.border.CompoundBorder;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2014-06-08
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("SerializableStoresNonSerializable")
 | 
			
		||||
public abstract class Components {
 | 
			
		||||
 | 
			
		||||
    public static final float TEXT_SIZE_HEADING = 19f;
 | 
			
		||||
    public static final float TEXT_SIZE_CONTROL = 13f;
 | 
			
		||||
    public static final int   SIZE_MARGIN       = 20;
 | 
			
		||||
    public static final int   SIZE_MARGIN       = 12;
 | 
			
		||||
    public static final int   SIZE_PADDING      = 8;
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel boxPanel(final int axis, final Component... components) {
 | 
			
		||||
        GradientPanel container = gradientPanel( null, null );
 | 
			
		||||
    public static GradientPanel panel(final int axis, final Component... components) {
 | 
			
		||||
        return panel( axis, null, components );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel panel(final int axis, @Nullable final Color background, final Component... components) {
 | 
			
		||||
        GradientPanel container = gradientPanel( background, null );
 | 
			
		||||
        container.setLayout( new BoxLayout( container, axis ) );
 | 
			
		||||
        for (final Component component : components)
 | 
			
		||||
            container.add( component );
 | 
			
		||||
@@ -46,20 +53,24 @@ public abstract class Components {
 | 
			
		||||
        return container;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel borderPanel(@Nullable final Border border, final Component... components) {
 | 
			
		||||
        return borderPanel( border, null, components );
 | 
			
		||||
    public static GradientPanel borderPanel(final int axis, final Component... components) {
 | 
			
		||||
        return borderPanel( marginBorder(), null, axis, components );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel borderPanel(@Nullable final Border border, @Nullable final Color background,
 | 
			
		||||
                                            final Component... components) {
 | 
			
		||||
        GradientPanel box = boxPanel( BoxLayout.LINE_AXIS, components );
 | 
			
		||||
    public static GradientPanel borderPanel(@Nullable final Border border, final int axis, final Component... components) {
 | 
			
		||||
        return borderPanel( border, null, axis, components );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel borderPanel(@Nullable final Color background, final int axis, final Component... components) {
 | 
			
		||||
        return borderPanel( marginBorder(), background, axis, components );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static GradientPanel borderPanel(@Nullable final Border border, @Nullable final Color background, final int axis,
 | 
			
		||||
                                            final Component... components) {
 | 
			
		||||
        GradientPanel box = panel( axis, background, components );
 | 
			
		||||
        if (border != null)
 | 
			
		||||
            box.setBorder( border );
 | 
			
		||||
 | 
			
		||||
        if (background != null)
 | 
			
		||||
            box.setBackground( background );
 | 
			
		||||
 | 
			
		||||
        return box;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -69,11 +80,39 @@ public abstract class Components {
 | 
			
		||||
                setOpaque( color != null );
 | 
			
		||||
                setBackground( color );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final JOptionPane pane) {
 | 
			
		||||
        JDialog dialog = pane.createDialog( owner, title );
 | 
			
		||||
        dialog.setModalityType( Dialog.ModalityType.DOCUMENT_MODAL );
 | 
			
		||||
 | 
			
		||||
        return showDialog( dialog );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final Container content) {
 | 
			
		||||
        JDialog dialog = new JDialog( (owner != null)? SwingUtilities.windowForComponent( owner ): null,
 | 
			
		||||
                                      title, Dialog.ModalityType.DOCUMENT_MODAL );
 | 
			
		||||
        dialog.setMinimumSize( new Dimension( 320, 0 ) );
 | 
			
		||||
        dialog.setLocationRelativeTo( owner );
 | 
			
		||||
        dialog.setLocationByPlatform( true );
 | 
			
		||||
        dialog.setContentPane( content );
 | 
			
		||||
 | 
			
		||||
        return showDialog( dialog );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static JDialog showDialog(final JDialog dialog) {
 | 
			
		||||
        // OpenJDK does not correctly implement this setting in native code.
 | 
			
		||||
        dialog.getRootPane().putClientProperty( "apple.awt.documentModalSheet", Boolean.TRUE );
 | 
			
		||||
        dialog.getRootPane().putClientProperty( "Window.style", "small" );
 | 
			
		||||
        dialog.pack();
 | 
			
		||||
 | 
			
		||||
        dialog.setVisible( true );
 | 
			
		||||
 | 
			
		||||
        return dialog;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JTextField textField() {
 | 
			
		||||
        return new JTextField() {
 | 
			
		||||
            {
 | 
			
		||||
@@ -81,7 +120,6 @@ public abstract class Components {
 | 
			
		||||
                                                               BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
 | 
			
		||||
                setFont( Res.fonts().valueFont( TEXT_SIZE_CONTROL ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
@@ -97,7 +135,6 @@ public abstract class Components {
 | 
			
		||||
                setBorder( BorderFactory.createCompoundBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ),
 | 
			
		||||
                                                               BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
@@ -126,7 +163,6 @@ public abstract class Components {
 | 
			
		||||
                    }
 | 
			
		||||
                } );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
@@ -141,22 +177,21 @@ public abstract class Components {
 | 
			
		||||
            {
 | 
			
		||||
                setBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JButton button(final String label) {
 | 
			
		||||
        return button( label, null );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static JButton button(final String label, @Nullable final Action action) {
 | 
			
		||||
        return new JButton( label ) {
 | 
			
		||||
            {
 | 
			
		||||
                setFont( Res.fonts().controlFont( TEXT_SIZE_CONTROL ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public Dimension getMaximumSize() {
 | 
			
		||||
                return new Dimension( 20, getPreferredSize().height );
 | 
			
		||||
                if (action != null)
 | 
			
		||||
                    setAction( action );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
@@ -169,7 +204,6 @@ public abstract class Components {
 | 
			
		||||
        Dimension  studDimension = new Dimension( size, size );
 | 
			
		||||
        Box.Filler rigidArea     = new Box.Filler( studDimension, studDimension, studDimension );
 | 
			
		||||
        rigidArea.setAlignmentX( Component.LEFT_ALIGNMENT );
 | 
			
		||||
        rigidArea.setAlignmentY( Component.BOTTOM_ALIGNMENT );
 | 
			
		||||
        rigidArea.setBackground( Color.red );
 | 
			
		||||
        return rigidArea;
 | 
			
		||||
    }
 | 
			
		||||
@@ -194,7 +228,6 @@ public abstract class Components {
 | 
			
		||||
                        BorderFactory.createEmptyBorder( 4, 4, 4, 4 ) );
 | 
			
		||||
                ((DefaultEditor) getEditor()).getTextField().setBorder( editorBorder );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
                setBorder( null );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -231,7 +264,6 @@ public abstract class Components {
 | 
			
		||||
            {
 | 
			
		||||
                setFont( Res.fonts().controlFont( TEXT_SIZE_HEADING ).deriveFont( Font.BOLD ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
@@ -267,7 +299,6 @@ public abstract class Components {
 | 
			
		||||
            {
 | 
			
		||||
                setFont( Res.fonts().controlFont( TEXT_SIZE_CONTROL ) );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
@@ -283,7 +314,6 @@ public abstract class Components {
 | 
			
		||||
                setFont( Res.fonts().controlFont( TEXT_SIZE_CONTROL ) );
 | 
			
		||||
                setBackground( null );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
@@ -293,6 +323,16 @@ public abstract class Components {
 | 
			
		||||
        return comboBox( new DefaultComboBoxModel<>( values ), valueTransformer );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <E> JComboBox<E> comboBox(final E[] values, final Function<E, String> valueTransformer, final E selectedItem,
 | 
			
		||||
                                            @Nullable final Consumer<E> selectionConsumer) {
 | 
			
		||||
        return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <E> JComboBox<E> comboBox(final Collection<E> values, final Function<E, String> valueTransformer, final E selectedItem,
 | 
			
		||||
                                            @Nullable final Consumer<E> selectionConsumer) {
 | 
			
		||||
        return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static <E> JComboBox<E> comboBox(final ComboBoxModel<E> model, final Function<E, String> valueTransformer) {
 | 
			
		||||
        return new JComboBox<E>( model ) {
 | 
			
		||||
            {
 | 
			
		||||
@@ -311,8 +351,8 @@ public abstract class Components {
 | 
			
		||||
                                list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
 | 
			
		||||
                    }
 | 
			
		||||
                } );
 | 
			
		||||
                putClientProperty( "JComboBox.isPopDown", Boolean.TRUE );
 | 
			
		||||
                setAlignmentX( LEFT_ALIGNMENT );
 | 
			
		||||
                setAlignmentY( BOTTOM_ALIGNMENT );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
package com.lyndir.masterpassword.gui.view;
 | 
			
		||||
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.lyndir.masterpassword.MPAlgorithm;
 | 
			
		||||
import com.lyndir.masterpassword.MPResultType;
 | 
			
		||||
import com.lyndir.masterpassword.gui.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.awt.event.ItemEvent;
 | 
			
		||||
import java.awt.event.ItemListener;
 | 
			
		||||
import java.awt.event.*;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.concurrent.CopyOnWriteArraySet;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
@@ -17,14 +20,16 @@ import javax.swing.*;
 | 
			
		||||
/**
 | 
			
		||||
 * @author lhunath, 2018-07-14
 | 
			
		||||
 */
 | 
			
		||||
@SuppressWarnings("serial")
 | 
			
		||||
public class FilesPanel extends JPanel implements ItemListener {
 | 
			
		||||
 | 
			
		||||
    private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
 | 
			
		||||
 | 
			
		||||
    private final JLabel                         avatarLabel = new JLabel();
 | 
			
		||||
    private final CollectionListModel<MPUser<?>> usersModel  = new CollectionListModel<>();
 | 
			
		||||
    private final JComboBox<MPUser<?>>           userField   =
 | 
			
		||||
    private final JLabel                         avatarLabel       = new JLabel();
 | 
			
		||||
    private final CollectionListModel<MPUser<?>> usersModel        = new CollectionListModel<>();
 | 
			
		||||
    private final JComboBox<MPUser<?>>           userField         =
 | 
			
		||||
            Components.comboBox( usersModel, user -> (user != null)? user.getFullName(): null );
 | 
			
		||||
    private final JButton                        preferencesButton = Components.button( "..." );
 | 
			
		||||
 | 
			
		||||
    protected FilesPanel() {
 | 
			
		||||
        setOpaque( false );
 | 
			
		||||
@@ -41,10 +46,34 @@ public class FilesPanel extends JPanel implements ItemListener {
 | 
			
		||||
        avatarLabel.setToolTipText( "The avatar for your user.  Click to change it." );
 | 
			
		||||
 | 
			
		||||
        // -
 | 
			
		||||
        add( Components.strut( 20 ) );
 | 
			
		||||
        add( Components.strut( Components.margin() ) );
 | 
			
		||||
 | 
			
		||||
        // User Selection
 | 
			
		||||
        add( userField );
 | 
			
		||||
        add( Components.panel( BoxLayout.LINE_AXIS, userField, preferencesButton ) );
 | 
			
		||||
        preferencesButton.setAction( new AbstractAction() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void actionPerformed(final ActionEvent e) {
 | 
			
		||||
                MPUser<?> user = usersModel.getSelectedItem();
 | 
			
		||||
                if (user == null)
 | 
			
		||||
                    return;
 | 
			
		||||
                MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
 | 
			
		||||
 | 
			
		||||
                ImmutableList.Builder<Component> components = ImmutableList.builder();
 | 
			
		||||
                if (fileUser != null)
 | 
			
		||||
                    components.add( Components.label( "Default Password Type:" ),
 | 
			
		||||
                                    Components.comboBox( MPResultType.values(), MPResultType::getLongName,
 | 
			
		||||
                                                         fileUser.getDefaultType(), fileUser::setDefaultType ),
 | 
			
		||||
                                    Components.strut() );
 | 
			
		||||
 | 
			
		||||
                components.add( Components.label( "Default Algorithm:" ),
 | 
			
		||||
                                Components.comboBox( MPAlgorithm.Version.values(), MPAlgorithm.Version::name,
 | 
			
		||||
                                                     user.getAlgorithm().version(),
 | 
			
		||||
                                                     version -> user.setAlgorithm( version.getAlgorithm() ) ) );
 | 
			
		||||
 | 
			
		||||
                Components.showDialog( preferencesButton, user.getFullName(), new JOptionPane( Components.panel(
 | 
			
		||||
                        BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
 | 
			
		||||
            }
 | 
			
		||||
        } );
 | 
			
		||||
        userField.addItemListener( this );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import java.awt.event.ComponentEvent;
 | 
			
		||||
import java.awt.event.ComponentListener;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import javax.swing.border.BevelBorder;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -29,24 +30,23 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener,
 | 
			
		||||
        super( "Master Password" );
 | 
			
		||||
 | 
			
		||||
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );
 | 
			
		||||
        setContentPane( root = Components.gradientPanel( Res.colors().frameBg(), new FlowLayout() ) );
 | 
			
		||||
        root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
        root.setBorder( Components.marginBorder() );
 | 
			
		||||
 | 
			
		||||
        setContentPane( root = Components.borderPanel( Res.colors().frameBg(), BoxLayout.PAGE_AXIS ) );
 | 
			
		||||
        root.add( filesPanel );
 | 
			
		||||
        root.add( new JSeparator( SwingConstants.HORIZONTAL ) );
 | 
			
		||||
        root.add( Components.strut() );
 | 
			
		||||
        root.add( Components.borderPanel( BorderFactory.createRaisedBevelBorder(), Res.colors().controlBg(), userPanel ) );
 | 
			
		||||
        root.add( Components.borderPanel(
 | 
			
		||||
                BorderFactory.createBevelBorder( BevelBorder.RAISED, Res.colors().controlBorder(), Res.colors().frameBg() ),
 | 
			
		||||
                Res.colors().controlBg(), BoxLayout.PAGE_AXIS, userPanel ) );
 | 
			
		||||
 | 
			
		||||
        filesPanel.addListener( this );
 | 
			
		||||
        filesPanel.reload();
 | 
			
		||||
 | 
			
		||||
        addComponentListener(this  );
 | 
			
		||||
        addComponentListener( this );
 | 
			
		||||
        setPreferredSize( new Dimension( 640, 480 ) );
 | 
			
		||||
        pack();
 | 
			
		||||
 | 
			
		||||
        setLocationByPlatform( true );
 | 
			
		||||
        setLocationRelativeTo( null );
 | 
			
		||||
        setLocationByPlatform( true );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,8 @@ import com.lyndir.masterpassword.gui.Res;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.CollectionListModel;
 | 
			
		||||
import com.lyndir.masterpassword.gui.util.Components;
 | 
			
		||||
import com.lyndir.masterpassword.model.*;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileSite;
 | 
			
		||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.datatransfer.StringSelection;
 | 
			
		||||
import java.awt.datatransfer.Transferable;
 | 
			
		||||
@@ -129,7 +131,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
        @Override
 | 
			
		||||
        public void actionPerformed(final ActionEvent event) {
 | 
			
		||||
            updateIdenticon();
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            char[] masterPassword = masterPasswordField.getPassword();
 | 
			
		||||
            Res.job( () -> {
 | 
			
		||||
                try {
 | 
			
		||||
@@ -223,6 +225,7 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
            add( queryLabel );
 | 
			
		||||
            queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
 | 
			
		||||
            add( queryField );
 | 
			
		||||
            queryField.putClientProperty( "JTextField.variant", "search" );
 | 
			
		||||
            queryField.addActionListener( this );
 | 
			
		||||
            queryField.addKeyListener( this );
 | 
			
		||||
            queryField.getDocument().addDocumentListener( this );
 | 
			
		||||
@@ -236,10 +239,14 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void actionPerformed(final ActionEvent event) {
 | 
			
		||||
            showSiteResult( sitesList.getSelectedValue(), result -> {
 | 
			
		||||
            MPSite<?> site = sitesList.getSelectedValue();
 | 
			
		||||
            showSiteResult( site, result -> {
 | 
			
		||||
                if (result == null)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (site instanceof MPFileSite)
 | 
			
		||||
                    ((MPFileSite) site).use();
 | 
			
		||||
 | 
			
		||||
                Transferable clipboardContents = new StringSelection( result );
 | 
			
		||||
                Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -95,6 +95,8 @@ public class MPFileSite extends MPBasicSite<MPFileQuestion> {
 | 
			
		||||
        uses++;
 | 
			
		||||
        lastUsed = new Instant();
 | 
			
		||||
        user.use();
 | 
			
		||||
 | 
			
		||||
        setChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getResult()
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user