Implement sites list and copy result.
This commit is contained in:
parent
476a4046e7
commit
400ebe59db
@ -0,0 +1,88 @@
|
|||||||
|
package com.lyndir.masterpassword.gui.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.*;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lhunath, 2018-07-19
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CollectionListModel<E> extends AbstractListModel<E> implements ComboBoxModel<E> {
|
||||||
|
|
||||||
|
private final List<E> model = new LinkedList<>();
|
||||||
|
@Nullable
|
||||||
|
private E selectedItem;
|
||||||
|
|
||||||
|
public CollectionListModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectionListModel(final Collection<E> model) {
|
||||||
|
this.model.addAll( model );
|
||||||
|
fireIntervalAdded( this, 0, model.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized int getSize() {
|
||||||
|
return model.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized E getElementAt(final int index) {
|
||||||
|
return model.get( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace this model's contents with the objects from the new model collection.
|
||||||
|
*
|
||||||
|
* This operation will mutate the internal model to reflect the given model.
|
||||||
|
* The given model will remain untouched and independent from this object.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("AssignmentToForLoopParameter")
|
||||||
|
public synchronized void set(final Collection<? extends E> newModel) {
|
||||||
|
ImmutableList<? extends E> newModelList = ImmutableList.copyOf( newModel );
|
||||||
|
|
||||||
|
ListIterator<E> oldIt = model.listIterator();
|
||||||
|
for (int from = 0; oldIt.hasNext(); ++from) {
|
||||||
|
int to = newModelList.indexOf( oldIt.next() );
|
||||||
|
|
||||||
|
if (to != from) {
|
||||||
|
oldIt.remove();
|
||||||
|
fireIntervalRemoved( this, from, from );
|
||||||
|
--from;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<? extends E> newIt = newModelList.iterator();
|
||||||
|
for (int to = 0; newIt.hasNext(); ++to) {
|
||||||
|
E newSite = newIt.next();
|
||||||
|
|
||||||
|
if ((to >= model.size()) || !Objects.equals( model.get( to ), newSite )) {
|
||||||
|
model.add( to, newSite );
|
||||||
|
fireIntervalAdded( this, to, to );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((selectedItem == null) && !model.isEmpty())
|
||||||
|
setSelectedItem( model.get( 0 ) );
|
||||||
|
else if (!model.contains( selectedItem ))
|
||||||
|
setSelectedItem( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "unchecked", "SuspiciousMethodCalls" })
|
||||||
|
public synchronized void setSelectedItem(@Nullable final Object newSelectedItem) {
|
||||||
|
if (!Objects.equals( selectedItem, newSelectedItem ) && model.contains( newSelectedItem )) {
|
||||||
|
selectedItem = (E) newSelectedItem;
|
||||||
|
fireContentsChanged( this, -1, -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public synchronized E getSelectedItem() {
|
||||||
|
return selectedItem;
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ package com.lyndir.masterpassword.gui.util;
|
|||||||
|
|
||||||
import com.lyndir.masterpassword.gui.Res;
|
import com.lyndir.masterpassword.gui.Res;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.function.Function;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
@ -34,9 +35,8 @@ public abstract class Components {
|
|||||||
private static final float HEADING_TEXT_SIZE = 19f;
|
private static final float HEADING_TEXT_SIZE = 19f;
|
||||||
private static final float CONTROL_TEXT_SIZE = 13f;
|
private static final float CONTROL_TEXT_SIZE = 13f;
|
||||||
|
|
||||||
public static GradientPanel boxLayout(final int axis, final Component... components) {
|
public static GradientPanel boxPanel(final int axis, final Component... components) {
|
||||||
GradientPanel container = gradientPanel( null, null );
|
GradientPanel container = gradientPanel( null, null );
|
||||||
// container.setBackground( Color.red );
|
|
||||||
container.setLayout( new BoxLayout( container, axis ) );
|
container.setLayout( new BoxLayout( container, axis ) );
|
||||||
for (final Component component : components)
|
for (final Component component : components)
|
||||||
container.add( component );
|
container.add( component );
|
||||||
@ -44,12 +44,13 @@ public abstract class Components {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border) {
|
public static GradientPanel borderPanel(@Nullable final Border border, final Component... components) {
|
||||||
return borderPanel( component, border, null );
|
return borderPanel( border, null, components );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border, @Nullable final Color background) {
|
public static GradientPanel borderPanel(@Nullable final Border border, @Nullable final Color background,
|
||||||
GradientPanel box = boxLayout( BoxLayout.LINE_AXIS, component );
|
final Component... components) {
|
||||||
|
GradientPanel box = boxPanel( BoxLayout.LINE_AXIS, components );
|
||||||
|
|
||||||
if (border != null)
|
if (border != null)
|
||||||
box.setBorder( border );
|
box.setBorder( border );
|
||||||
@ -60,11 +61,11 @@ public abstract class Components {
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GradientPanel gradientPanel(@Nullable final LayoutManager layout, @Nullable final Color color) {
|
public static GradientPanel gradientPanel(@Nullable final Color color, @Nullable final LayoutManager layout) {
|
||||||
return new GradientPanel( layout, color ) {
|
return new GradientPanel( layout, color ) {
|
||||||
{
|
{
|
||||||
setOpaque( color != null );
|
setOpaque( color != null );
|
||||||
setBackground( null );
|
setBackground( color );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
}
|
}
|
||||||
@ -104,6 +105,45 @@ public abstract class Components {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <E> JList<E> list(final ListModel<E> model, final Function<E, String> valueTransformer) {
|
||||||
|
return new JList<E>( model ) {
|
||||||
|
{
|
||||||
|
setFont( Res.fonts().valueFont().deriveFont( CONTROL_TEXT_SIZE ) );
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 4, 0, 4, 0 ) );
|
||||||
|
setCellRenderer( new DefaultListCellRenderer() {
|
||||||
|
{
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 0, 4, 0, 4 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
||||||
|
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
||||||
|
final boolean isSelected, final boolean cellHasFocus) {
|
||||||
|
return super.getListCellRendererComponent(
|
||||||
|
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JScrollPane scrollPane(final Component child) {
|
||||||
|
return new JScrollPane( child ) {
|
||||||
|
{
|
||||||
|
setBorder( BorderFactory.createLineBorder( Res.colors().controlBorder(), 1, true ) );
|
||||||
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static JButton button(final String label) {
|
public static JButton button(final String label) {
|
||||||
return new JButton( label ) {
|
return new JButton( label ) {
|
||||||
{
|
{
|
||||||
@ -219,14 +259,28 @@ public abstract class Components {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public static <V> JComboBox<V> comboBox(final V... values) {
|
public static <E> JComboBox<E> comboBox(final Function<E, String> valueTransformer, final E... values) {
|
||||||
return comboBox( new DefaultComboBoxModel<>( values ) );
|
return comboBox( new DefaultComboBoxModel<>( values ), valueTransformer );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
public static <E> JComboBox<E> comboBox(final ComboBoxModel<E> model, final Function<E, String> valueTransformer) {
|
||||||
return new JComboBox<M>( model ) {
|
return new JComboBox<E>( model ) {
|
||||||
{
|
{
|
||||||
setFont( Res.fonts().controlFont().deriveFont( CONTROL_TEXT_SIZE ) );
|
setFont( Res.fonts().valueFont().deriveFont( CONTROL_TEXT_SIZE ) );
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 4, 0, 4, 0 ) );
|
||||||
|
setRenderer( new DefaultListCellRenderer() {
|
||||||
|
{
|
||||||
|
setBorder( BorderFactory.createEmptyBorder( 0, 4, 0, 4 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings({ "unchecked", "SerializableStoresNonSerializable" })
|
||||||
|
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
||||||
|
final boolean isSelected, final boolean cellHasFocus) {
|
||||||
|
return super.getListCellRendererComponent(
|
||||||
|
list, valueTransformer.apply( (E) value ), index, isSelected, cellHasFocus );
|
||||||
|
}
|
||||||
|
} );
|
||||||
setAlignmentX( LEFT_ALIGNMENT );
|
setAlignmentX( LEFT_ALIGNMENT );
|
||||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
package com.lyndir.masterpassword.gui.view;
|
package com.lyndir.masterpassword.gui.view;
|
||||||
|
|
||||||
import com.lyndir.masterpassword.gui.Res;
|
import com.lyndir.masterpassword.gui.Res;
|
||||||
|
import com.lyndir.masterpassword.gui.util.CollectionListModel;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import com.lyndir.masterpassword.model.MPUser;
|
import com.lyndir.masterpassword.model.MPUser;
|
||||||
import com.lyndir.masterpassword.model.impl.MPFileUser;
|
|
||||||
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
|
import com.lyndir.masterpassword.model.impl.MPFileUserManager;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.ItemEvent;
|
||||||
import java.util.Set;
|
import java.awt.event.ItemListener;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.plaf.metal.MetalComboBoxEditor;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author lhunath, 2018-07-14
|
* @author lhunath, 2018-07-14
|
||||||
*/
|
*/
|
||||||
public class FilesPanel extends JPanel implements ActionListener {
|
public class FilesPanel extends JPanel implements ItemListener {
|
||||||
|
|
||||||
private final Set<Listener> listeners = new CopyOnWriteArraySet<>();
|
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
private final JLabel avatarLabel = new JLabel();
|
private final JLabel avatarLabel = new JLabel();
|
||||||
private final JComboBox<MPFileUser> userField = Components.comboBox();
|
private final CollectionListModel<MPUser<?>> usersModel = new CollectionListModel<>();
|
||||||
|
private final JComboBox<MPUser<?>> userField =
|
||||||
|
Components.comboBox( usersModel, user -> (user != null)? user.getFullName(): null );
|
||||||
|
|
||||||
protected FilesPanel() {
|
protected FilesPanel() {
|
||||||
setOpaque( false );
|
setOpaque( false );
|
||||||
@ -43,25 +45,7 @@ public class FilesPanel extends JPanel implements ActionListener {
|
|||||||
|
|
||||||
// User Selection
|
// User Selection
|
||||||
add( userField );
|
add( userField );
|
||||||
userField.addActionListener( this );
|
userField.addItemListener( this );
|
||||||
userField.setFont( Res.fonts().valueFont().deriveFont( userField.getFont().getSize2D() ) );
|
|
||||||
userField.setRenderer( new DefaultListCellRenderer() {
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index,
|
|
||||||
final boolean isSelected, final boolean cellHasFocus) {
|
|
||||||
String userValue = (value == null)? null: ((MPFileUser) value).getFullName();
|
|
||||||
return super.getListCellRendererComponent( list, userValue, index, isSelected, cellHasFocus );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
userField.setEditor( new MetalComboBoxEditor() {
|
|
||||||
@Override
|
|
||||||
protected JTextField createEditorComponent() {
|
|
||||||
JTextField editorComponents = Components.textField();
|
|
||||||
editorComponents.setForeground( Color.red );
|
|
||||||
return editorComponents;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
// -
|
// -
|
||||||
add( Box.createVerticalGlue() );
|
add( Box.createVerticalGlue() );
|
||||||
@ -69,36 +53,25 @@ public class FilesPanel extends JPanel implements ActionListener {
|
|||||||
|
|
||||||
public void reload() {
|
public void reload() {
|
||||||
MPFileUserManager.get().reload();
|
MPFileUserManager.get().reload();
|
||||||
userField.setModel( new DefaultComboBoxModel<>( MPFileUserManager.get().getFiles().toArray( new MPFileUser[0] ) ) );
|
usersModel.set( MPFileUserManager.get().getFiles() );
|
||||||
updateFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(final ActionEvent e) {
|
|
||||||
updateFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private MPFileUser getSelectedUser() {
|
|
||||||
int selectedIndex = userField.getSelectedIndex();
|
|
||||||
if (selectedIndex < 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return userField.getModel().getElementAt( selectedIndex );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateFile() {
|
|
||||||
MPFileUser selectedFile = getSelectedUser();
|
|
||||||
avatarLabel.setIcon( Res.avatar( (selectedFile == null)? 0: selectedFile.getAvatar() ) );
|
|
||||||
|
|
||||||
for (final Listener listener : listeners)
|
|
||||||
listener.onUserSelected( selectedFile );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addListener(final Listener listener) {
|
public boolean addListener(final Listener listener) {
|
||||||
return listeners.add( listener );
|
return listeners.add( listener );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void itemStateChanged(final ItemEvent e) {
|
||||||
|
if (e.getStateChange() != ItemEvent.SELECTED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MPUser<?> selectedUser = usersModel.getSelectedItem();
|
||||||
|
avatarLabel.setIcon( Res.avatar( (selectedUser == null)? 0: selectedUser.getAvatar() ) );
|
||||||
|
|
||||||
|
for (final Listener listener : listeners)
|
||||||
|
listener.onUserSelected( selectedUser );
|
||||||
|
}
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
|
|
||||||
void onUserSelected(@Nullable MPUser<?> selectedUser);
|
void onUserSelected(@Nullable MPUser<?> selectedUser);
|
||||||
|
@ -23,12 +23,14 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener {
|
|||||||
super( "Master Password" );
|
super( "Master Password" );
|
||||||
|
|
||||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||||
setContentPane( root = Components.gradientPanel( new FlowLayout(), Res.colors().frameBg() ) );
|
setContentPane( root = Components.gradientPanel( Res.colors().frameBg(), new FlowLayout() ) );
|
||||||
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
root.setLayout( new BoxLayout( root, BoxLayout.PAGE_AXIS ) );
|
||||||
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
root.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||||
|
|
||||||
root.add( filesPanel );
|
root.add( filesPanel );
|
||||||
root.add( Components.borderPanel( userPanel, BorderFactory.createRaisedBevelBorder(), Res.colors().controlBg() ) );
|
root.add( new JSeparator( SwingConstants.HORIZONTAL ) );
|
||||||
|
root.add( Components.strut() );
|
||||||
|
root.add( Components.borderPanel( BorderFactory.createRaisedBevelBorder(), Res.colors().controlBg(), userPanel ) );
|
||||||
|
|
||||||
filesPanel.addListener( this );
|
filesPanel.addListener( this );
|
||||||
filesPanel.reload();
|
filesPanel.reload();
|
||||||
|
@ -2,22 +2,25 @@ package com.lyndir.masterpassword.gui.view;
|
|||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.google.common.primitives.UnsignedInteger;
|
import com.google.common.primitives.UnsignedInteger;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.gui.Res;
|
import com.lyndir.masterpassword.gui.Res;
|
||||||
|
import com.lyndir.masterpassword.gui.util.CollectionListModel;
|
||||||
import com.lyndir.masterpassword.gui.util.Components;
|
import com.lyndir.masterpassword.gui.util.Components;
|
||||||
import com.lyndir.masterpassword.model.MPIncorrectMasterPasswordException;
|
import com.lyndir.masterpassword.model.*;
|
||||||
import com.lyndir.masterpassword.model.MPUser;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.datatransfer.StringSelection;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.datatransfer.Transferable;
|
||||||
|
import java.awt.event.*;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.DocumentEvent;
|
import javax.swing.event.*;
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,30 +131,35 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
public void insertUpdate(final DocumentEvent event) {
|
||||||
errorLabel.setText( null );
|
errorLabel.setText( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
public void removeUpdate(final DocumentEvent event) {
|
||||||
errorLabel.setText( null );
|
errorLabel.setText( null );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
public void changedUpdate(final DocumentEvent event) {
|
||||||
errorLabel.setText( null );
|
errorLabel.setText( null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final class AuthenticatedUserPanel extends JPanel implements ActionListener, DocumentListener {
|
private static final class AuthenticatedUserPanel extends JPanel implements ActionListener, DocumentListener, ListSelectionListener,
|
||||||
|
KeyListener {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final MPUser<?> user;
|
private final MPUser<?> user;
|
||||||
private final JLabel passwordLabel = Components.label( " ", SwingConstants.CENTER );
|
private final JLabel passwordLabel = Components.label( " ", SwingConstants.CENTER );
|
||||||
private final JLabel passwordField = Components.heading( " ", SwingConstants.CENTER );
|
private final JLabel passwordField = Components.heading( " ", SwingConstants.CENTER );
|
||||||
private final JLabel queryLabel = Components.label( " " );
|
private final JLabel queryLabel = Components.label( " " );
|
||||||
private final JTextField queryField = Components.textField();
|
private final JTextField queryField = Components.textField();
|
||||||
|
private final CollectionListModel<MPSite<?>> sitesModel = new CollectionListModel<>();
|
||||||
|
private final JList<MPSite<?>> sitesList = Components.list( sitesModel,
|
||||||
|
value -> (value != null)? value.getName(): null );
|
||||||
|
private Future<?> updateSitesJob;
|
||||||
|
|
||||||
private AuthenticatedUserPanel(@Nonnull final MPUser<?> user) {
|
private AuthenticatedUserPanel(@Nonnull final MPUser<?> user) {
|
||||||
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
|
setLayout( new BoxLayout( this, BoxLayout.PAGE_AXIS ) );
|
||||||
@ -166,12 +174,17 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
passwordField.setForeground( Res.colors().highlightFg() );
|
passwordField.setForeground( Res.colors().highlightFg() );
|
||||||
passwordField.setFont( Res.fonts().bigValueFont().deriveFont( Font.BOLD, 48 ) );
|
passwordField.setFont( Res.fonts().bigValueFont().deriveFont( Font.BOLD, 48 ) );
|
||||||
add( Box.createGlue() );
|
add( Box.createGlue() );
|
||||||
|
add( Components.strut() );
|
||||||
|
|
||||||
add( queryLabel );
|
add( queryLabel );
|
||||||
queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
|
queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
|
||||||
add( queryField );
|
add( queryField );
|
||||||
queryField.addActionListener( this );
|
queryField.addActionListener( this );
|
||||||
|
queryField.addKeyListener( this );
|
||||||
queryField.getDocument().addDocumentListener( this );
|
queryField.getDocument().addDocumentListener( this );
|
||||||
|
add( Components.strut() );
|
||||||
|
add( Components.scrollPane( sitesList ) );
|
||||||
|
sitesList.addListSelectionListener( this );
|
||||||
add( Box.createGlue() );
|
add( Box.createGlue() );
|
||||||
|
|
||||||
Res.ui( false, queryField::requestFocusInWindow );
|
Res.ui( false, queryField::requestFocusInWindow );
|
||||||
@ -179,12 +192,59 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(final ActionEvent event) {
|
public void actionPerformed(final ActionEvent event) {
|
||||||
String siteName = queryField.getText();
|
showSiteResult( sitesList.getSelectedValue(), result -> {
|
||||||
|
if (result == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Transferable clipboardContents = new StringSelection( result );
|
||||||
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
||||||
|
|
||||||
|
Res.ui( () -> {
|
||||||
|
Window window = SwingUtilities.windowForComponent( this );
|
||||||
|
window.dispatchEvent( new WindowEvent( window, WindowEvent.WINDOW_CLOSING ) );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(final DocumentEvent event) {
|
||||||
|
updateSites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(final DocumentEvent event) {
|
||||||
|
updateSites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(final DocumentEvent event) {
|
||||||
|
updateSites();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void valueChanged(final ListSelectionEvent event) {
|
||||||
|
showSiteResult( event.getValueIsAdjusting()? null: sitesList.getSelectedValue(), null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showSiteResult(@Nullable final MPSite<?> site, @Nullable final Consumer<String> resultCallback) {
|
||||||
|
if (site == null) {
|
||||||
|
if (resultCallback != null)
|
||||||
|
resultCallback.accept( null );
|
||||||
|
Res.ui( () -> {
|
||||||
|
passwordLabel.setText( " " );
|
||||||
|
passwordField.setText( " " );
|
||||||
|
} );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String siteName = site.getName();
|
||||||
Res.job( () -> {
|
Res.job( () -> {
|
||||||
try {
|
try {
|
||||||
String result = user.getMasterKey().siteResult(
|
String result = user.getMasterKey().siteResult(
|
||||||
siteName, user.getAlgorithm(), UnsignedInteger.ONE,
|
siteName, user.getAlgorithm(), UnsignedInteger.ONE,
|
||||||
MPKeyPurpose.Authentication, null, MPResultType.GeneratedLong, null );
|
MPKeyPurpose.Authentication, null, MPResultType.GeneratedLong, null );
|
||||||
|
if (resultCallback != null)
|
||||||
|
resultCallback.accept( result );
|
||||||
|
|
||||||
Res.ui( () -> {
|
Res.ui( () -> {
|
||||||
passwordLabel.setText( strf( "Your password for %s:", siteName ) );
|
passwordLabel.setText( strf( "Your password for %s:", siteName ) );
|
||||||
@ -192,27 +252,35 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
|||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
|
catch (final MPKeyUnavailableException | MPAlgorithmException e) {
|
||||||
logger.err( e, "While resolving password for: %s", siteName );
|
logger.err( e, "While resolving password for: %s", site );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void insertUpdate(final DocumentEvent e) {
|
public void keyTyped(final KeyEvent event) {
|
||||||
// TODO
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeUpdate(final DocumentEvent e) {
|
public void keyPressed(final KeyEvent event) {
|
||||||
// TODO
|
if ((event.getKeyCode() == KeyEvent.VK_UP) || (event.getKeyCode() == KeyEvent.VK_DOWN))
|
||||||
|
sitesList.dispatchEvent( event );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changedUpdate(final DocumentEvent e) {
|
public void keyReleased(final KeyEvent event) {
|
||||||
// TODO
|
if ((event.getKeyCode() == KeyEvent.VK_UP) || (event.getKeyCode() == KeyEvent.VK_DOWN))
|
||||||
|
sitesList.dispatchEvent( event );
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void updateSites() {
|
||||||
|
if (updateSitesJob != null)
|
||||||
|
updateSitesJob.cancel( true );
|
||||||
|
|
||||||
|
updateSitesJob = Res.job( () -> {
|
||||||
|
ImmutableCollection<? extends MPSite<?>> sites = user.findSites( queryField.getText() );
|
||||||
|
Res.ui( () -> sitesModel.set( sites ) );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.lyndir.masterpassword.model;
|
package com.lyndir.masterpassword.model;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.lyndir.masterpassword.*;
|
import com.lyndir.masterpassword.*;
|
||||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
||||||
import com.lyndir.masterpassword.model.impl.MPBasicUser;
|
import com.lyndir.masterpassword.model.impl.MPBasicUser;
|
||||||
@ -94,7 +95,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
|||||||
Collection<S> getSites();
|
Collection<S> getSites();
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
Collection<S> findSites(String query);
|
ImmutableCollection<S> findSites(String query);
|
||||||
|
|
||||||
boolean addListener(Listener listener);
|
boolean addListener(Listener listener);
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ package com.lyndir.masterpassword.model.impl;
|
|||||||
|
|
||||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableCollection;
|
||||||
import com.google.common.collect.ImmutableSortedSet;
|
import com.google.common.collect.ImmutableSortedSet;
|
||||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||||
@ -172,7 +173,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable i
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public Collection<S> findSites(final String query) {
|
public ImmutableCollection<S> findSites(final String query) {
|
||||||
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
||||||
for (final S site : getSites())
|
for (final S site : getSites())
|
||||||
if (site.getName().startsWith( query ))
|
if (site.getName().startsWith( query ))
|
||||||
|
Loading…
Reference in New Issue
Block a user