diff --git a/versioning.util/src/org/netbeans/modules/versioning/history/SummaryCellRenderer.java b/versioning.util/src/org/netbeans/modules/versioning/history/SummaryCellRenderer.java --- a/versioning.util/src/org/netbeans/modules/versioning/history/SummaryCellRenderer.java +++ b/versioning.util/src/org/netbeans/modules/versioning/history/SummaryCellRenderer.java @@ -85,14 +85,17 @@ import org.netbeans.api.editor.mimelookup.MimeLookup; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.editor.settings.FontColorSettings; +import org.netbeans.modules.versioning.history.AbstractSummaryView.LogEntry; import org.netbeans.modules.versioning.history.AbstractSummaryView.LogEntry.Event; import org.netbeans.modules.versioning.history.AbstractSummaryView.RevisionItem; import org.netbeans.modules.versioning.history.AbstractSummaryView.SummaryViewMaster.SearchHighlight; import org.netbeans.modules.versioning.util.Utils; import org.netbeans.modules.versioning.util.VCSHyperlinkProvider; import org.netbeans.modules.versioning.util.VCSHyperlinkSupport; +import org.netbeans.modules.versioning.util.VCSHyperlinkSupport.IssueLinker; import org.netbeans.modules.versioning.util.VCSKenaiAccessor; import org.openide.ErrorManager; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -116,9 +119,9 @@ private MoreRevisionsRenderer mr = new MoreRevisionsRenderer(); private DefaultListCellRenderer dlcr = new DefaultListCellRenderer(); private ListCellRenderer remainingFilesRenderer = new RemainingFilesRenderer(); - + private AttributeSet searchHiliteAttrs; - + private static final Icon ICON_COLLAPSED = UIManager.getIcon("Tree.collapsedIcon"); //NOI18N private static final Icon ICON_EXPANDED = UIManager.getIcon("Tree.expandedIcon"); //NOI18N private static final int INDENT = ICON_EXPANDED.getIconWidth() + 3; @@ -233,7 +236,7 @@ } return maxWidth; } - + public Collection getHyperlinkProviders() { if (hpInstances == null) { Lookup.Result hpResult = Lookup.getDefault().lookupResult(VCSHyperlinkProvider.class); @@ -250,19 +253,92 @@ return kenaiUser; } + /** + * Composite panel for displaying revision, author, commit message and other info. + */ + public class RevisionItemCell extends JPanel{ + + private JTextPane authorControl; + + private JTextPane dateControl; + private JTextPane revisionControl; + private JTextPane commitMessageControl; + private JPanel northPanel; + private JPanel authorDatePanel; + + public RevisionItemCell() { + + northPanel = new javax.swing.JPanel(); + authorDatePanel = new javax.swing.JPanel(); + dateControl = new JTextPane(); + authorControl = new JTextPane(); + revisionControl = new JTextPane(); + commitMessageControl = new JTextPane(); + + this.setBorder(null); + this.setLayout(new BorderLayout(0, 0)); + this.add(commitMessageControl, java.awt.BorderLayout.CENTER); + this.add(northPanel, java.awt.BorderLayout.NORTH); + dateControl.setBorder(null); + dateControl.setLayout(null); + authorControl.setBorder(null); + authorControl.setLayout(null); + revisionControl.setBorder(null); + revisionControl.setLayout(null); + commitMessageControl.setBorder(null); + commitMessageControl.setLayout(null); + + + northPanel.setBorder(BorderFactory.createEmptyBorder()); + northPanel.setLayout(new BorderLayout(5, 0)); + northPanel.add(authorDatePanel, java.awt.BorderLayout.EAST); + northPanel.add(revisionControl, java.awt.BorderLayout.CENTER); + + authorDatePanel.setLayout(new BorderLayout(5, 0)); + authorDatePanel.setBorder(BorderFactory.createEmptyBorder()); + authorDatePanel.add(authorControl, BorderLayout.CENTER); + authorDatePanel.add(dateControl,BorderLayout.EAST); + + northPanel.setOpaque(false); + revisionControl.setOpaque(false); + dateControl.setOpaque(false); + authorControl.setOpaque(false); + commitMessageControl.setOpaque(false); + authorDatePanel.setOpaque(false); + + this.setFocusable(false); + northPanel.setFocusable(false); + authorDatePanel.setFocusable(false); + + } + + public JTextPane getAuthorControl() { + return authorControl; + } + + public JTextPane getDateControl() { + return dateControl; + } + + public JTextPane getRevisionControl() { + return revisionControl; + } + + public JTextPane getCommitMessageControl() { + return commitMessageControl; + } + + public JPanel getNorthPanel() { + return northPanel; + } + + } + private class RevisionRenderer extends JPanel implements ListCellRenderer { private String id; - private final Style selectedStyle; - private final Style normalStyle; - private final Style indentStyle; - private final Style noindentStyle; - private final Style issueHyperlinkStyle; - private final Style linkStyle; - private final Style authorStyle; - private final Style hiliteStyle; private boolean lastSelection = false; - private final JTextPane textPane; + private final RevisionItemCell revisionCell = new RevisionItemCell(); private final JButton expandButton; private String commitMessage = ""; //NOI18N private boolean lastMessageExpanded; @@ -272,46 +348,16 @@ public RevisionRenderer() { selectionForeground = new JList().getSelectionForeground(); - textPane = new JTextPane(); expandButton = new LinkButton(ICON_COLLAPSED); expandButton.setBorder(BorderFactory.createEmptyBorder()); - selectedStyle = textPane.addStyle("selected", null); //NOI18N - StyleConstants.setForeground(selectedStyle, selectionForeground); - StyleConstants.setBackground(selectedStyle, selectionBackground); - normalStyle = textPane.addStyle("normal", null); //NOI18N - StyleConstants.setForeground(normalStyle, UIManager.getColor("List.foreground")); //NOI18N - indentStyle = textPane.addStyle("indent", null); //NOI18N - StyleConstants.setLeftIndent(indentStyle, 50); - noindentStyle = textPane.addStyle("noindent", null); //NOI18N - StyleConstants.setLeftIndent(noindentStyle, 0); + this.setBorder(BorderFactory.createMatteBorder(3, 0, 0, 0, UIManager.getColor("List.background"))); //NOI18N + this.setLayout(new BorderLayout(3, 0)); - issueHyperlinkStyle = textPane.addStyle("issuehyperlink", normalStyle); //NOI18N - StyleConstants.setForeground(issueHyperlinkStyle, Color.BLUE); - StyleConstants.setUnderline(issueHyperlinkStyle, true); - - linkStyle = textPane.addStyle("link", normalStyle); //NOI18N - StyleConstants.setForeground(linkStyle, Color.BLUE); - StyleConstants.setBold(linkStyle, true); - - authorStyle = textPane.addStyle("author", normalStyle); //NOI18N - StyleConstants.setForeground(authorStyle, Color.BLUE); - - hiliteStyle = textPane.addStyle("hilite", normalStyle); //NOI18N - - Color c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Background); - if (c != null) StyleConstants.setBackground(hiliteStyle, c); - c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Foreground); - if (c != null) StyleConstants.setForeground(hiliteStyle, c); - - textPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); - setLayout(new BorderLayout(0, 0)); - setBorder(BorderFactory.createMatteBorder(3, 0, 0, 0, UIManager.getColor("List.background"))); //NOI18N - - add(expandButton, BorderLayout.WEST); expandButton.setMaximumSize(expandButton.getPreferredSize()); expandButton.setMinimumSize(expandButton.getPreferredSize()); - add(textPane, BorderLayout.CENTER); + this.add(expandButton, BorderLayout.WEST); + this.add(revisionCell, BorderLayout.CENTER); } @Override @@ -319,32 +365,26 @@ AbstractSummaryView.RevisionItem item = (AbstractSummaryView.RevisionItem) value; AbstractSummaryView.LogEntry entry = item.getUserData(); - StyledDocument sd = textPane.getStyledDocument(); Collection highlights = summaryView.getMaster().getSearchHighlights(); - if (sd.getLength() == 0 || selected != lastSelection || item.messageExpanded != lastMessageExpanded || item.revisionExpanded != lastRevisionExpanded - || !highlights.equals(lastHighlights)) { + if (revisionCell.getRevisionControl().getStyledDocument().getLength() == 0 || revisionCell.getDateControl().getStyledDocument().getLength() == 0 ||revisionCell.getAuthorControl().getStyledDocument().getLength() == 0|| revisionCell.getCommitMessageControl().getStyledDocument().getLength() == 0 || selected != lastSelection || item.messageExpanded != lastMessageExpanded || item.revisionExpanded != lastRevisionExpanded + || !highlights.equals(lastHighlights)) + { lastSelection = selected; lastMessageExpanded = item.messageExpanded; lastRevisionExpanded = item.revisionExpanded; lastHighlights = highlights; - Style style; Color backgroundColor; - Color foregroundColor; if (selected) { - foregroundColor = selectionForeground; backgroundColor = selectionBackground; - style = selectedStyle; } else { - foregroundColor = UIManager.getColor("List.foreground"); //NOI18N backgroundColor = UIManager.getColor("List.background"); //NOI18N backgroundColor = entry.isLessInteresting() ? darkerUninteresting(backgroundColor) : darker(backgroundColor); - style = normalStyle; } - textPane.setOpaque(false); - textPane.setBackground(new Color(0, 0, 0, 0)); - setBackground(backgroundColor); + this.setBackground(backgroundColor); + revisionCell.setBackground(backgroundColor); + if (item.revisionExpanded) { expandButton.setIcon(ICON_EXPANDED); } else { @@ -357,35 +397,102 @@ } try { - // clear document - sd.remove(0, sd.getLength()); - sd.setParagraphAttributes(0, sd.getLength(), noindentStyle, false); + addRevision(revisionCell.getRevisionControl(), item, selected, highlights); + addCommitMessage(revisionCell.getCommitMessageControl(), item, selected, highlights); + addAuthor(revisionCell.getAuthorControl(), item, selected, highlights); + addDate(revisionCell.getDateControl(), item, selected, highlights); + } catch (BadLocationException e) { + ErrorManager.getDefault().notify(e); + } + } + lastWidth = resizePane(revisionCell.getCommitMessageControl().getText(), list, lastWidth); - // add revision - sd.insertString(0, item.getUserData().getRevision(), null); - sd.setCharacterAttributes(0, sd.getLength(), normalStyle, false); - if (!selected) { - for (AbstractSummaryView.LogEntry.RevisionHighlight highlight : item.getUserData().getRevisionHighlights()) { - Style s = textPane.addStyle(null, normalStyle); - StyleConstants.setForeground(s, highlight.getForeground()); - StyleConstants.setBackground(s, highlight.getBackground()); - sd.setCharacterAttributes(highlight.getStart(), highlight.getLength(), s, false); - } - for (SearchHighlight highlight : highlights) { - if (highlight.getKind() == SearchHighlight.Kind.REVISION) { - int doclen = sd.getLength(); - String highlightMessage = highlight.getSearchText(); - String revisionText = item.getUserData().getRevision().toLowerCase(); - int idx = revisionText.indexOf(highlightMessage); - if (idx > -1) { - sd.setCharacterAttributes(doclen - revisionText.length() + idx, highlightMessage.length(), hiliteStyle, false); - } + return this; + } + + @SuppressWarnings("empty-statement") + private int resizePane(String text, JList list, int lastWidth) { + if (text == null) { + text = ""; //NOI18N + } + int width = summaryView.getMaster().getComponent().getWidth(); + if (width > 0 && width != lastWidth) { + String[] rows = text.split("\n"); //NOI18N + FontMetrics fm = list.getFontMetrics(list.getFont()); + int lines = 0; + for (String row : rows) { + Rectangle2D rect = fm.getStringBounds(row, revisionCell.getGraphics()); + lines += (int) (rect.getWidth() / (width - 80) + 1); + } + int ph = fm.getHeight() * (lines+1)+4; + revisionCell.setPreferredSize(new Dimension(width - 50 - ICON_COLLAPSED.getIconWidth(), ph)); + setPreferredSize(revisionCell.getPreferredSize()); + } + return width; + } + + + private void addRevision(JTextPane pane, RevisionItem item, boolean selected, Collection highlights) throws BadLocationException { + { + StyledDocument sd = pane.getStyledDocument(); + // clear document + clearSD(pane, sd); + + Style selectedStyle = createSelectedStyle(pane); + Style normalStyle = createNormalStyle(pane); + Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs); + Style style; + if (selected) { + style = selectedStyle; + } else { + style = normalStyle; + } + + + // add revision + sd.insertString(0, item.getUserData().getRevision(), style); + if (!selected) { + for (AbstractSummaryView.LogEntry.RevisionHighlight highlight : item.getUserData().getRevisionHighlights()) { + Style s = pane.addStyle(null, normalStyle); + StyleConstants.setForeground(s, highlight.getForeground()); + StyleConstants.setBackground(s, highlight.getBackground()); + sd.setCharacterAttributes(highlight.getStart(), highlight.getLength(), s, false); + } + for (SearchHighlight highlight : highlights) { + if (highlight.getKind() == SearchHighlight.Kind.REVISION) { + int doclen = sd.getLength(); + String highlightMessage = highlight.getSearchText(); + String revisionText = item.getUserData().getRevision().toLowerCase(); + int idx = revisionText.indexOf(highlightMessage); + if (idx > -1) { + sd.setCharacterAttributes(doclen - revisionText.length() + idx, highlightMessage.length(), hiliteStyle, false); } } } + } + } + } - // add author - sd.insertString(sd.getLength(), FIELDS_SEPARATOR, style); + private void addAuthor(JTextPane pane, RevisionItem item, boolean selected, Collection highlights) throws BadLocationException { + { + LogEntry entry=item.getUserData(); + StyledDocument sd = pane.getStyledDocument(); + // clear document + clearSD(pane, sd); + + + Style selectedStyle = createSelectedStyle(pane); + Style normalStyle = createNormalStyle(pane); + Style style; + if (selected) { + style = selectedStyle; + } else { + style = normalStyle; + } + + Style authorStyle = createAuthorStyle(pane, normalStyle); + Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs); + // add author String author = entry.getAuthor(); VCSHyperlinkSupport.StyledDocumentHyperlink l = linkerSupport.getLinker(VCSHyperlinkSupport.AuthorLinker.class, id); if(l == null) { @@ -394,7 +501,6 @@ l = new VCSHyperlinkSupport.AuthorLinker(kenaiUser, authorStyle, sd, author); linkerSupport.add(l, id); } - } int pos = sd.getLength(); if(l != null) { l.insertString(sd, selected ? style : null); @@ -414,23 +520,68 @@ } } } + } + } + } - // add date - sd.insertString(sd.getLength(), FIELDS_SEPARATOR + entry.getDate(), null); + private void addDate(JTextPane pane, RevisionItem item, boolean selected, Collection highlights) throws BadLocationException { + + LogEntry entry = item.getUserData(); + StyledDocument sd = pane.getStyledDocument(); + // clear document + clearSD(pane, sd); - // add commit msg - boolean messageChanged = !entry.getMessage().equals(commitMessage); - commitMessage = entry.getMessage(); - if (commitMessage.endsWith("\n")) commitMessage = commitMessage.substring(0, commitMessage.length() - 1); //NOI18N - sd.insertString(sd.getLength(), "\n", null); //NOI18N - int nlc, i; - for (i = 0, nlc = -1; i != -1 ; i = commitMessage.indexOf('\n', i + 1), nlc++); - if (nlc > 0 && !item.messageExpanded) { - commitMessage = commitMessage.substring(0, commitMessage.indexOf("\n")); //NOI18N - } + Style selectedStyle = createSelectedStyle(pane); + Style normalStyle = createNormalStyle(pane); + Style style; + if (selected) { + style = selectedStyle; + } else { + style = normalStyle; + } + // add date + sd.insertString(sd.getLength(), entry.getDate(), style); + } + + private void addCommitMessage(JTextPane pane, RevisionItem item, boolean selected, Collection highlights) throws BadLocationException { + { + LogEntry entry=item.getUserData(); + StyledDocument sd=pane.getStyledDocument(); + // clear document + clearSD(pane, sd); + + Style selectedStyle = createSelectedStyle(pane); + Style normalStyle = createNormalStyle(pane); + Style linkStyle = createLinkStyle(pane, normalStyle); + Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs); + Style issueHyperlinkStyle = createIssueHyperlinkStyle(pane, normalStyle); + + Style style; + if (selected) { + style = selectedStyle; + } else { + style = normalStyle; + } + + + // add commit msg + boolean messageChanged = !entry.getMessage().equals(commitMessage); + commitMessage = entry.getMessage(); + if (commitMessage.endsWith("\n")) { + commitMessage = commitMessage.substring(0, commitMessage.length() - 1); //NOI18N + } + + int nlc, i; + for (i = 0, nlc = -1; i != -1; i = commitMessage.indexOf('\n', i + 1), nlc++); + if (nlc > 0 && !item.messageExpanded) { + commitMessage = commitMessage.substring(0, commitMessage.indexOf("\n")); //NOI18N + } + + int pos = 0; + { // compute issue hyperlinks - l = linkerSupport.getLinker(VCSHyperlinkSupport.IssueLinker.class, id); + IssueLinker l = linkerSupport.getLinker(VCSHyperlinkSupport.IssueLinker.class, id); if (messageChanged) { lastWidth = -1; if (l != null) { @@ -448,12 +599,13 @@ } } } - pos = sd.getLength(); + if(l != null) { l.insertString(sd, style); } else { sd.insertString(sd.getLength(), commitMessage, style); } + } // tooltip for message MessageTooltip mtt = linkerSupport.getLinker(MessageTooltip.class, id); @@ -470,21 +622,22 @@ if (lineEnd == -1) { lineEnd = sd.getLength() - pos; } - Style s = textPane.addStyle(null, style); + Style s = pane.addStyle(null, style); StyleConstants.setBold(s, true); sd.setCharacterAttributes(pos, lineEnd, s, false); int msglen = commitMessage.length(); int doclen = sd.getLength(); + { if (nlc > 0 && !item.messageExpanded) { - l = linkerSupport.getLinker(ExpandMsgHyperlink.class, id); + ExpandMsgHyperlink l = linkerSupport.getLinker(ExpandMsgHyperlink.class, id); if (l == null) { l = new ExpandMsgHyperlink(item, sd.getLength(), id); linkerSupport.add(l, id); } l.insertString(sd, linkStyle); } - + } if (!selected) { for (SearchHighlight highlight : highlights) { @@ -505,45 +658,83 @@ if (selected) { sd.setCharacterAttributes(0, Integer.MAX_VALUE, style, false); } - } catch (BadLocationException e) { - ErrorManager.getDefault().notify(e); - } } - lastWidth = resizePane(textPane.getText(), list, lastWidth); + } - return this; + private Style createNormalStyle(JTextPane textPane) { + Style normalStyle = textPane.addStyle("normal", null); //NOI18N + StyleConstants.setForeground(normalStyle, UIManager.getColor("List.foreground")); //NOI18N + return normalStyle; + } + private Style createIssueHyperlinkStyle(JTextPane textPane, Style normalStyle) { + Style issueHyperlinkStyle = textPane.addStyle("issuehyperlink", normalStyle); //NOI18N + StyleConstants.setForeground(issueHyperlinkStyle, Color.BLUE); + StyleConstants.setUnderline(issueHyperlinkStyle, true); + return issueHyperlinkStyle; + } + + private Style createAuthorStyle(JTextPane textPane, Style normalStyle) { + Style authorStyle = textPane.addStyle("author", normalStyle); //NOI18N + StyleConstants.setForeground(authorStyle, Color.BLUE); + return authorStyle; } - @SuppressWarnings("empty-statement") - private int resizePane(String text, JList list, int lastWidth) { - if(text == null) { - text = ""; //NOI18N + private Style createLinkStyle(JTextPane textPane, Style normalStyle) { + Style linkStyle = textPane.addStyle("link", normalStyle); //NOI18N + StyleConstants.setForeground(linkStyle, Color.BLUE); + StyleConstants.setBold(linkStyle, true); + return linkStyle; + } + + private Style createNoindentStyle(JTextPane textPane) { + Style noindentStyle = textPane.addStyle("noindent", null); //NOI18N + StyleConstants.setLeftIndent(noindentStyle, 0); + return noindentStyle; + } + + private Style createSelectedStyle(JTextPane textPane) { + Style selectedStyle = textPane.addStyle("selected", null); //NOI18N + StyleConstants.setForeground(selectedStyle, selectionForeground); + StyleConstants.setBackground(selectedStyle, selectionBackground); + return selectedStyle; + } + + private Style createHiliteStyleStyle(JTextPane textPane, Style normalStyle, AttributeSet searchHiliteAttrs) { + Style hiliteStyle = textPane.addStyle("hilite", normalStyle); //NOI18N + + Color c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Background); + if (c != null) { + StyleConstants.setBackground(hiliteStyle, c); } - int width = summaryView.getMaster().getComponent().getWidth(); - if (width > 0 && width != lastWidth) { - String[] rows = text.split("\n"); //NOI18N - FontMetrics fm = list.getFontMetrics(list.getFont()); - int lines = 0; - for (String row : rows) { - Rectangle2D rect = fm.getStringBounds(row, textPane.getGraphics()); - lines += (int) (rect.getWidth() / (width - 80) + 1); - } - int ph = fm.getHeight() * lines + 9; - textPane.setPreferredSize(new Dimension(width - 50 - ICON_COLLAPSED.getIconWidth(), ph)); - setPreferredSize(textPane.getPreferredSize()); + c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Foreground); + if (c != null) { + StyleConstants.setForeground(hiliteStyle, c); } - return width; + + return hiliteStyle; } @Override public void paint(Graphics g) { super.paint(g); - linkerSupport.computeBounds(textPane, id); + linkerSupport.computeBounds(revisionCell.getCommitMessageControl(), id); + ExpandLink link = linkerSupport.getLinker(ExpandLink.class, id); if (link != null) { link.computeBounds(expandButton); } } + + private void clearSD(JTextPane pane, StyledDocument sd) { + try { + Style noindentStyle = createNoindentStyle(pane); + + sd.remove(0, sd.getLength()); + sd.setParagraphAttributes(0, sd.getLength(), noindentStyle, false); + } catch (BadLocationException ex) { + Exceptions.printStackTrace(ex); + } + } } private static class MessageTooltip extends VCSHyperlinkSupport.Hyperlink { @@ -1049,6 +1240,11 @@ Rectangle endr = mtv.getBounds(); bounds = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height); + // NOTE the textPane is positioned within a parent panel so the relative bound has to be modified too + // FIXME this is an ugly hack because the public API of org.netbeans.modules.versioning.util.VCSHyperlinkSupport should stay stable + // SOLUTION: add parameter 'rootPane' to the computeBounds method and translate from bottom to top of the component hiearchy + bounds.translate(textPane.getX(), textPane.getY()); + bounds.translate(textPane.getParent().getX(), textPane.getParent().getY()); } catch (BadLocationException ex) { throw new RuntimeException(ex); } diff --git a/versioning.util/src/org/netbeans/modules/versioning/util/VCSHyperlinkSupport.java b/versioning.util/src/org/netbeans/modules/versioning/util/VCSHyperlinkSupport.java --- a/versioning.util/src/org/netbeans/modules/versioning/util/VCSHyperlinkSupport.java +++ b/versioning.util/src/org/netbeans/modules/versioning/util/VCSHyperlinkSupport.java @@ -240,6 +240,8 @@ Rectangle startr = tui.modelToView(textPane, docstart[i], Position.Bias.Forward).getBounds(); Rectangle endr = tui.modelToView(textPane, docend[i], Position.Bias.Backward).getBounds(); this.bounds[i] = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height); + //NOTE the textPane is positioned within a parent panel so the origin has to be modified too + this.bounds[i].translate(textPane.getX(), textPane.getY()); } catch (BadLocationException ex) { } } }