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 java.awt.*;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
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 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 );
|
||||
// container.setBackground( Color.red );
|
||||
container.setLayout( new BoxLayout( container, axis ) );
|
||||
for (final Component component : components)
|
||||
container.add( component );
|
||||
@ -44,12 +44,13 @@ public abstract class Components {
|
||||
return container;
|
||||
}
|
||||
|
||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border) {
|
||||
return borderPanel( component, border, null );
|
||||
public static GradientPanel borderPanel(@Nullable final Border border, final Component... components) {
|
||||
return borderPanel( border, null, components );
|
||||
}
|
||||
|
||||
public static GradientPanel borderPanel(final JComponent component, @Nullable final Border border, @Nullable final Color background) {
|
||||
GradientPanel box = boxLayout( BoxLayout.LINE_AXIS, component );
|
||||
public static GradientPanel borderPanel(@Nullable final Border border, @Nullable final Color background,
|
||||
final Component... components) {
|
||||
GradientPanel box = boxPanel( BoxLayout.LINE_AXIS, components );
|
||||
|
||||
if (border != null)
|
||||
box.setBorder( border );
|
||||
@ -60,11 +61,11 @@ public abstract class Components {
|
||||
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 ) {
|
||||
{
|
||||
setOpaque( color != null );
|
||||
setBackground( null );
|
||||
setBackground( color );
|
||||
setAlignmentX( LEFT_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) {
|
||||
return new JButton( label ) {
|
||||
{
|
||||
@ -219,14 +259,28 @@ public abstract class Components {
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <V> JComboBox<V> comboBox(final V... values) {
|
||||
return comboBox( new DefaultComboBoxModel<>( values ) );
|
||||
public static <E> JComboBox<E> comboBox(final Function<E, String> valueTransformer, final E... values) {
|
||||
return comboBox( new DefaultComboBoxModel<>( values ), valueTransformer );
|
||||
}
|
||||
|
||||
public static <M> JComboBox<M> comboBox(final ComboBoxModel<M> model) {
|
||||
return new JComboBox<M>( model ) {
|
||||
public static <E> JComboBox<E> comboBox(final ComboBoxModel<E> model, final Function<E, String> valueTransformer) {
|
||||
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 );
|
||||
setAlignmentY( BOTTOM_ALIGNMENT );
|
||||
}
|
||||
|
@ -1,28 +1,30 @@
|
||||
package com.lyndir.masterpassword.gui.view;
|
||||
|
||||
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.*;
|
||||
import java.util.Set;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.metal.MetalComboBoxEditor;
|
||||
|
||||
|
||||
/**
|
||||
* @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 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() {
|
||||
setOpaque( false );
|
||||
@ -43,25 +45,7 @@ public class FilesPanel extends JPanel implements ActionListener {
|
||||
|
||||
// User Selection
|
||||
add( userField );
|
||||
userField.addActionListener( 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;
|
||||
}
|
||||
} );
|
||||
userField.addItemListener( this );
|
||||
|
||||
// -
|
||||
add( Box.createVerticalGlue() );
|
||||
@ -69,36 +53,25 @@ public class FilesPanel extends JPanel implements ActionListener {
|
||||
|
||||
public void reload() {
|
||||
MPFileUserManager.get().reload();
|
||||
userField.setModel( new DefaultComboBoxModel<>( MPFileUserManager.get().getFiles().toArray( new MPFileUser[0] ) ) );
|
||||
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 );
|
||||
usersModel.set( MPFileUserManager.get().getFiles() );
|
||||
}
|
||||
|
||||
public boolean addListener(final Listener 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 {
|
||||
|
||||
void onUserSelected(@Nullable MPUser<?> selectedUser);
|
||||
|
@ -23,12 +23,14 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener {
|
||||
super( "Master Password" );
|
||||
|
||||
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.setBorder( BorderFactory.createEmptyBorder( 20, 20, 20, 20 ) );
|
||||
|
||||
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.reload();
|
||||
|
@ -2,22 +2,25 @@ package com.lyndir.masterpassword.gui.view;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.primitives.UnsignedInteger;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
import com.lyndir.masterpassword.*;
|
||||
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.MPIncorrectMasterPasswordException;
|
||||
import com.lyndir.masterpassword.model.MPUser;
|
||||
import com.lyndir.masterpassword.model.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.event.*;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.event.*;
|
||||
|
||||
|
||||
/**
|
||||
@ -128,23 +131,24 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(final DocumentEvent e) {
|
||||
public void insertUpdate(final DocumentEvent event) {
|
||||
errorLabel.setText( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
public void removeUpdate(final DocumentEvent event) {
|
||||
errorLabel.setText( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
public void changedUpdate(final DocumentEvent event) {
|
||||
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
|
||||
private final MPUser<?> user;
|
||||
@ -152,6 +156,10 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
||||
private final JLabel passwordField = Components.heading( " ", SwingConstants.CENTER );
|
||||
private final JLabel queryLabel = Components.label( " " );
|
||||
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) {
|
||||
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.setFont( Res.fonts().bigValueFont().deriveFont( Font.BOLD, 48 ) );
|
||||
add( Box.createGlue() );
|
||||
add( Components.strut() );
|
||||
|
||||
add( queryLabel );
|
||||
queryLabel.setText( strf( "%s's password for:", user.getFullName() ) );
|
||||
add( queryField );
|
||||
queryField.addActionListener( this );
|
||||
queryField.addKeyListener( this );
|
||||
queryField.getDocument().addDocumentListener( this );
|
||||
add( Components.strut() );
|
||||
add( Components.scrollPane( sitesList ) );
|
||||
sitesList.addListSelectionListener( this );
|
||||
add( Box.createGlue() );
|
||||
|
||||
Res.ui( false, queryField::requestFocusInWindow );
|
||||
@ -179,12 +192,59 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
|
||||
|
||||
@Override
|
||||
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( () -> {
|
||||
try {
|
||||
String result = user.getMasterKey().siteResult(
|
||||
siteName, user.getAlgorithm(), UnsignedInteger.ONE,
|
||||
MPKeyPurpose.Authentication, null, MPResultType.GeneratedLong, null );
|
||||
if (resultCallback != null)
|
||||
resultCallback.accept( result );
|
||||
|
||||
Res.ui( () -> {
|
||||
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) {
|
||||
logger.err( e, "While resolving password for: %s", siteName );
|
||||
logger.err( e, "While resolving password for: %s", site );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(final DocumentEvent e) {
|
||||
// TODO
|
||||
|
||||
public void keyTyped(final KeyEvent event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
// TODO
|
||||
|
||||
public void keyPressed(final KeyEvent event) {
|
||||
if ((event.getKeyCode() == KeyEvent.VK_UP) || (event.getKeyCode() == KeyEvent.VK_DOWN))
|
||||
sitesList.dispatchEvent( event );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
// TODO
|
||||
public void keyReleased(final KeyEvent event) {
|
||||
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;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.lyndir.masterpassword.*;
|
||||
import com.lyndir.masterpassword.model.impl.MPBasicSite;
|
||||
import com.lyndir.masterpassword.model.impl.MPBasicUser;
|
||||
@ -94,7 +95,7 @@ public interface MPUser<S extends MPSite<?>> extends Comparable<MPUser<?>> {
|
||||
Collection<S> getSites();
|
||||
|
||||
@Nonnull
|
||||
Collection<S> findSites(String query);
|
||||
ImmutableCollection<S> findSites(String query);
|
||||
|
||||
boolean addListener(Listener listener);
|
||||
|
||||
|
@ -20,6 +20,7 @@ package com.lyndir.masterpassword.model.impl;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.lyndir.lhunath.opal.system.CodeUtils;
|
||||
import com.lyndir.lhunath.opal.system.logging.Logger;
|
||||
@ -172,7 +173,7 @@ public abstract class MPBasicUser<S extends MPBasicSite<?>> extends Changeable i
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Collection<S> findSites(final String query) {
|
||||
public ImmutableCollection<S> findSites(final String query) {
|
||||
ImmutableSortedSet.Builder<S> results = ImmutableSortedSet.naturalOrder();
|
||||
for (final S site : getSites())
|
||||
if (site.getName().startsWith( query ))
|
||||
|
Loading…
Reference in New Issue
Block a user