From 8caeb8bd29810fa1f6b8fa47b3b5f621cc459938 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Fri, 17 Feb 2017 20:55:28 +0100 Subject: [PATCH] 1.0.0 --- VERSION | 2 +- configure.sh | 2 +- libs/WrapLayout.java | 205 +++++++++++++++++++++++++++ src/be/nikiroo/utils/UIUtils.java | 32 +++++ src/be/nikiroo/utils/WrapLayout.java | 205 +++++++++++++++++++++++++++ 5 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 libs/WrapLayout.java create mode 100644 src/be/nikiroo/utils/UIUtils.java create mode 100644 src/be/nikiroo/utils/WrapLayout.java diff --git a/VERSION b/VERSION index c81aa44..3eefcb9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.7 +1.0.0 diff --git a/configure.sh b/configure.sh index 026db92..12984bb 100755 --- a/configure.sh +++ b/configure.sh @@ -45,7 +45,7 @@ fi; echo "MAIN = be/nikiroo/utils/resources/TransBundle" > Makefile -echo "MORE = be/nikiroo/utils/StringUtils be/nikiroo/utils/IOUtils be/nikiroo/utils/MarkableFileInputStream be/nikiroo/utils/test/TestLauncher" >> Makefile +echo "MORE = be/nikiroo/utils/StringUtils be/nikiroo/utils/IOUtils be/nikiroo/utils/MarkableFileInputStream be/nikiroo/utils/UIUtils be/nikiroo/utils/WrapLayout be/nikiroo/utils/test/TestLauncher" >> Makefile echo "TEST = be/nikiroo/utils/test/Test" >> Makefile echo "TEST_PARAMS = $cols $ok $ko" >> Makefile echo "NAME = nikiroo-utils" >> Makefile diff --git a/libs/WrapLayout.java b/libs/WrapLayout.java new file mode 100644 index 0000000..da03c2f --- /dev/null +++ b/libs/WrapLayout.java @@ -0,0 +1,205 @@ +package be.nikiroo.utils; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** + * FlowLayout subclass that fully supports wrapping of components. + * + * @author https://tips4java.wordpress.com/2008/11/06/wrap-layout/ + */ +public class WrapLayout extends FlowLayout { + private static final long serialVersionUID = 1L; + + /** + * Constructs a new WrapLayout with a left alignment and a + * default 5-unit horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new FlowLayout with the specified alignment and + * a default 5-unit horizontal and vertical gap. The value of the alignment + * argument must be one of WrapLayout, WrapLayout, + * or WrapLayout. + * + * @param align + * the alignment value + */ + public WrapLayout(int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment and the + * indicated horizontal and vertical gaps. + *

+ * The value of the alignment argument must be one of + * WrapLayout, WrapLayout, or + * WrapLayout. + * + * @param align + * the alignment value + * @param hgap + * the horizontal gap between components + * @param vgap + * the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the visible + * components in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the visible + * components contained in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the minimum dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target + * container. + * + * @param target + * target to get layout size for + * @param preferred + * should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the + // container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + Container container = target; + + while (container.getSize().width == 0 + && container.getParent() != null) { + container = container.getParent(); + } + + targetWidth = container.getSize().width; + + if (targetWidth == 0) + targetWidth = Integer.MAX_VALUE; + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m + .getMinimumSize(); + + // Can't add the component to current row. Start a new + // row. + + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the + // first + + if (rowWidth != 0) { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need + // to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do + // this. + + Container scrollPane = SwingUtilities.getAncestorOfClass( + JScrollPane.class, target); + + if (scrollPane != null && target.isValid()) { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row to update + * the preferred size for the container. + * + * @param dim update the width and height when appropriate + * + * @param rowWidth the width of the row to add + * + * @param rowHeight the height of the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } +} \ No newline at end of file diff --git a/src/be/nikiroo/utils/UIUtils.java b/src/be/nikiroo/utils/UIUtils.java new file mode 100644 index 0000000..abdfcad --- /dev/null +++ b/src/be/nikiroo/utils/UIUtils.java @@ -0,0 +1,32 @@ +package be.nikiroo.utils; + +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +/** + * Some Java Swing utilities. + * + * @author niki + */ +public class UIUtils { + /** + * Set a fake "native look & feel" for the application if possible + * (check for the one currently in use, then try GTK). + *

+ * Must be called prior to any GUI work. + */ + static public void setLookAndFeel() { + // native look & feel + try { + String noLF = "javax.swing.plaf.metal.MetalLookAndFeel"; + String lf = UIManager.getSystemLookAndFeelClassName(); + if (lf.equals(noLF)) + lf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel"; + UIManager.setLookAndFeel(lf); + } catch (InstantiationException e) { + } catch (ClassNotFoundException e) { + } catch (UnsupportedLookAndFeelException e) { + } catch (IllegalAccessException e) { + } + } +} diff --git a/src/be/nikiroo/utils/WrapLayout.java b/src/be/nikiroo/utils/WrapLayout.java new file mode 100644 index 0000000..da03c2f --- /dev/null +++ b/src/be/nikiroo/utils/WrapLayout.java @@ -0,0 +1,205 @@ +package be.nikiroo.utils; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** + * FlowLayout subclass that fully supports wrapping of components. + * + * @author https://tips4java.wordpress.com/2008/11/06/wrap-layout/ + */ +public class WrapLayout extends FlowLayout { + private static final long serialVersionUID = 1L; + + /** + * Constructs a new WrapLayout with a left alignment and a + * default 5-unit horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new FlowLayout with the specified alignment and + * a default 5-unit horizontal and vertical gap. The value of the alignment + * argument must be one of WrapLayout, WrapLayout, + * or WrapLayout. + * + * @param align + * the alignment value + */ + public WrapLayout(int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment and the + * indicated horizontal and vertical gaps. + *

+ * The value of the alignment argument must be one of + * WrapLayout, WrapLayout, or + * WrapLayout. + * + * @param align + * the alignment value + * @param hgap + * the horizontal gap between components + * @param vgap + * the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the visible + * components in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the visible + * components contained in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the minimum dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target + * container. + * + * @param target + * target to get layout size for + * @param preferred + * should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the + // container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + Container container = target; + + while (container.getSize().width == 0 + && container.getParent() != null) { + container = container.getParent(); + } + + targetWidth = container.getSize().width; + + if (targetWidth == 0) + targetWidth = Integer.MAX_VALUE; + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m + .getMinimumSize(); + + // Can't add the component to current row. Start a new + // row. + + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the + // first + + if (rowWidth != 0) { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need + // to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do + // this. + + Container scrollPane = SwingUtilities.getAncestorOfClass( + JScrollPane.class, target); + + if (scrollPane != null && target.isValid()) { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row to update + * the preferred size for the container. + * + * @param dim update the width and height when appropriate + * + * @param rowWidth the width of the row to add + * + * @param rowHeight the height of the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } +} \ No newline at end of file -- 2.27.0