releases.moxie | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/utils/ColorFactory.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/wicket/WicketUtils.java | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/wicket/pages/BlamePage.html | ●●●●● patch | view | raw | blame | history | |
src/main/java/com/gitblit/wicket/pages/BlamePage.java | ●●●●● patch | view | raw | blame | history | |
src/main/resources/gitblit.css | ●●●●● patch | view | raw | blame | history |
releases.moxie
@@ -39,6 +39,7 @@ - Revised committer verification to require a matching displayname or account name AND the email address - Serve repositories on both /r and /git, displaying /r because it is shorter additions: - Added color modes for the blame page (issue-2) - Added an optional MirrorExecutor which will periodically fetch ref updates from source repositories for mirrors (issue-5). Repositories must be manually cloned using native git and "--mirror". - Added branch graph image servlet based on EGit's branch graph renderer (issue-194) - Added option to render Markdown commit messages (issue-203) @@ -82,6 +83,7 @@ - Guenter Dressel - fpeters.fae - David Ostrovsky - Alex Lewis } # src/main/java/com/gitblit/utils/ColorFactory.java
New file @@ -0,0 +1,135 @@ /* * Copyright 2013 Alex Lewis. * Copyright 2013 gitblit.com. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gitblit.utils; import java.awt.Color; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.Set; /** * Factory for creating color maps. * * @author Alex Lewis */ public class ColorFactory { private static final double MAX_TINT_FACTOR = 1; private static final double MIN_TINT_FACTOR = 0.2; private static final double FIXED_TINT_FACTOR = 0.875; /** * Builds a map of the supplied keys to a random color tinted according to * the key's position in the set. * * Depending on the number of keys in the set a tint is calculated from 1.0 * (I.e. white) to a minimum tint. The keys are sorted such that the * "lowest" value will have a full tint applied to it (1.0) with an equally * decreasing tint applied to each key thereafter. * * @param keys * The keys to create a tinted color for. * @param baseColor * the base color (optional) * @return The map of key to tinted color. */ public <T> Map<T, String> getGraduatedColorMap(Set<T> keys, Color baseColor) { Map<T, String> colorMap = new HashMap<T, String>(); if (baseColor == null) { baseColor = getRandomColor(); } double tintStep = (MAX_TINT_FACTOR - MIN_TINT_FACTOR) / keys.size(); double currentTint = MAX_TINT_FACTOR; for (T key : keys) { Color color = tintColor(baseColor, currentTint); colorMap.put(key, getColorString(color)); currentTint -= tintStep; } return colorMap; } /** * Builds a map of the supplied keys to random colors. * * Each color is selected randomly and tinted with a fixed tint. * * @param keys The keys to create the mapped colors. * @return The map of key to random color. */ public <T> Map<T, String> getRandomColorMap(Set<T> keys) { Map<T, String> colorMap = new HashMap<T, String>(); for (T key : keys) { Color color = tintColor(getRandomColor(), FIXED_TINT_FACTOR); colorMap.put(key, getColorString(color)); } return colorMap; } private Color getRandomColor() { Random random = new Random(); Color randomColor = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)); return randomColor; } private Color tintColor(Color origColor, double tintFactor) { int tintedRed = applyTint(origColor.getRed(), tintFactor); int tintedGreen = applyTint(origColor.getGreen(), tintFactor); int tintedBlue = applyTint(origColor.getBlue(), tintFactor); Color tintedColor = new Color(tintedRed, tintedGreen, tintedBlue); return tintedColor; } /** * Convert the color to an HTML compatible color string in hex format E.g. * #FF0000 * * @param color The color to convert * @return The string version of the color I.e. #RRGGBB */ private String getColorString(Color color) { return "#" + Integer.toHexString(color.getRGB() & 0x00ffffff); } /** * Tint the supplied color with a tint factor (0 to 1 inclusive) to make the * colour more pale I.e. closer to white. * * A Tint of 0 has no effect, a Tint of 1 turns the color white. * * @param color The original color * @param tintFactor The factor - 0 to 1 inclusive * @return The tinted color. */ private int applyTint(int color, double tintFactor) { return (int) (color + ((255 - color) * tintFactor)); } } src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -8,6 +8,7 @@ gb.author = author gb.committer = committer gb.commit = commit gb.age = age gb.tree = tree gb.parent = parent gb.url = URL src/main/java/com/gitblit/wicket/WicketUtils.java
@@ -434,6 +434,16 @@ parameterMap.put("pg", String.valueOf(pageNumber)); return new PageParameters(parameterMap); } public static PageParameters newBlameTypeParameter(String repositoryName, String commitId, String path, String blameType) { Map<String, String> parameterMap = new HashMap<String, String>(); parameterMap.put("r", repositoryName); parameterMap.put("h", commitId); parameterMap.put("f", path); parameterMap.put("blametype", blameType); return new PageParameters(parameterMap); } public static String getProjectName(PageParameters params) { return params.getString("p", ""); src/main/java/com/gitblit/wicket/pages/BlamePage.html
@@ -21,17 +21,19 @@ <div wicket:id="missingBlob">[missing blob]</div> <!-- blame content --> <table class="annotated" style="margin-bottom:5px;"> <table class="annotated" style="margin-bottom:0px;"> <tbody> <tr> <th><wicket:message key="gb.commit">[commit]</wicket:message></th> <th><wicket:message key="gb.line">[line]</wicket:message></th> <th><wicket:message key="gb.content">[content]</wicket:message></th> <td colspan="3" class="rightAlign" style="background-color:#fbfbfb;"> <span class="link" style="padding-right:10px;"> <a wicket:id="blameByCommitLink"><wicket:message key="gb.commit">[blamebycommit]</wicket:message></a> | <a wicket:id="blameByAuthorLink"><wicket:message key="gb.author">[blamebyauthor]</wicket:message></a> | <a wicket:id="blameByAgeLink"><wicket:message key="gb.age">[blamebyage]</wicket:message></a> </span> </td> </tr> <tr wicket:id="annotation"> <td><span class="sha1" wicket:id="commit"></span></td> <td><span class="sha1" wicket:id="line"></span></td> <td><span class="sha1" wicket:id="data"></span></td> <tr wicket:id="blameView"> <td class="lineCommit"><span class="sha1" wicket:id="commit"></span></td> <td class="lineNumber"><span class="sha1" wicket:id="line"></span></td> <td class="lineContent sha1" wicket:id="data"></td> </tr> </tbody> </table> src/main/java/com/gitblit/wicket/pages/BlamePage.java
@@ -15,12 +15,21 @@ */ package com.gitblit.wicket.pages; import java.awt.Color; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.wicket.Component; import org.apache.wicket.PageParameters; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.repeater.Item; @@ -33,6 +42,7 @@ import com.gitblit.Keys; import com.gitblit.models.AnnotatedLine; import com.gitblit.models.PathModel; import com.gitblit.utils.ColorFactory; import com.gitblit.utils.DiffUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; @@ -46,10 +56,42 @@ @CacheControl(LastModified.BOOT) public class BlamePage extends RepositoryPage { /** * The different types of Blame visualizations. */ private enum BlameType { COMMIT, AUTHOR, AGE; private BlameType() { } public static BlameType get(String name) { for (BlameType blameType : BlameType.values()) { if (blameType.name().equalsIgnoreCase(name)) { return blameType; } } throw new IllegalArgumentException("Unknown Blame Type [" + name + "]"); } @Override public String toString() { return name().toLowerCase(); } } public BlamePage(PageParameters params) { super(params); final String blobPath = WicketUtils.getPath(params); final String blameTypeParam = params.getString("blametype", BlameType.COMMIT.toString()); final BlameType activeBlameType = BlameType.get(blameTypeParam); RevCommit commit = getCommit(); @@ -65,6 +107,26 @@ WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath))); add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath))); // "Blame by" links for (BlameType type : BlameType.values()) { String typeString = type.toString(); PageParameters blameTypePageParam = WicketUtils.newBlameTypeParameter(repositoryName, commit.getName(), WicketUtils.getPath(params), typeString); String blameByLinkText = "blameBy" + Character.toUpperCase(typeString.charAt(0)) + typeString.substring(1) + "Link"; BookmarkablePageLink<Void> blameByPageLink = new BookmarkablePageLink<Void>(blameByLinkText, BlamePage.class, blameTypePageParam); if (activeBlameType == type) { blameByPageLink.add(new SimpleAttributeModifier("style", "font-weight:bold;")); } add(blameByPageLink); } add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); @@ -93,23 +155,21 @@ add(new Label("missingBlob").setVisible(false)); List<AnnotatedLine> lines = DiffUtils.blame(getRepository(), blobPath, objectId); final Map<?, String> colorMap = initializeColors(activeBlameType, lines); ListDataProvider<AnnotatedLine> blameDp = new ListDataProvider<AnnotatedLine>(lines); DataView<AnnotatedLine> blameView = new DataView<AnnotatedLine>("annotation", blameDp) { DataView<AnnotatedLine> blameView = new DataView<AnnotatedLine>("blameView", blameDp) { private static final long serialVersionUID = 1L; private int count; private String lastCommitId = ""; private boolean showInitials = true; private String zeroId = ObjectId.zeroId().getName(); @Override public void populateItem(final Item<AnnotatedLine> item) { AnnotatedLine entry = item.getModelObject(); item.add(new Label("line", "" + entry.lineNumber)); item.add(new Label("data", StringUtils.escapeForHtml(entry.data, true)) .setEscapeModelStrings(false)); final AnnotatedLine entry = item.getModelObject(); // commit id and author if (!lastCommitId.equals(entry.commitId)) { lastCommitId = entry.commitId; count++; if (zeroId.equals(entry.commitId)) { // unknown commit item.add(new Label("commit", "<?>")); @@ -122,6 +182,7 @@ WicketUtils.setHtmlTooltip(commitLink, MessageFormat.format("{0}, {1}", entry.author, df.format(entry.when))); item.add(commitLink); WicketUtils.setCssStyle(item, "border-top: 1px solid #ddd;"); showInitials = true; } } else { @@ -134,11 +195,26 @@ item.add(new Label("commit").setVisible(false)); } } if (count % 2 == 0) { WicketUtils.setCssClass(item, "even"); } else { WicketUtils.setCssClass(item, "odd"); // line number item.add(new Label("line", "" + entry.lineNumber)); // line content String color; switch (activeBlameType) { case AGE: color = colorMap.get(entry.when); break; case AUTHOR: color = colorMap.get(entry.author); break; default: color = colorMap.get(entry.commitId); break; } Component data = new Label("data", StringUtils.escapeForHtml(entry.data, true)).setEscapeModelStrings(false); data.add(new SimpleAttributeModifier("style", "background-color: " + color + ";")); item.add(data); } }; add(blameView); @@ -171,4 +247,40 @@ sb.append("</div>"); return sb.toString(); } private Map<?, String> initializeColors(BlameType blameType, List<AnnotatedLine> lines) { ColorFactory colorFactory = new ColorFactory(); Map<?, String> colorMap; if (BlameType.AGE == blameType) { Set<Date> keys = new TreeSet<Date>(new Comparator<Date>() { @Override public int compare(Date o1, Date o2) { // younger code has a brighter, older code lightens to white return o1.compareTo(o2); } }); for (AnnotatedLine line : lines) { keys.add(line.when); } // TODO consider making this a setting colorMap = colorFactory.getGraduatedColorMap(keys, Color.decode("#FFA63A")); } else { Set<String> keys = new HashSet<String>(); for (AnnotatedLine line : lines) { if (blameType == BlameType.AUTHOR) { keys.add(line.author); } else { keys.add(line.commitId); } } colorMap = colorFactory.getRandomColorMap(keys); } return colorMap; } } src/main/resources/gitblit.css
@@ -1318,6 +1318,7 @@ } table.annotated { width: 100%; border:1px solid #ddd; } @@ -1332,6 +1333,24 @@ table.annotated td { padding: 0px; border: 0; } table.annotated td.lineCommit { padding-left: 5px; padding-right: 5px; } table.annotated td.lineNumber { border-right: 1px solid #ddd; border-left: 1px solid #ddd; padding-left: 5px; padding-right: 5px; text-align: right; } table.annotated td.lineContent { padding-left: 5px; font: monospace; } table.activity { @@ -1379,7 +1398,7 @@ white-space: nowrap; } span.sha1, span.sha1 a, span.sha1 a span, .commit_message, span.shortsha1 { span.sha1, span.sha1 a, span.sha1 a span, .commit_message, span.shortsha1, td.sha1 { font-family: consolas, monospace; font-size: 13px; }