From 0e6eace75d39ef300891b529d471617b5da8cfaa Mon Sep 17 00:00:00 2001
From: John Crygier <john.crygier@aon.com>
Date: Mon, 07 May 2012 10:58:56 -0400
Subject: [PATCH] Add custom fields to the manager

---
 src/com/gitblit/client/VerticalFlowLayout.java   |  496 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/com/gitblit/client/RepositoriesPanel.java    |    2 
 src/com/gitblit/client/EditRepositoryDialog.java |   45 ++++
 3 files changed, 541 insertions(+), 2 deletions(-)

diff --git a/src/com/gitblit/client/EditRepositoryDialog.java b/src/com/gitblit/client/EditRepositoryDialog.java
index 156de15..6ad75c6 100644
--- a/src/com/gitblit/client/EditRepositoryDialog.java
+++ b/src/com/gitblit/client/EditRepositoryDialog.java
@@ -28,6 +28,7 @@
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -44,10 +45,12 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JRootPane;
+import javax.swing.JScrollPane;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
 import javax.swing.KeyStroke;
 import javax.swing.ListCellRenderer;
+import javax.swing.ScrollPaneConstants;
 
 import com.gitblit.Constants.AccessRestrictionType;
 import com.gitblit.Constants.FederationStrategy;
@@ -117,6 +120,8 @@
 	private JLabel postReceiveInherited;
 
 	private Set<String> repositoryNames;
+	
+	private JPanel customFieldsPanel;
 
 	public EditRepositoryDialog(int protocolVersion) {
 		this(protocolVersion, new RepositoryModel());
@@ -277,6 +282,11 @@
 		JPanel postReceivePanel = new JPanel(new BorderLayout(5, 5));
 		postReceivePanel.add(postReceivePalette, BorderLayout.CENTER);
 		postReceivePanel.add(postReceiveInherited, BorderLayout.WEST);
+		
+		customFieldsPanel = new JPanel(new VerticalFlowLayout());
+		JScrollPane customFieldsScrollPane = new JScrollPane(customFieldsPanel);
+		customFieldsScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+		customFieldsScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
 
 		JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
 		panel.addTab(Translation.get("gb.general"), fieldsPanel);
@@ -290,6 +300,9 @@
 		}
 		panel.addTab(Translation.get("gb.preReceiveScripts"), preReceivePanel);
 		panel.addTab(Translation.get("gb.postReceiveScripts"), postReceivePanel);
+		
+		panel.addTab(Translation.get("gb.customFields"), customFieldsScrollPane);
+		
 
 		JButton createButton = new JButton(Translation.get("gb.save"));
 		createButton.addActionListener(new ActionListener() {
@@ -331,11 +344,15 @@
 		pack();
 		nameField.requestFocus();
 	}
-
+	
 	private JPanel newFieldPanel(String label, JComponent comp) {
+		return newFieldPanel(label, 150, comp);
+	}
+
+	private JPanel newFieldPanel(String label, int labelSize, JComponent comp) {
 		JLabel fieldLabel = new JLabel(label);
 		fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD));
-		fieldLabel.setPreferredSize(new Dimension(150, 20));
+		fieldLabel.setPreferredSize(new Dimension(labelSize, 20));
 		JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
 		panel.add(fieldLabel);
 		panel.add(comp);
@@ -448,6 +465,15 @@
 		repository.indexedBranches = indexedBranchesPalette.getSelections();
 		repository.preReceiveScripts = preReceivePalette.getSelections();
 		repository.postReceiveScripts = postReceivePalette.getSelections();
+		
+		// Custom Fields
+		repository.customFields = new HashMap<String, String>();
+		
+		for (Component aCustomFieldPanel : customFieldsPanel.getComponents()) {
+			JTextField textField = (JTextField) ((JPanel)aCustomFieldPanel).getComponent(1);
+			repository.customFields.put(textField.getName(), textField.getText());
+		}
+		
 		return true;
 	}
 
@@ -525,6 +551,21 @@
 	public List<String> getPermittedTeams() {
 		return teamsPalette.getSelections();
 	}
+	
+	public void setCustomFields(RepositoryModel repository, List<String> customFields) {
+		customFieldsPanel.removeAll();
+		
+		for (String customFieldDef : customFields) {
+			String[] customFieldProperty = customFieldDef.split("=");
+			String fieldName = customFieldProperty[0];
+			String fieldLabel = customFieldProperty[1];
+			
+			JTextField textField = new JTextField(repository.customFields.get(fieldName), 50);
+			textField.setName(fieldName);
+			
+			customFieldsPanel.add(newFieldPanel(fieldLabel, 250, textField));
+		}
+	}
 
 	/**
 	 * ListCellRenderer to display descriptive text about the access
diff --git a/src/com/gitblit/client/RepositoriesPanel.java b/src/com/gitblit/client/RepositoriesPanel.java
index 685a70a..89ec605 100644
--- a/src/com/gitblit/client/RepositoriesPanel.java
+++ b/src/com/gitblit/client/RepositoriesPanel.java
@@ -47,6 +47,7 @@
 
 import com.gitblit.Constants;
 import com.gitblit.Constants.RpcRequest;
+import com.gitblit.Keys;
 import com.gitblit.models.FeedModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.utils.StringUtils;
@@ -430,6 +431,7 @@
 				gitblit.getPreReceiveScriptsInherited(repository), repository.preReceiveScripts);
 		dialog.setPostReceiveScripts(gitblit.getPostReceiveScriptsUnused(repository),
 				gitblit.getPostReceiveScriptsInherited(repository), repository.postReceiveScripts);
+		dialog.setCustomFields(repository, gitblit.getSettings().get(Keys.repository.customFields).getStrings());
 		dialog.setVisible(true);
 		final RepositoryModel revisedRepository = dialog.getRepository();
 		final List<String> permittedUsers = dialog.getPermittedUsers();
diff --git a/src/com/gitblit/client/VerticalFlowLayout.java b/src/com/gitblit/client/VerticalFlowLayout.java
new file mode 100644
index 0000000..d799cb3
--- /dev/null
+++ b/src/com/gitblit/client/VerticalFlowLayout.java
@@ -0,0 +1,496 @@
+package com.gitblit.client;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ * A flow layout arranges components in a directional flow, much
+ * like lines of text in a paragraph. The flow direction is
+ * determined by the container's <code>componentOrientation</code>
+ * property and may be one of two values:
+ * <ul>
+ * <li><code>ComponentOrientation.TOP_TO_BOTTOM</code>
+ * <li><code>ComponentOrientation.BOTTOM_TO_TOP</code>
+ * </ul>
+ * Flow layouts are typically used
+ * to arrange buttons in a panel. It arranges buttons
+ * horizontally until no more buttons fit on the same line.
+ * The line alignment is determined by the <code>align</code>
+ * property. The possible values are:
+ * <ul>
+ * <li>{@link #TOP TOP}
+ * <li>{@link #BOTTOM BOTTOM}
+ * <li>{@link #CENTER CENTER}
+ * <li>{@link #LEADING LEADING}
+ * <li>{@link #TRAILING TRAILING}
+ * </ul>
+ * <p>
+ */
+public class VerticalFlowLayout implements LayoutManager, java.io.Serializable
+{
+    /**
+     * This value indicates that each row of components
+     * should be left-justified.
+     */
+    public static final int TOP     = 0;
+
+    /**
+     * This value indicates that each row of components
+     * should be centered.
+     */
+    public static final int CENTER    = 1;
+
+    /**
+     * This value indicates that each row of components
+     * should be right-justified.
+     */
+    public static final int BOTTOM     = 2;
+
+    /**
+     * <code>align</code> is the property that determines
+     * how each column distributes empty space.
+     * It can be one of the following three values:
+     * <ul>
+     * <code>TOP</code>
+     * <code>BOTTOM</code>
+     * <code>CENTER</code>
+     * </ul>
+     *
+     * @see #getAlignment
+     * @see #setAlignment
+     */
+    int align;     // This is the one we actually use
+
+    /**
+     * The flow layout manager allows a seperation of
+     * components with gaps.  The horizontal gap will
+     * specify the space between components and between
+     * the components and the borders of the
+     * <code>Container</code>.
+     *
+     * @see #getHgap()
+     * @see #setHgap(int)
+     */
+    int hgap;
+
+    /**
+     * The flow layout manager allows a seperation of
+     * components with gaps.  The vertical gap will
+     * specify the space between rows and between the
+     * the rows and the borders of the <code>Container</code>.
+     *
+     * @see #getHgap()
+     * @see #setHgap(int)
+     */
+    int vgap;
+
+    /**
+     * Constructs a new <code>VerticalFlowLayout</code> with a centered alignment and a
+     * default 5-unit horizontal and vertical gap.
+     */
+    public VerticalFlowLayout()
+    {
+        this(CENTER, 5, 5);
+    }
+
+    /**
+     * Constructs a new <code>VerticalFlowLayout</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>VerticalFlowLayout.TOP</code>, <code>VerticalFlowLayout.BOTTOM</code>,
+     * or <code>VerticalFlowLayout.CENTER</code>
+     * @param align the alignment value
+     */
+    public VerticalFlowLayout(int align)
+    {
+        this(align, 5, 5);
+    }
+
+    /**
+     * 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>VerticalFlowLayout.TOP</code>, <code>VerticalFlowLayout.BOTTOM</code>,
+     * or <code>VerticalFlowLayout.CENTER</code>.
+     * @param     align   the alignment value
+     * @param     hgap  the horizontal gap between components
+     *                   and between the components and the
+     *                   borders of the <code>Container</code>
+     * @param     vgap  the vertical gap between components
+     *                   and between the components and the
+     *                   borders of the <code>Container</code>
+     */
+    public VerticalFlowLayout(int align, int hgap, int vgap)
+    {
+        this.hgap = hgap;
+        this.vgap = vgap;
+        setAlignment(align);
+    }
+
+    /**
+     * Gets the alignment for this layout.
+     * Possible values are <code>VerticalFlowLayout.TOP</code>,
+     * <code>VerticalFlowLayout.BOTTOM</code> or <code>VerticalFlowLayout.CENTER</code>,
+     * @return   the alignment value for this layout
+     * @see     java.awt.VerticalFlowLayout#setAlignment
+     * @since     JDK1.1
+     */
+    public int getAlignment()
+    {
+        return align;
+    }
+
+    /**
+     * Sets the alignment for this layout. Possible values are
+     * <ul>
+     * <li><code>VerticalFlowLayout.TOP</code>
+     * <li><code>VerticalFlowLayout.BOTTOM</code>
+     * <li><code>VerticalFlowLayout.CENTER</code>
+     * </ul>
+     * @param     align one of the alignment values shown above
+     * @see     #getAlignment()
+     * @since     JDK1.1
+     */
+    public void setAlignment(int align)
+    {
+        this.align = align;
+    }
+
+    /**
+     * Gets the horizontal gap between components
+     * and between the components and the borders
+     * of the <code>Container</code>
+     *
+     * @return   the horizontal gap between components
+     *           and between the components and the borders
+     *           of the <code>Container</code>
+     * @see     java.awt.VerticalFlowLayout#setHgap
+     * @since     JDK1.1
+     */
+    public int getHgap() {
+        return hgap;
+    }
+
+    /**
+     * Sets the horizontal gap between components and
+     * between the components and the borders of the
+     * <code>Container</code>.
+     *
+     * @param hgap the horizontal gap between components
+     *           and between the components and the borders
+     *           of the <code>Container</code>
+     * @see     java.awt.VerticalFlowLayout#getHgap
+     * @since     JDK1.1
+     */
+    public void setHgap(int hgap) {
+        this.hgap = hgap;
+    }
+
+    /**
+     * Gets the vertical gap between components and
+     * between the components and the borders of the
+     * <code>Container</code>.
+     *
+     * @return   the vertical gap between components
+     *           and between the components and the borders
+     *           of the <code>Container</code>
+     * @see     java.awt.VerticalFlowLayout#setVgap
+     * @since     JDK1.1
+     */
+    public int getVgap() {
+        return vgap;
+    }
+
+    /**
+     * Sets the vertical gap between components and between
+     * the components and the borders of the <code>Container</code>.
+     *
+     * @param vgap the vertical gap between components
+     *           and between the components and the borders
+     *           of the <code>Container</code>
+     * @see     java.awt.VerticalFlowLayout#getVgap
+     */
+    public void setVgap(int vgap) {
+        this.vgap = vgap;
+    }
+
+    /**
+     * Adds the specified component to the layout.
+     * Not used by this class.
+     * @param name the name of the component
+     * @param comp the component to be added
+     */
+    public void addLayoutComponent(String name, Component comp) {
+    }
+
+    /**
+     * Removes the specified component from the layout.
+     * Not used by this class.
+     * @param comp the component to remove
+     * @see    java.awt.Container#removeAll
+     */
+    public void removeLayoutComponent(Component comp) {
+    }
+
+    /**
+     * Returns the preferred dimensions for this layout given the
+     * <i>visible</i> components in the specified target container.
+     *
+     * @param target the container that needs to be laid out
+     * @return  the preferred dimensions to lay out the
+     *          subcomponents of the specified container
+     * @see Container
+     * @see #minimumLayoutSize
+     * @see    java.awt.Container#getPreferredSize
+     */
+    public Dimension preferredLayoutSize(Container target)
+    {
+    synchronized (target.getTreeLock())
+    {
+        Dimension dim = new Dimension(0, 0);
+        int nmembers = target.getComponentCount();
+        boolean firstVisibleComponent = true;
+
+        for (int i = 0 ; i < nmembers ; i++)
+        {
+            Component m = target.getComponent(i);
+
+            if (m.isVisible())
+            {
+                Dimension d = m.getPreferredSize();
+                dim.width = Math.max(dim.width, d.width);
+
+                if (firstVisibleComponent)
+                {
+                    firstVisibleComponent = false;
+                }
+                else
+                {
+                    dim.height += vgap;
+                }
+
+                dim.height += d.height;
+            }
+        }
+
+        Insets insets = target.getInsets();
+        dim.width += insets.left + insets.right + hgap*2;
+        dim.height += insets.top + insets.bottom + vgap*2;
+        return dim;
+    }
+    }
+
+    /**
+     * Returns the minimum dimensions needed to layout the <i>visible</i>
+     * components contained in the specified target container.
+     * @param target the container that needs to be laid out
+     * @return  the minimum dimensions to lay out the
+     *          subcomponents of the specified container
+     * @see #preferredLayoutSize
+     * @see    java.awt.Container
+     * @see    java.awt.Container#doLayout
+     */
+    public Dimension minimumLayoutSize(Container target)
+    {
+    synchronized (target.getTreeLock())
+    {
+        Dimension dim = new Dimension(0, 0);
+        int nmembers = target.getComponentCount();
+        boolean firstVisibleComponent = true;
+
+        for (int i = 0 ; i < nmembers ; i++)
+        {
+            Component m = target.getComponent(i);
+            if (m.isVisible())
+            {
+                Dimension d = m.getMinimumSize();
+                dim.width = Math.max(dim.width, d.width);
+
+                if (firstVisibleComponent)
+                {
+                    firstVisibleComponent = false;
+                }
+                else
+                {
+                    dim.height += vgap;
+                }
+
+                dim.height += d.height;
+            }
+        }
+
+
+        Insets insets = target.getInsets();
+        dim.width += insets.left + insets.right + hgap*2;
+        dim.height += insets.top + insets.bottom + vgap*2;
+        return dim;
+    }
+    }
+
+    /**
+     * Lays out the container. This method lets each
+     * <i>visible</i> component take
+     * its preferred size by reshaping the components in the
+     * target container in order to satisfy the alignment of
+     * this <code>VerticalFlowLayout</code> object.
+     *
+     * @param target the specified component being laid out
+     * @see Container
+     * @see    java.awt.Container#doLayout
+     */
+    public void layoutContainer(Container target)
+    {
+    synchronized (target.getTreeLock())
+    {
+        Insets insets = target.getInsets();
+        int maxHeight = target.getSize().height - (insets.top + insets.bottom + vgap*2);
+        int nmembers = target.getComponentCount();
+        int x = insets.left + hgap;
+        int y = 0;
+        int columnWidth = 0;
+        int start = 0;
+
+        boolean ttb = target.getComponentOrientation().isLeftToRight();
+
+        for (int i = 0 ; i < nmembers ; i++)
+        {
+            Component m = target.getComponent(i);
+
+            if (m.isVisible())
+            {
+                Dimension d = m.getPreferredSize();
+                m.setSize(d.width, d.height);
+
+                if ((y == 0) || ((y + d.height) <= maxHeight))
+                {
+                    if (y > 0)
+                    {
+                        y += vgap;
+                    }
+
+                    y += d.height;
+                    columnWidth = Math.max(columnWidth, d.width);
+                }
+                else
+                {
+                    moveComponents(target, x, insets.top + vgap, columnWidth, maxHeight - y, start, i, ttb);
+                    y = d.height;
+                    x += hgap + columnWidth;
+                    columnWidth = d.width;
+                    start = i;
+                }
+            }
+        }
+
+        moveComponents(target, x, insets.top + vgap, columnWidth, maxHeight - y, start, nmembers, ttb);
+    }
+    }
+
+    /**
+     * Centers the elements in the specified row, if there is any slack.
+     * @param target the component which needs to be moved
+     * @param x the x coordinate
+     * @param y the y coordinate
+     * @param width the width dimensions
+     * @param height the height dimensions
+     * @param columnStart the beginning of the column
+     * @param columnEnd the the ending of the column
+     */
+    private void moveComponents(
+        Container target, int x, int y, int width, int height, int columnStart, int columnEnd, boolean ttb)
+    {
+        switch (align)
+        {
+            case TOP:
+                y += ttb ? 0 : height;
+                break;
+            case CENTER:
+                y += height / 2;
+                break;
+            case BOTTOM:
+                y += ttb ? height : 0;
+                break;
+        }
+
+        for (int i = columnStart ; i < columnEnd ; i++)
+        {
+            Component m = target.getComponent(i);
+
+            if (m.isVisible())
+            {
+                int cx;
+                cx = x + (width - m.getSize().width) / 2;
+
+                if (ttb)
+                {
+                    m.setLocation(cx, y);
+                }
+                else
+                {
+                    m.setLocation(cx, target.getSize().height - y - m.getSize().height);
+                }
+
+                y += m.getSize().height + vgap;
+            }
+        }
+    }
+
+    /**
+     * Returns a string representation of this <code>VerticalFlowLayout</code>
+     * object and its values.
+     * @return   a string representation of this layout
+     */
+    public String toString()
+    {
+        String str = "";
+
+        switch (align)
+        {
+            case TOP:    str = ",align=top"; break;
+            case CENTER: str = ",align=center"; break;
+            case BOTTOM: str = ",align=bottom"; break;
+        }
+
+        return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";
+    }
+
+
+    public static void main(String[] args)
+    {
+        JPanel main = new JPanel( new BorderLayout() );
+
+        final JPanel buttons = new JPanel(new VerticalFlowLayout() );
+//      buttons.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
+        main.add(buttons, BorderLayout.CENTER);
+
+        for (int i = 0; i < 7; i++)
+        {
+            buttons.add( new JRadioButton("button " + i) );
+        }
+
+        JButton button = new JButton("Add Radio Button");
+        main.add(button, BorderLayout.SOUTH);
+        button.addActionListener( new ActionListener()
+        {
+            private int i = 8;
+
+            public void actionPerformed(ActionEvent e)
+            {
+                buttons.add( new JRadioButton("button R Us" + i++) );
+                buttons.revalidate();
+//              pack();
+            }
+        });
+
+        JFrame frame = new JFrame();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.add(main);
+        frame.setSize(300, 300);
+        frame.setLocationRelativeTo(null);
+        frame.setVisible(true);
+    }
+
+
+}
\ No newline at end of file

--
Gitblit v1.9.1