2
0

Avatar configuration & move preferences into user panel.

This commit is contained in:
Maarten Billemont 2018-07-26 15:07:37 -04:00
parent 7c83a62f91
commit e639137304
13 changed files with 196 additions and 168 deletions

View File

@ -24,7 +24,7 @@
</value> </value>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" project-jdk-name="10" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -0,0 +1,25 @@
package com.lyndir.masterpassword.util;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;
/**
* @author lhunath, 2018-07-25
*/
public final class Utilities {
@Nullable
public static <T, R> R ifNotNull(@Nullable final T value, final Function<T, R> consumer) {
if (value == null)
return null;
return consumer.apply( value );
}
public static <T> void ifNotNullDo(@Nullable final T value, final Consumer<T> consumer) {
if (value != null)
consumer.accept( value );
}
}

View File

@ -0,0 +1,7 @@
/**
* @author lhunath, 2018-07-25
*/
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.util;
import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -24,6 +24,7 @@ import com.google.common.base.Charsets;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.TypeUtils; import com.lyndir.lhunath.opal.system.util.TypeUtils;
import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.gui.view.MasterPasswordFrame; import com.lyndir.masterpassword.gui.view.MasterPasswordFrame;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;

View File

@ -16,7 +16,7 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
package com.lyndir.masterpassword.gui.platform.mac; package com.lyndir.masterpassword.gui.platform.macos;
import com.apple.eawt.*; import com.apple.eawt.*;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;

View File

@ -20,6 +20,6 @@
* @author lhunath, 2018-04-26 * @author lhunath, 2018-04-26
*/ */
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
package com.lyndir.masterpassword.gui.platform.mac; package com.lyndir.masterpassword.gui.platform.macos;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;

View File

@ -27,13 +27,16 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
return copy( Arrays.asList( elements ) ); return copy( Arrays.asList( elements ) );
} }
public static <E> CollectionListModel<E> copy(final Collection<E> elements) { public static <E> CollectionListModel<E> copy(final Collection<? extends E> elements) {
CollectionListModel<E> model = new CollectionListModel<>(); CollectionListModel<E> model = new CollectionListModel<>();
synchronized (model) {
model.model.addAll( elements ); model.model.addAll( elements );
model.selectedItem = model.getElementAt( 0 );
model.fireIntervalAdded( model, 0, model.model.size() ); model.fireIntervalAdded( model, 0, model.model.size() );
return model; return model;
} }
}
@Override @Override
public synchronized int getSize() { public synchronized int getSize() {
@ -41,8 +44,9 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
} }
@Override @Override
@Nullable
public synchronized E getElementAt(final int index) { public synchronized E getElementAt(final int index) {
return model.get( index ); return (index < model.size())? model.get( index ): null;
} }
/** /**
@ -76,10 +80,8 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
} }
} }
if ((selectedItem == null) && !model.isEmpty()) if ((selectedItem == null) || !model.contains( selectedItem ))
setSelectedItem( model.get( 0 ) ); setSelectedItem( getElementAt( 0 ) );
else if (!model.contains( selectedItem ))
setSelectedItem( null );
} }
@Override @Override
@ -114,15 +116,19 @@ public class CollectionListModel<E> extends AbstractListModel<E> implements Comb
this.list.setModel( this ); this.list.setModel( this );
} }
public CollectionListModel<E> selection(@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) { public synchronized CollectionListModel<E> selection(@Nullable final Consumer<E> selectionConsumer) {
this.selectionConsumer = selectionConsumer;
if (selectionConsumer != null)
selectionConsumer.accept( selectedItem );
return this;
}
public synchronized CollectionListModel<E> selection(@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
this.selectionConsumer = null; this.selectionConsumer = null;
setSelectedItem( selectedItem ); setSelectedItem( selectedItem );
this.selectionConsumer = selectionConsumer; return selection( selectionConsumer );
if (this.selectionConsumer != null)
this.selectionConsumer.accept( selectedItem );
return this;
} }
@Override @Override

View File

@ -18,8 +18,9 @@
package com.lyndir.masterpassword.gui.util; package com.lyndir.masterpassword.gui.util;
import com.lyndir.masterpassword.gui.Res;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection; import java.util.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@ -32,7 +33,7 @@ import javax.swing.border.CompoundBorder;
/** /**
* @author lhunath, 2014-06-08 * @author lhunath, 2014-06-08
*/ */
@SuppressWarnings("SerializableStoresNonSerializable") @SuppressWarnings({ "SerializableStoresNonSerializable", "serial" })
public abstract class Components { public abstract class Components {
public static final float TEXT_SIZE_HEADING = 19f; public static final float TEXT_SIZE_HEADING = 19f;
@ -45,7 +46,7 @@ public abstract class Components {
} }
public static GradientPanel panel(final int axis, @Nullable final Color background, final Component... components) { public static GradientPanel panel(final int axis, @Nullable final Color background, final Component... components) {
GradientPanel container = gradientPanel( background, null ); GradientPanel container = panel( null, background );
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 );
@ -74,14 +75,8 @@ public abstract class Components {
return box; return box;
} }
public static GradientPanel gradientPanel(@Nullable final Color color, @Nullable final LayoutManager layout) { public static GradientPanel panel(@Nullable final LayoutManager layout, @Nullable final Color color) {
return new GradientPanel( layout, color ) { return new GradientPanel( layout, color );
{
setOpaque( color != null );
setBackground( color );
setAlignmentX( LEFT_ALIGNMENT );
}
};
} }
public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final JOptionPane pane) { public static JDialog showDialog(@Nullable final Component owner, @Nullable final String title, final JOptionPane pane) {
@ -181,17 +176,40 @@ public abstract class Components {
}; };
} }
public static JButton button(final String label) { public static JButton button(final String label, @Nullable final ActionListener actionListener) {
return button( label, null ); return button( new AbstractAction( label ) {
@Override
public void actionPerformed(final ActionEvent e) {
if (actionListener != null)
actionListener.actionPerformed( e );
}
} );
} }
public static JButton button(final String label, @Nullable final Action action) { public static JButton button(final Icon icon, @Nullable final ActionListener actionListener) {
return new JButton( label ) { JButton iconButton = button( new AbstractAction( null, icon ) {
@Override
public void actionPerformed(final ActionEvent e) {
if (actionListener != null)
actionListener.actionPerformed( e );
}
} );
iconButton.setFocusable( false );
return iconButton;
}
public static JButton button(final Action action) {
return new JButton( action ) {
{ {
setFont( Res.fonts().controlFont( TEXT_SIZE_CONTROL ) ); setFont( Res.fonts().controlFont( TEXT_SIZE_CONTROL ) );
setAlignmentX( LEFT_ALIGNMENT ); setAlignmentX( LEFT_ALIGNMENT );
if (action != null)
setAction( action ); if (getText() == null) {
setContentAreaFilled( false );
setBorderPainted( false );
setOpaque( false );
}
} }
}; };
} }
@ -323,13 +341,18 @@ public abstract class Components {
return comboBox( new DefaultComboBoxModel<>( values ), valueTransformer ); return comboBox( new DefaultComboBoxModel<>( values ), valueTransformer );
} }
public static <E> JComboBox<E> comboBox(final E[] values, final Function<E, String> valueTransformer, final E selectedItem, public static <E> JComboBox<E> comboBox(final E[] values, final Function<E, String> valueTransformer,
@Nullable final Consumer<E> selectionConsumer) { @Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer ); 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, public static <E> JComboBox<E> comboBox(final Collection<E> values, final Function<E, String> valueTransformer,
@Nullable final Consumer<E> selectionConsumer) { @Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectionConsumer ), valueTransformer );
}
public static <E> JComboBox<E> comboBox(final Collection<E> values, final Function<E, String> valueTransformer,
@Nullable final E selectedItem, @Nullable final Consumer<E> selectionConsumer) {
return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer ); return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer );
} }
@ -378,10 +401,15 @@ public abstract class Components {
this( null, gradientColor ); this( null, gradientColor );
} }
public GradientPanel(@Nullable final LayoutManager layout) {
this( layout, null );
}
public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) { public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) {
super( layout ); super( layout );
this.gradientColor = gradientColor; this.gradientColor = gradientColor;
setBackground( null ); setBackground( null );
setAlignmentX( LEFT_ALIGNMENT );
} }
@Nullable @Nullable

View File

@ -16,9 +16,8 @@
// LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>. // LICENSE file. Alternatively, see <http://www.gnu.org/licenses/>.
//============================================================================== //==============================================================================
package com.lyndir.masterpassword.gui; package com.lyndir.masterpassword.gui.util;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*; import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@ -26,15 +25,13 @@ import com.google.common.io.Resources;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.MPIdenticon; import com.lyndir.masterpassword.MPIdenticon;
import com.lyndir.masterpassword.gui.SwingExecutorService;
import java.awt.*; import java.awt.*;
import java.awt.image.ImageObserver;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.Map; import java.util.Map;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.regex.Matcher; import javax.annotation.Nullable;
import java.util.regex.Pattern;
import javax.swing.*; import javax.swing.*;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
@ -42,7 +39,7 @@ import org.jetbrains.annotations.NonNls;
/** /**
* @author lhunath, 2014-06-11 * @author lhunath, 2014-06-11
*/ */
@SuppressWarnings({ "HardcodedFileSeparator", "MethodReturnAlwaysConstant", "SpellCheckingInspection" }) @SuppressWarnings({ "HardcodedFileSeparator", "MethodReturnAlwaysConstant", "SpellCheckingInspection", "serial" })
public abstract class Res { public abstract class Res {
private static final int AVATAR_COUNT = 19; private static final int AVATAR_COUNT = 19;
@ -51,6 +48,7 @@ public abstract class Res {
private static final Executor immediateUiExecutor = new SwingExecutorService( true ); private static final Executor immediateUiExecutor = new SwingExecutorService( true );
private static final Executor laterUiExecutor = new SwingExecutorService( false ); private static final Executor laterUiExecutor = new SwingExecutorService( false );
private static final Logger logger = Logger.get( Res.class ); private static final Logger logger = Logger.get( Res.class );
private static final Icons icons = new Icons();
private static final Fonts fonts = new Fonts(); private static final Fonts fonts = new Fonts();
private static final Colors colors = new Colors(); private static final Colors colors = new Colors();
@ -93,24 +91,8 @@ public abstract class Res {
return immediate? immediateUiExecutor: laterUiExecutor; return immediate? immediateUiExecutor: laterUiExecutor;
} }
public static Icon iconAdd() { public static Icons icons() {
return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) ); return icons;
}
public static Icon iconDelete() {
return new RetinaIcon( Resources.getResource( "media/icon_delete@2x.png" ) );
}
public static Icon iconQuestion() {
return new RetinaIcon( Resources.getResource( "media/icon_question@2x.png" ) );
}
public static Icon avatar(final int index) {
return new RetinaIcon( Resources.getResource( strf( "media/avatar-%d@2x.png", index % avatars() ) ) );
}
public static int avatars() {
return AVATAR_COUNT;
} }
public static Fonts fonts() { public static Fonts fonts() {
@ -121,64 +103,39 @@ public abstract class Res {
return colors; return colors;
} }
private static final class RetinaIcon extends ImageIcon { public static final class Icons {
private static final Pattern scalePattern = Pattern.compile( ".*@(\\d+)x.[^.]+$" ); public Icon add() {
private static final long serialVersionUID = 1L; return icon( "media/icon_add.png" );
private final float scale;
private RetinaIcon(final URL url) {
super( url );
Matcher scaleMatcher = scalePattern.matcher( url.getPath() );
scale = scaleMatcher.matches()? Float.parseFloat( scaleMatcher.group( 1 ) ): 1;
} }
//private static URL retinaURL(final URL url) { public Icon delete() {
// try { return icon( "media/icon_delete.png" );
// final boolean[] isRetina = new boolean[1];
// new apple.awt.CImage.HiDPIScaledImage(1,1, BufferedImage.TYPE_INT_ARGB) {
// @Override
// public void drawIntoImage(BufferedImage image, float v) {
// isRetina[0] = v > 1;
// }
// };
// return isRetina[0];
// } catch (Throwable e) {
// e.printStackTrace();
// return url;
// }
//}
@Override
public int getIconWidth() {
return (int) (super.getIconWidth() / scale);
} }
@Override public Icon question() {
public int getIconHeight() { return icon( "media/icon_question.png" );
return (int) (super.getIconHeight() / scale);
} }
@Override public Icon user() {
public synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { return icon( "media/icon_user.png" );
ImageObserver observer = ifNotNullElse( getImageObserver(), c ); }
Image image = getImage(); public Icon avatar(final int index) {
int width = image.getWidth( observer ); return icon( strf( "media/avatar-%d.png", index % avatars() ) );
int height = image.getHeight( observer ); }
Graphics2D g2d = (Graphics2D) g.create( x, y, width, height );
g2d.scale( 1 / scale, 1 / scale ); public int avatars() {
g2d.drawImage( image, 0, 0, observer ); return AVATAR_COUNT;
g2d.scale( 1, 1 ); }
g2d.dispose();
private static Icon icon(@NonNls final String resourceName) {
return new ImageIcon( Toolkit.getDefaultToolkit().getImage( Res.class.getClassLoader().getResource( resourceName ) ) );
} }
} }
public static class Fonts { public static final class Fonts {
public Font emoticonsFont(final float size) { public Font emoticonsFont(final float size) {
return emoticonsRegular().deriveFont( size ); return emoticonsRegular().deriveFont( size );
@ -266,7 +223,7 @@ public abstract class Res {
} }
public static class Colors { public static final class Colors {
private final Color transparent = new Color( 0, 0, 0, 0 ); private final Color transparent = new Color( 0, 0, 0, 0 );
private final Color frameBg = Color.decode( "#5A5D6B" ); private final Color frameBg = Color.decode( "#5A5D6B" );

View File

@ -1,16 +1,13 @@
package com.lyndir.masterpassword.gui.view; package com.lyndir.masterpassword.gui.view;
import com.google.common.collect.ImmutableList; import static com.lyndir.masterpassword.util.Utilities.*;
import com.lyndir.masterpassword.MPAlgorithm;
import com.lyndir.masterpassword.MPResultType; import com.lyndir.masterpassword.gui.util.Res;
import com.lyndir.masterpassword.gui.Res;
import com.lyndir.masterpassword.gui.util.CollectionListModel; 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.util.Collection; import java.util.Collection;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -21,15 +18,16 @@ import javax.swing.*;
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class FilesPanel extends JPanel implements ItemListener { public class FilesPanel extends JPanel {
private final Collection<Listener> listeners = new CopyOnWriteArraySet<>(); private final Collection<Listener> listeners = new CopyOnWriteArraySet<>();
private final JLabel avatarLabel = new JLabel(); private final JButton avatarButton = Components.button( Res.icons().avatar( 0 ), event -> setAvatar() );
private final CollectionListModel<MPUser<?>> usersModel = new CollectionListModel<>();
private final JComboBox<MPUser<?>> userField = private final CollectionListModel<MPUser<?>> usersModel =
Components.comboBox( usersModel, user -> (user != null)? user.getFullName(): null ); CollectionListModel.<MPUser<?>>copy( MPFileUserManager.get().getFiles() ).selection( this::setUser );
private final JButton preferencesButton = Components.button( "..." ); private final JComboBox<? extends MPUser<?>> userField =
Components.comboBox( usersModel, user -> ifNotNull( user, MPUser::getFullName ) );
protected FilesPanel() { protected FilesPanel() {
setOpaque( false ); setOpaque( false );
@ -40,66 +38,45 @@ public class FilesPanel extends JPanel implements ItemListener {
add( Box.createVerticalGlue() ); add( Box.createVerticalGlue() );
// Avatar // Avatar
add( avatarLabel ); add( avatarButton );
avatarLabel.setHorizontalAlignment( SwingConstants.CENTER ); avatarButton.setHorizontalAlignment( SwingConstants.CENTER );
avatarLabel.setMaximumSize( new Dimension( Integer.MAX_VALUE, 0 ) ); avatarButton.setMaximumSize( new Dimension( Integer.MAX_VALUE, 0 ) );
avatarLabel.setToolTipText( "The avatar for your user. Click to change it." ); avatarButton.setToolTipText( "The avatar for your user. Click to change it." );
// - // -
add( Components.strut( Components.margin() ) ); add( Components.strut( Components.margin() ) );
// User Selection // User Selection
add( Components.panel( BoxLayout.LINE_AXIS, userField, preferencesButton ) ); add( userField );
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 );
} }
public void reload() { public void reload() {
MPFileUserManager.get().reload(); // TODO: Should we use a listener here instead?
usersModel.set( MPFileUserManager.get().getFiles() ); usersModel.set( MPFileUserManager.get().reload() );
} }
public boolean addListener(final Listener listener) { public boolean addListener(final Listener listener) {
return listeners.add( listener ); return listeners.add( listener );
} }
@Override private void setAvatar() {
public void itemStateChanged(final ItemEvent e) { MPUser<?> selectedUser = usersModel.getSelectedItem();
if (e.getStateChange() != ItemEvent.SELECTED) if (selectedUser == null)
return; return;
MPUser<?> selectedUser = usersModel.getSelectedItem(); selectedUser.setAvatar( (selectedUser.getAvatar() + 1) % Res.icons().avatars() );
avatarLabel.setIcon( Res.avatar( (selectedUser == null)? 0: selectedUser.getAvatar() ) ); avatarButton.setIcon( Res.icons().avatar( selectedUser.getAvatar() ) );
}
public void setUser(@Nullable final MPUser<?> user) {
avatarButton.setIcon( Res.icons().avatar( (user == null)? 0: user.getAvatar() ) );
for (final Listener listener : listeners) for (final Listener listener : listeners)
listener.onUserSelected( selectedUser ); listener.onUserSelected( user );
} }
public interface Listener { public interface Listener {
void onUserSelected(@Nullable MPUser<?> selectedUser); void onUserSelected(@Nullable MPUser<?> user);
} }
} }

View File

@ -1,7 +1,7 @@
package com.lyndir.masterpassword.gui.view; package com.lyndir.masterpassword.gui.view;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.gui.Res; import com.lyndir.masterpassword.gui.util.Res;
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 java.awt.*; import java.awt.*;
@ -50,8 +50,8 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener,
} }
@Override @Override
public void onUserSelected(@Nullable final MPUser<?> selectedUser) { public void onUserSelected(@Nullable final MPUser<?> user) {
userPanel.setUser( selectedUser ); userPanel.setUser( user );
} }
@Override @Override

View File

@ -3,10 +3,11 @@ 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.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
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.util.Res;
import com.lyndir.masterpassword.gui.util.CollectionListModel; 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.*; import com.lyndir.masterpassword.model.*;
@ -30,6 +31,7 @@ import javax.swing.event.*;
/** /**
* @author lhunath, 2018-07-14 * @author lhunath, 2018-07-14
*/ */
@SuppressWarnings("SerializableStoresNonSerializable")
public class UserPanel extends Components.GradientPanel implements MPUser.Listener { public class UserPanel extends Components.GradientPanel implements MPUser.Listener {
private static final Logger logger = Logger.get( UserPanel.class ); private static final Logger logger = Logger.get( UserPanel.class );
@ -212,8 +214,12 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
this.user = user; this.user = user;
add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); add( new Components.GradientPanel( new BorderLayout() ) {
add( Components.strut() ); {
add( Components.heading( user.getFullName(), SwingConstants.CENTER ), BorderLayout.CENTER );
add( Components.button( Res.icons().user(), event -> showPreferences() ), BorderLayout.LINE_END );
}
} );
add( passwordLabel ); add( passwordLabel );
add( passwordField ); add( passwordField );
@ -237,6 +243,25 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen
add( Box.createGlue() ); add( Box.createGlue() );
} }
public void showPreferences() {
ImmutableList.Builder<Component> components = ImmutableList.builder();
MPFileUser fileUser = (user instanceof MPFileUser)? (MPFileUser) user: null;
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( this, user.getFullName(), new JOptionPane( Components.panel(
BoxLayout.PAGE_AXIS, components.build().toArray( new Component[0] ) ) ) );
}
@Override @Override
public void actionPerformed(final ActionEvent event) { public void actionPerformed(final ActionEvent event) {
MPSite<?> site = sitesList.getSelectedValue(); MPSite<?> site = sitesList.getSelectedValue();

View File

@ -67,13 +67,13 @@ public class MPFileUserManager {
this.path = path; this.path = path;
} }
public void reload() { public ImmutableSortedSet<MPFileUser> reload() {
userByName.clear(); userByName.clear();
File[] pathFiles; File[] pathFiles;
if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) { if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) {
logger.err( "Couldn't create directory for user files: %s", path ); logger.err( "Couldn't create directory for user files: %s", path );
return; return getFiles();
} }
for (final File file : pathFiles) for (final File file : pathFiles)
@ -89,6 +89,8 @@ public class MPFileUserManager {
catch (final IOException | MPMarshalException e) { catch (final IOException | MPMarshalException e) {
logger.err( e, "Couldn't read user from: %s", file ); logger.err( e, "Couldn't read user from: %s", file );
} }
return getFiles();
} }
public MPFileUser add(final String fullName) { public MPFileUser add(final String fullName) {