From e639137304d18b0e2b6a3ebce579e4fde7abac41 Mon Sep 17 00:00:00 2001 From: Maarten Billemont Date: Thu, 26 Jul 2018 15:07:37 -0400 Subject: [PATCH] Avatar configuration & move preferences into user panel. --- gradle/.idea/misc.xml | 2 +- .../lyndir/masterpassword/util/Utilities.java | 25 +++++ .../masterpassword/util/package-info.java | 7 ++ .../com/lyndir/masterpassword/gui/GUI.java | 1 + .../gui/platform/{mac => macos}/AppleGUI.java | 2 +- .../platform/{mac => macos}/package-info.java | 2 +- .../gui/util/CollectionListModel.java | 36 ++++--- .../masterpassword/gui/util/Components.java | 68 +++++++++---- .../masterpassword/gui/{ => util}/Res.java | 99 ++++++------------- .../masterpassword/gui/view/FilesPanel.java | 79 ++++++--------- .../gui/view/MasterPasswordFrame.java | 6 +- .../masterpassword/gui/view/UserPanel.java | 31 +++++- .../model/impl/MPFileUserManager.java | 6 +- 13 files changed, 196 insertions(+), 168 deletions(-) create mode 100644 platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/Utilities.java create mode 100644 platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/package-info.java rename platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/{mac => macos}/AppleGUI.java (97%) rename platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/{mac => macos}/package-info.java (94%) rename platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/{ => util}/Res.java (77%) diff --git a/gradle/.idea/misc.xml b/gradle/.idea/misc.xml index 88b86d0a..f8dfb26d 100644 --- a/gradle/.idea/misc.xml +++ b/gradle/.idea/misc.xml @@ -24,7 +24,7 @@ - + diff --git a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/Utilities.java b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/Utilities.java new file mode 100644 index 00000000..e0e83937 --- /dev/null +++ b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/Utilities.java @@ -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 R ifNotNull(@Nullable final T value, final Function consumer) { + if (value == null) + return null; + + return consumer.apply( value ); + } + + public static void ifNotNullDo(@Nullable final T value, final Consumer consumer) { + if (value != null) + consumer.accept( value ); + } +} diff --git a/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/package-info.java b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/package-info.java new file mode 100644 index 00000000..3f134b8b --- /dev/null +++ b/platform-independent/java/algorithm/src/main/java/com/lyndir/masterpassword/util/package-info.java @@ -0,0 +1,7 @@ +/** + * @author lhunath, 2018-07-25 + */ +@ParametersAreNonnullByDefault +package com.lyndir.masterpassword.util; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java index 23f42e3b..e6a5ea8c 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/GUI.java @@ -24,6 +24,7 @@ import com.google.common.base.Charsets; import com.google.common.io.ByteSource; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.util.TypeUtils; +import com.lyndir.masterpassword.gui.util.Res; import com.lyndir.masterpassword.gui.view.MasterPasswordFrame; import java.io.IOException; import java.io.InputStream; diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/AppleGUI.java similarity index 97% rename from platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java rename to platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/AppleGUI.java index 33650a31..2a7c109f 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/AppleGUI.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/AppleGUI.java @@ -16,7 +16,7 @@ // LICENSE file. Alternatively, see . //============================================================================== -package com.lyndir.masterpassword.gui.platform.mac; +package com.lyndir.masterpassword.gui.platform.macos; import com.apple.eawt.*; import com.google.common.base.Preconditions; diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/package-info.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/package-info.java similarity index 94% rename from platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/package-info.java rename to platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/package-info.java index 732e2bef..1994f1f5 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/mac/package-info.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/platform/macos/package-info.java @@ -20,6 +20,6 @@ * @author lhunath, 2018-04-26 */ @ParametersAreNonnullByDefault -package com.lyndir.masterpassword.gui.platform.mac; +package com.lyndir.masterpassword.gui.platform.macos; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/CollectionListModel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/CollectionListModel.java index 6de1ae74..9ab70ae4 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/CollectionListModel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/CollectionListModel.java @@ -27,12 +27,15 @@ public class CollectionListModel extends AbstractListModel implements Comb return copy( Arrays.asList( elements ) ); } - public static CollectionListModel copy(final Collection elements) { + public static CollectionListModel copy(final Collection elements) { CollectionListModel model = new CollectionListModel<>(); - model.model.addAll( elements ); - model.fireIntervalAdded( model, 0, model.model.size() ); + synchronized (model) { + model.model.addAll( elements ); + model.selectedItem = model.getElementAt( 0 ); + model.fireIntervalAdded( model, 0, model.model.size() ); - return model; + return model; + } } @Override @@ -41,8 +44,9 @@ public class CollectionListModel extends AbstractListModel implements Comb } @Override + @Nullable 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 extends AbstractListModel implements Comb } } - if ((selectedItem == null) && !model.isEmpty()) - setSelectedItem( model.get( 0 ) ); - else if (!model.contains( selectedItem )) - setSelectedItem( null ); + if ((selectedItem == null) || !model.contains( selectedItem )) + setSelectedItem( getElementAt( 0 ) ); } @Override @@ -114,15 +116,19 @@ public class CollectionListModel extends AbstractListModel implements Comb this.list.setModel( this ); } - public CollectionListModel selection(@Nullable final E selectedItem, @Nullable final Consumer selectionConsumer) { + public synchronized CollectionListModel selection(@Nullable final Consumer selectionConsumer) { + this.selectionConsumer = selectionConsumer; + if (selectionConsumer != null) + selectionConsumer.accept( selectedItem ); + + return this; + } + + public synchronized CollectionListModel selection(@Nullable final E selectedItem, @Nullable final Consumer selectionConsumer) { this.selectionConsumer = null; setSelectedItem( selectedItem ); - this.selectionConsumer = selectionConsumer; - if (this.selectionConsumer != null) - this.selectionConsumer.accept( selectedItem ); - - return this; + return selection( selectionConsumer ); } @Override diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java index 4864276e..294337c7 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Components.java @@ -18,8 +18,9 @@ package com.lyndir.masterpassword.gui.util; -import com.lyndir.masterpassword.gui.Res; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.Collection; import java.util.function.Consumer; import java.util.function.Function; @@ -32,7 +33,7 @@ import javax.swing.border.CompoundBorder; /** * @author lhunath, 2014-06-08 */ -@SuppressWarnings("SerializableStoresNonSerializable") +@SuppressWarnings({ "SerializableStoresNonSerializable", "serial" }) public abstract class Components { 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) { - GradientPanel container = gradientPanel( background, null ); + GradientPanel container = panel( null, background ); container.setLayout( new BoxLayout( container, axis ) ); for (final Component component : components) container.add( component ); @@ -74,14 +75,8 @@ public abstract class Components { return box; } - public static GradientPanel gradientPanel(@Nullable final Color color, @Nullable final LayoutManager layout) { - return new GradientPanel( layout, color ) { - { - setOpaque( color != null ); - setBackground( color ); - setAlignmentX( LEFT_ALIGNMENT ); - } - }; + public static GradientPanel panel(@Nullable final LayoutManager layout, @Nullable final Color color) { + return new GradientPanel( layout, color ); } 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) { - return button( label, null ); + public static JButton button(final String label, @Nullable final ActionListener actionListener) { + 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) { - return new JButton( label ) { + public static JButton button(final Icon icon, @Nullable final ActionListener actionListener) { + 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 ) ); 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 ); } - public static JComboBox comboBox(final E[] values, final Function valueTransformer, final E selectedItem, - @Nullable final Consumer selectionConsumer) { + public static JComboBox comboBox(final E[] values, final Function valueTransformer, + @Nullable final E selectedItem, @Nullable final Consumer selectionConsumer) { return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer ); } - public static JComboBox comboBox(final Collection values, final Function valueTransformer, final E selectedItem, + public static JComboBox comboBox(final Collection values, final Function valueTransformer, @Nullable final Consumer selectionConsumer) { + return comboBox( CollectionListModel.copy( values ).selection( selectionConsumer ), valueTransformer ); + } + + public static JComboBox comboBox(final Collection values, final Function valueTransformer, + @Nullable final E selectedItem, @Nullable final Consumer selectionConsumer) { return comboBox( CollectionListModel.copy( values ).selection( selectedItem, selectionConsumer ), valueTransformer ); } @@ -378,10 +401,15 @@ public abstract class Components { this( null, gradientColor ); } + public GradientPanel(@Nullable final LayoutManager layout) { + this( layout, null ); + } + public GradientPanel(@Nullable final LayoutManager layout, @Nullable final Color gradientColor) { super( layout ); this.gradientColor = gradientColor; setBackground( null ); + setAlignmentX( LEFT_ALIGNMENT ); } @Nullable diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/Res.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java similarity index 77% rename from platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/Res.java rename to platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java index 66aec042..dbdaa367 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/Res.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/util/Res.java @@ -16,9 +16,8 @@ // LICENSE file. Alternatively, see . //============================================================================== -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 com.google.common.collect.Maps; @@ -26,15 +25,13 @@ import com.google.common.io.Resources; import com.google.common.util.concurrent.*; import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.masterpassword.MPIdenticon; +import com.lyndir.masterpassword.gui.SwingExecutorService; import java.awt.*; -import java.awt.image.ImageObserver; import java.io.IOException; import java.lang.ref.SoftReference; -import java.net.URL; import java.util.Map; import java.util.concurrent.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import javax.annotation.Nullable; import javax.swing.*; import org.jetbrains.annotations.NonNls; @@ -42,7 +39,7 @@ import org.jetbrains.annotations.NonNls; /** * @author lhunath, 2014-06-11 */ -@SuppressWarnings({ "HardcodedFileSeparator", "MethodReturnAlwaysConstant", "SpellCheckingInspection" }) +@SuppressWarnings({ "HardcodedFileSeparator", "MethodReturnAlwaysConstant", "SpellCheckingInspection", "serial" }) public abstract class Res { 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 laterUiExecutor = new SwingExecutorService( false ); 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 Colors colors = new Colors(); @@ -93,24 +91,8 @@ public abstract class Res { return immediate? immediateUiExecutor: laterUiExecutor; } - public static Icon iconAdd() { - return new RetinaIcon( Resources.getResource( "media/icon_add@2x.png" ) ); - } - - 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 Icons icons() { + return icons; } public static Fonts fonts() { @@ -121,64 +103,39 @@ public abstract class Res { return colors; } - private static final class RetinaIcon extends ImageIcon { + public static final class Icons { - private static final Pattern scalePattern = Pattern.compile( ".*@(\\d+)x.[^.]+$" ); - private static final long serialVersionUID = 1L; - - 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; + public Icon add() { + return icon( "media/icon_add.png" ); } - //private static URL retinaURL(final URL url) { - // try { - // 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); + public Icon delete() { + return icon( "media/icon_delete.png" ); } - @Override - public int getIconHeight() { - return (int) (super.getIconHeight() / scale); + public Icon question() { + return icon( "media/icon_question.png" ); } - @Override - public synchronized void paintIcon(final Component c, final Graphics g, final int x, final int y) { - ImageObserver observer = ifNotNullElse( getImageObserver(), c ); + public Icon user() { + return icon( "media/icon_user.png" ); + } - Image image = getImage(); - int width = image.getWidth( observer ); - int height = image.getHeight( observer ); - Graphics2D g2d = (Graphics2D) g.create( x, y, width, height ); + public Icon avatar(final int index) { + return icon( strf( "media/avatar-%d.png", index % avatars() ) ); + } - g2d.scale( 1 / scale, 1 / scale ); - g2d.drawImage( image, 0, 0, observer ); - g2d.scale( 1, 1 ); - g2d.dispose(); + public int avatars() { + return AVATAR_COUNT; + } + + 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) { 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 frameBg = Color.decode( "#5A5D6B" ); diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/FilesPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/FilesPanel.java index f683ac28..eadbc75c 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/FilesPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/FilesPanel.java @@ -1,16 +1,13 @@ 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 static com.lyndir.masterpassword.util.Utilities.*; + +import com.lyndir.masterpassword.gui.util.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.Collection; import java.util.concurrent.CopyOnWriteArraySet; import javax.annotation.Nullable; @@ -21,15 +18,16 @@ import javax.swing.*; * @author lhunath, 2018-07-14 */ @SuppressWarnings("serial") -public class FilesPanel extends JPanel implements ItemListener { +public class FilesPanel extends JPanel { private final Collection listeners = new CopyOnWriteArraySet<>(); - private final JLabel avatarLabel = new JLabel(); - private final CollectionListModel> usersModel = new CollectionListModel<>(); - private final JComboBox> userField = - Components.comboBox( usersModel, user -> (user != null)? user.getFullName(): null ); - private final JButton preferencesButton = Components.button( "..." ); + private final JButton avatarButton = Components.button( Res.icons().avatar( 0 ), event -> setAvatar() ); + + private final CollectionListModel> usersModel = + CollectionListModel.>copy( MPFileUserManager.get().getFiles() ).selection( this::setUser ); + private final JComboBox> userField = + Components.comboBox( usersModel, user -> ifNotNull( user, MPUser::getFullName ) ); protected FilesPanel() { setOpaque( false ); @@ -40,66 +38,45 @@ public class FilesPanel extends JPanel implements ItemListener { add( Box.createVerticalGlue() ); // Avatar - add( avatarLabel ); - avatarLabel.setHorizontalAlignment( SwingConstants.CENTER ); - avatarLabel.setMaximumSize( new Dimension( Integer.MAX_VALUE, 0 ) ); - avatarLabel.setToolTipText( "The avatar for your user. Click to change it." ); + add( avatarButton ); + avatarButton.setHorizontalAlignment( SwingConstants.CENTER ); + avatarButton.setMaximumSize( new Dimension( Integer.MAX_VALUE, 0 ) ); + avatarButton.setToolTipText( "The avatar for your user. Click to change it." ); // - add( Components.strut( Components.margin() ) ); // User Selection - 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 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 ); + add( userField ); } public void reload() { - MPFileUserManager.get().reload(); - usersModel.set( MPFileUserManager.get().getFiles() ); + // TODO: Should we use a listener here instead? + usersModel.set( MPFileUserManager.get().reload() ); } public boolean addListener(final Listener listener) { return listeners.add( listener ); } - @Override - public void itemStateChanged(final ItemEvent e) { - if (e.getStateChange() != ItemEvent.SELECTED) + private void setAvatar() { + MPUser selectedUser = usersModel.getSelectedItem(); + if (selectedUser == null) return; - MPUser selectedUser = usersModel.getSelectedItem(); - avatarLabel.setIcon( Res.avatar( (selectedUser == null)? 0: selectedUser.getAvatar() ) ); + selectedUser.setAvatar( (selectedUser.getAvatar() + 1) % Res.icons().avatars() ); + 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) - listener.onUserSelected( selectedUser ); + listener.onUserSelected( user ); } public interface Listener { - void onUserSelected(@Nullable MPUser selectedUser); + void onUserSelected(@Nullable MPUser user); } } diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java index 1b1bef97..96c0220d 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/MasterPasswordFrame.java @@ -1,7 +1,7 @@ package com.lyndir.masterpassword.gui.view; 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.model.MPUser; import java.awt.*; @@ -50,8 +50,8 @@ public class MasterPasswordFrame extends JFrame implements FilesPanel.Listener, } @Override - public void onUserSelected(@Nullable final MPUser selectedUser) { - userPanel.setUser( selectedUser ); + public void onUserSelected(@Nullable final MPUser user) { + userPanel.setUser( user ); } @Override diff --git a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserPanel.java b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserPanel.java index 064cbc0f..6a331d33 100644 --- a/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserPanel.java +++ b/platform-independent/java/gui/src/main/java/com/lyndir/masterpassword/gui/view/UserPanel.java @@ -3,10 +3,11 @@ 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.collect.ImmutableList; 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.Res; import com.lyndir.masterpassword.gui.util.CollectionListModel; import com.lyndir.masterpassword.gui.util.Components; import com.lyndir.masterpassword.model.*; @@ -30,6 +31,7 @@ import javax.swing.event.*; /** * @author lhunath, 2018-07-14 */ +@SuppressWarnings("SerializableStoresNonSerializable") public class UserPanel extends Components.GradientPanel implements MPUser.Listener { 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; - add( Components.heading( user.getFullName(), SwingConstants.CENTER ) ); - add( Components.strut() ); + add( new Components.GradientPanel( new BorderLayout() ) { + { + add( Components.heading( user.getFullName(), SwingConstants.CENTER ), BorderLayout.CENTER ); + add( Components.button( Res.icons().user(), event -> showPreferences() ), BorderLayout.LINE_END ); + } + } ); add( passwordLabel ); add( passwordField ); @@ -237,6 +243,25 @@ public class UserPanel extends Components.GradientPanel implements MPUser.Listen add( Box.createGlue() ); } + public void showPreferences() { + ImmutableList.Builder 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 public void actionPerformed(final ActionEvent event) { MPSite site = sitesList.getSelectedValue(); diff --git a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java index fe7956c8..c84abba2 100644 --- a/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java +++ b/platform-independent/java/model/src/main/java/com/lyndir/masterpassword/model/impl/MPFileUserManager.java @@ -67,13 +67,13 @@ public class MPFileUserManager { this.path = path; } - public void reload() { + public ImmutableSortedSet reload() { userByName.clear(); File[] pathFiles; if ((!path.exists() && !path.mkdirs()) || ((pathFiles = path.listFiles()) == null)) { logger.err( "Couldn't create directory for user files: %s", path ); - return; + return getFiles(); } for (final File file : pathFiles) @@ -89,6 +89,8 @@ public class MPFileUserManager { catch (final IOException | MPMarshalException e) { logger.err( e, "Couldn't read user from: %s", file ); } + + return getFiles(); } public MPFileUser add(final String fullName) {