User preferences.
This commit is contained in:
@@ -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 );
|
||||
|
||||
|
Reference in New Issue
Block a user