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
--- /dev/null
+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 <code>WrapLayout</code> with a left alignment and a
+ * default 5-unit horizontal and vertical gap.
+ */
+ public WrapLayout() {
+ super();
+ }
+
+ /**
+ * Constructs a new <code>FlowLayout</code> with the specified alignment and
+ * a default 5-unit horizontal and vertical gap. The value of the alignment
+ * argument must be one of <code>WrapLayout</code>, <code>WrapLayout</code>,
+ * or <code>WrapLayout</code>.
+ *
+ * @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.
+ * <p>
+ * The value of the alignment argument must be one of
+ * <code>WrapLayout</code>, <code>WrapLayout</code>, or
+ * <code>WrapLayout</code>.
+ *
+ * @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 <i>visible</i>
+ * 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 <i>visible</i>
+ * 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
--- /dev/null
+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).
+ * <p>
+ * <b>Must</b> 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) {
+ }
+ }
+}
--- /dev/null
+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 <code>WrapLayout</code> with a left alignment and a
+ * default 5-unit horizontal and vertical gap.
+ */
+ public WrapLayout() {
+ super();
+ }
+
+ /**
+ * Constructs a new <code>FlowLayout</code> with the specified alignment and
+ * a default 5-unit horizontal and vertical gap. The value of the alignment
+ * argument must be one of <code>WrapLayout</code>, <code>WrapLayout</code>,
+ * or <code>WrapLayout</code>.
+ *
+ * @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.
+ * <p>
+ * The value of the alignment argument must be one of
+ * <code>WrapLayout</code>, <code>WrapLayout</code>, or
+ * <code>WrapLayout</code>.
+ *
+ * @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 <i>visible</i>
+ * 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 <i>visible</i>
+ * 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