This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 218850
Collapse All | Expand All

(-)a/versioning.util/src/org/netbeans/modules/versioning/history/RevisionItemCell.java (+141 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.versioning.history;
43
44
import java.awt.BorderLayout;
45
import java.awt.Color;
46
import java.awt.Container;
47
import java.awt.Rectangle;
48
import javax.swing.BorderFactory;
49
import javax.swing.JPanel;
50
import javax.swing.JTextPane;
51
import org.netbeans.modules.versioning.util.VCSHyperlinkSupport;
52
53
/**
54
 * Composite panel for displaying revision, author, commit message and other
55
 * info.
56
 */
57
public class RevisionItemCell extends JPanel implements VCSHyperlinkSupport.BoundsTranslator {
58
    private JTextPane authorControl=new JTextPane();
59
    private JTextPane dateControl=new JTextPane();
60
    private JTextPane revisionControl=new JTextPane();
61
    private JTextPane commitMessageControl=new JTextPane();
62
    private JPanel northPanel=new JPanel();
63
    private JPanel authorDatePanel=new JPanel();
64
65
    public RevisionItemCell () {
66
        this.setBorder(null);
67
        this.setLayout(new BorderLayout(0, 0));
68
        this.add(commitMessageControl, java.awt.BorderLayout.CENTER);
69
        this.add(northPanel, java.awt.BorderLayout.NORTH);
70
        northPanel.setBorder(BorderFactory.createEmptyBorder());
71
        northPanel.setLayout(new BorderLayout(5, 0));
72
        northPanel.add(authorDatePanel, java.awt.BorderLayout.EAST);
73
        northPanel.add(revisionControl, java.awt.BorderLayout.CENTER);
74
        authorDatePanel.setLayout(new BorderLayout(5, 0));
75
        authorDatePanel.setBorder(BorderFactory.createEmptyBorder());
76
        authorDatePanel.add(authorControl, BorderLayout.CENTER);
77
        authorDatePanel.add(dateControl, BorderLayout.EAST);
78
79
        //
80
        initTextPane(dateControl);
81
        initTextPane(authorControl);
82
        initTextPane(revisionControl);
83
        initTextPane(commitMessageControl);
84
        northPanel.setOpaque(false);
85
        authorDatePanel.setOpaque(false);
86
        this.setFocusable(false);
87
        northPanel.setFocusable(false);
88
        authorDatePanel.setFocusable(false);
89
    }
90
    /**
91
     * Corrects the bounding rectangle of nested textpanes.
92
     * @param startComponent
93
     * @param r 
94
     */
95
    @Override
96
    public void correctTranslation (final Container startComponent, final Rectangle r) {
97
        if (null == startComponent) {
98
            return;
99
        }
100
        if (null == r) {
101
            return;
102
        }
103
        
104
        final RevisionItemCell stopComponent = this;
105
        Container current=startComponent;
106
        while (current != stopComponent) {
107
            r.translate(current.getX(), current.getY());
108
            current = current.getParent();
109
        }
110
        r.translate(current.getX(), current.getY());
111
    }
112
113
    public JTextPane getAuthorControl () {
114
        return authorControl;
115
    }
116
117
    public JTextPane getDateControl () {
118
        return dateControl;
119
    }
120
121
    public JTextPane getRevisionControl () {
122
        return revisionControl;
123
    }
124
125
    public JTextPane getCommitMessageControl () {
126
        return commitMessageControl;
127
    }
128
129
    public JPanel getNorthPanel () {
130
        return northPanel;
131
    }
132
133
    private void initTextPane (JTextPane pane) {
134
        pane.setBorder(null);
135
        pane.setLayout(null);
136
        //fix for nimbus laf
137
        pane.setOpaque(false);
138
        pane.setBackground(new Color(0, 0, 0, 0));
139
    }
140
    
141
}
(-)a/versioning.util/src/org/netbeans/modules/versioning/history/SummaryCellRenderer.java (-214 / +342 lines)
Lines 85-98 Link Here
85
import org.netbeans.api.editor.mimelookup.MimeLookup;
85
import org.netbeans.api.editor.mimelookup.MimeLookup;
86
import org.netbeans.api.editor.mimelookup.MimePath;
86
import org.netbeans.api.editor.mimelookup.MimePath;
87
import org.netbeans.api.editor.settings.FontColorSettings;
87
import org.netbeans.api.editor.settings.FontColorSettings;
88
import org.netbeans.modules.versioning.history.AbstractSummaryView.LogEntry;
88
import org.netbeans.modules.versioning.history.AbstractSummaryView.LogEntry.Event;
89
import org.netbeans.modules.versioning.history.AbstractSummaryView.LogEntry.Event;
89
import org.netbeans.modules.versioning.history.AbstractSummaryView.RevisionItem;
90
import org.netbeans.modules.versioning.history.AbstractSummaryView.RevisionItem;
90
import org.netbeans.modules.versioning.history.AbstractSummaryView.SummaryViewMaster.SearchHighlight;
91
import org.netbeans.modules.versioning.history.AbstractSummaryView.SummaryViewMaster.SearchHighlight;
91
import org.netbeans.modules.versioning.util.Utils;
92
import org.netbeans.modules.versioning.util.Utils;
92
import org.netbeans.modules.versioning.util.VCSHyperlinkProvider;
93
import org.netbeans.modules.versioning.util.VCSHyperlinkProvider;
93
import org.netbeans.modules.versioning.util.VCSHyperlinkSupport;
94
import org.netbeans.modules.versioning.util.VCSHyperlinkSupport;
95
import org.netbeans.modules.versioning.util.VCSHyperlinkSupport.AuthorLinker;
96
import org.netbeans.modules.versioning.util.VCSHyperlinkSupport.IssueLinker;
94
import org.netbeans.modules.versioning.util.VCSKenaiAccessor;
97
import org.netbeans.modules.versioning.util.VCSKenaiAccessor;
95
import org.openide.ErrorManager;
98
import org.openide.ErrorManager;
99
import org.openide.util.Exceptions;
96
import org.openide.util.Lookup;
100
import org.openide.util.Lookup;
97
import org.openide.util.NbBundle;
101
import org.openide.util.NbBundle;
98
102
Lines 253-270 Link Here
253
    private class RevisionRenderer extends JPanel implements ListCellRenderer {
257
    private class RevisionRenderer extends JPanel implements ListCellRenderer {
254
258
255
        private String id;
259
        private String id;
256
        private final Style selectedStyle;
257
        private final Style normalStyle;
258
        private final Style indentStyle;
259
        private final Style noindentStyle;
260
        private final Style issueHyperlinkStyle;
261
        private final Style linkStyle;
262
        private final Style authorStyle;
263
        private final Style hiliteStyle;
264
        private boolean lastSelection = false;
260
        private boolean lastSelection = false;
265
        private final JTextPane textPane;
261
        private final RevisionItemCell revisionCell = new RevisionItemCell();
266
        private final JButton expandButton;
262
        private final JButton expandButton;
267
        private String commitMessage = ""; //NOI18N
268
        private boolean lastMessageExpanded;
263
        private boolean lastMessageExpanded;
269
        private boolean lastRevisionExpanded;
264
        private boolean lastRevisionExpanded;
270
        private int lastWidth;
265
        private int lastWidth;
Lines 272-317 Link Here
272
267
273
        public RevisionRenderer() {
268
        public RevisionRenderer() {
274
            selectionForeground = new JList().getSelectionForeground();
269
            selectionForeground = new JList().getSelectionForeground();
275
            textPane = new JTextPane();
276
            expandButton = new LinkButton(ICON_COLLAPSED);
270
            expandButton = new LinkButton(ICON_COLLAPSED);
277
            expandButton.setBorder(BorderFactory.createEmptyBorder());
271
            expandButton.setBorder(BorderFactory.createEmptyBorder());
278
272
279
            selectedStyle = textPane.addStyle("selected", null); //NOI18N
273
            this.setBorder(BorderFactory.createMatteBorder(3, 0, 0, 0, UIManager.getColor("List.background"))); //NOI18N
280
            StyleConstants.setForeground(selectedStyle, selectionForeground);
274
            this.setLayout(new BorderLayout(3, 0));
281
            StyleConstants.setBackground(selectedStyle, selectionBackground);
282
            normalStyle = textPane.addStyle("normal", null); //NOI18N
283
            StyleConstants.setForeground(normalStyle, UIManager.getColor("List.foreground")); //NOI18N
284
            indentStyle = textPane.addStyle("indent", null); //NOI18N
285
            StyleConstants.setLeftIndent(indentStyle, 50);
286
            noindentStyle = textPane.addStyle("noindent", null); //NOI18N
287
            StyleConstants.setLeftIndent(noindentStyle, 0);
288
275
289
            issueHyperlinkStyle = textPane.addStyle("issuehyperlink", normalStyle); //NOI18N
290
            StyleConstants.setForeground(issueHyperlinkStyle, Color.BLUE);
291
            StyleConstants.setUnderline(issueHyperlinkStyle, true);
292
293
            linkStyle = textPane.addStyle("link", normalStyle); //NOI18N
294
            StyleConstants.setForeground(linkStyle, Color.BLUE);
295
            StyleConstants.setBold(linkStyle, true);
296
297
            authorStyle = textPane.addStyle("author", normalStyle); //NOI18N
298
            StyleConstants.setForeground(authorStyle, Color.BLUE);
299
300
            hiliteStyle = textPane.addStyle("hilite", normalStyle); //NOI18N
301
            
302
            Color c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Background);
303
            if (c != null) StyleConstants.setBackground(hiliteStyle, c);
304
            c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Foreground);
305
            if (c != null) StyleConstants.setForeground(hiliteStyle, c);
306
307
            textPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
308
            setLayout(new BorderLayout(0, 0));
309
            setBorder(BorderFactory.createMatteBorder(3, 0, 0, 0, UIManager.getColor("List.background"))); //NOI18N
310
            
311
            add(expandButton, BorderLayout.WEST);
312
            expandButton.setMaximumSize(expandButton.getPreferredSize());
276
            expandButton.setMaximumSize(expandButton.getPreferredSize());
313
            expandButton.setMinimumSize(expandButton.getPreferredSize());
277
            expandButton.setMinimumSize(expandButton.getPreferredSize());
314
            add(textPane, BorderLayout.CENTER);
278
            this.add(expandButton, BorderLayout.WEST);
279
            this.add(revisionCell, BorderLayout.CENTER);
315
        }
280
        }
316
281
317
        @Override
282
        @Override
Lines 319-350 Link Here
319
            AbstractSummaryView.RevisionItem item = (AbstractSummaryView.RevisionItem) value;
284
            AbstractSummaryView.RevisionItem item = (AbstractSummaryView.RevisionItem) value;
320
            AbstractSummaryView.LogEntry entry = item.getUserData();
285
            AbstractSummaryView.LogEntry entry = item.getUserData();
321
286
322
            StyledDocument sd = textPane.getStyledDocument();
323
            Collection<SearchHighlight> highlights = summaryView.getMaster().getSearchHighlights();
287
            Collection<SearchHighlight> highlights = summaryView.getMaster().getSearchHighlights();
324
            if (sd.getLength() == 0 || selected != lastSelection || item.messageExpanded != lastMessageExpanded || item.revisionExpanded != lastRevisionExpanded
288
            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
325
                    || !highlights.equals(lastHighlights)) {
289
                    || !highlights.equals(lastHighlights)) {
326
                lastSelection = selected;
290
                lastSelection = selected;
327
                lastMessageExpanded = item.messageExpanded;
291
                lastMessageExpanded = item.messageExpanded;
328
                lastRevisionExpanded = item.revisionExpanded;
292
                lastRevisionExpanded = item.revisionExpanded;
329
                lastHighlights = highlights;
293
                lastHighlights = highlights;
330
294
331
                Style style;
332
                Color backgroundColor;
295
                Color backgroundColor;
333
                Color foregroundColor;
334
296
335
                if (selected) {
297
                if (selected) {
336
                    foregroundColor = selectionForeground;
337
                    backgroundColor = selectionBackground;
298
                    backgroundColor = selectionBackground;
338
                    style = selectedStyle;
339
                } else {
299
                } else {
340
                    foregroundColor = UIManager.getColor("List.foreground"); //NOI18N
341
                    backgroundColor = UIManager.getColor("List.background"); //NOI18N
300
                    backgroundColor = UIManager.getColor("List.background"); //NOI18N
342
                    backgroundColor = entry.isLessInteresting() ? darkerUninteresting(backgroundColor) : darker(backgroundColor);
301
                    backgroundColor = entry.isLessInteresting() ? darkerUninteresting(backgroundColor) : darker(backgroundColor);
343
                    style = normalStyle;
344
                }
302
                }
345
                textPane.setOpaque(false);
303
                this.setBackground(backgroundColor);
346
                textPane.setBackground(new Color(0, 0, 0, 0));
304
                revisionCell.setBackground(backgroundColor);
347
                setBackground(backgroundColor);
305
348
                if (item.revisionExpanded) {
306
                if (item.revisionExpanded) {
349
                    expandButton.setIcon(ICON_EXPANDED);
307
                    expandButton.setIcon(ICON_EXPANDED);
350
                } else {
308
                } else {
Lines 357-522 Link Here
357
                }
315
                }
358
316
359
                try {
317
                try {
360
                    // clear document
318
                    addRevision(revisionCell.getRevisionControl(), item, selected, highlights);
361
                    sd.remove(0, sd.getLength());
319
                    addCommitMessage(revisionCell.getCommitMessageControl(), item, selected, highlights);
362
                    sd.setParagraphAttributes(0, sd.getLength(), noindentStyle, false);
320
                    addAuthor(revisionCell.getAuthorControl(), item, selected, highlights);
363
321
                    addDate(revisionCell.getDateControl(), item, selected, highlights);
364
                    // add revision
365
                    sd.insertString(0, item.getUserData().getRevision(), null);
366
                    sd.setCharacterAttributes(0, sd.getLength(), normalStyle, false);
367
                    if (!selected) {
368
                        for (AbstractSummaryView.LogEntry.RevisionHighlight highlight : item.getUserData().getRevisionHighlights()) {
369
                            Style s = textPane.addStyle(null, normalStyle);
370
                            StyleConstants.setForeground(s, highlight.getForeground());
371
                            StyleConstants.setBackground(s, highlight.getBackground());
372
                            sd.setCharacterAttributes(highlight.getStart(), highlight.getLength(), s, false);
373
                        }
374
                        for (SearchHighlight highlight : highlights) {
375
                            if (highlight.getKind() == SearchHighlight.Kind.REVISION) {
376
                                int doclen = sd.getLength();
377
                                String highlightMessage = highlight.getSearchText();
378
                                String revisionText = item.getUserData().getRevision().toLowerCase();
379
                                int idx = revisionText.indexOf(highlightMessage);
380
                                if (idx > -1) {
381
                                    sd.setCharacterAttributes(doclen - revisionText.length() + idx, highlightMessage.length(), hiliteStyle, false);
382
                                }
383
                            }
384
                        }
385
                    }
386
387
                    // add author
388
                    sd.insertString(sd.getLength(), FIELDS_SEPARATOR, style);
389
                    String author = entry.getAuthor();
390
                    VCSHyperlinkSupport.StyledDocumentHyperlink l = linkerSupport.getLinker(VCSHyperlinkSupport.AuthorLinker.class, id);
391
                    if(l == null) {
392
                        VCSKenaiAccessor.KenaiUser kenaiUser = getKenaiUser(author);
393
                        if (kenaiUser != null) {
394
                            l = new VCSHyperlinkSupport.AuthorLinker(kenaiUser, authorStyle, sd, author);
395
                            linkerSupport.add(l, id);
396
                        }
397
                    }
398
                    int pos = sd.getLength();
399
                    if(l != null) {
400
                        l.insertString(sd, selected ? style : null);
401
                    } else {
402
                        sd.insertString(sd.getLength(), author, style);
403
                    }
404
                    if (!selected) {
405
                        for (SearchHighlight highlight : highlights) {
406
                            if (highlight.getKind() == SearchHighlight.Kind.AUTHOR) {
407
                                int doclen = sd.getLength();
408
                                String highlightMessage = highlight.getSearchText();
409
                                String authorText = sd.getText(pos, doclen - pos).toLowerCase();
410
                                int idx = authorText.indexOf(highlightMessage);
411
                                if (idx > -1) {
412
                                    sd.setCharacterAttributes(doclen - authorText.length() + idx, highlightMessage.length(), hiliteStyle, false);
413
                                }
414
                            }
415
                        }
416
                    }
417
418
                    // add date
419
                    sd.insertString(sd.getLength(), FIELDS_SEPARATOR + entry.getDate(), null);
420
421
                    // add commit msg
422
                    boolean messageChanged = !entry.getMessage().equals(commitMessage);
423
                    commitMessage = entry.getMessage();
424
                    if (commitMessage.endsWith("\n")) commitMessage = commitMessage.substring(0, commitMessage.length() - 1); //NOI18N
425
                    sd.insertString(sd.getLength(), "\n", null); //NOI18N
426
                    int nlc, i;
427
                    for (i = 0, nlc = -1; i != -1 ; i = commitMessage.indexOf('\n', i + 1), nlc++);
428
                    if (nlc > 0 && !item.messageExpanded) {
429
                        commitMessage = commitMessage.substring(0, commitMessage.indexOf("\n")); //NOI18N
430
                    }
431
432
                    // compute issue hyperlinks
433
                    l = linkerSupport.getLinker(VCSHyperlinkSupport.IssueLinker.class, id);
434
                    if (messageChanged) {
435
                        lastWidth = -1;
436
                        if (l != null) {
437
                            // must reinitialize issue linker to paint the new message
438
                            linkerSupport.remove(l, id);
439
                            l = null;
440
                        }
441
                    }
442
                    if(l == null) {
443
                        for (VCSHyperlinkProvider hp : getHyperlinkProviders()) {
444
                            l = VCSHyperlinkSupport.IssueLinker.create(hp, issueHyperlinkStyle, summaryView.getRoot(), sd, commitMessage);
445
                            if(l != null) {
446
                                linkerSupport.add(l, id);
447
                                break; // get the first one
448
                            }
449
                        }
450
                    }
451
                    pos = sd.getLength();
452
                    if(l != null) {
453
                        l.insertString(sd, style);
454
                    } else {
455
                        sd.insertString(sd.getLength(), commitMessage, style);
456
                    }
457
458
                    // tooltip for message
459
                    MessageTooltip mtt = linkerSupport.getLinker(MessageTooltip.class, id);
460
                    if (messageChanged) {
461
                        linkerSupport.remove(mtt, id);
462
                        mtt = null;
463
                    }
464
                    if (mtt == null) {
465
                        linkerSupport.add(new MessageTooltip(entry.getMessage(), pos, sd.getLength()), id);
466
                    }
467
                    
468
                    // paint first line of commit message bold
469
                    int lineEnd = sd.getText(pos, sd.getLength() - pos).indexOf("\n");
470
                    if (lineEnd == -1) {
471
                        lineEnd = sd.getLength() - pos;
472
                    }
473
                    Style s = textPane.addStyle(null, style);
474
                    StyleConstants.setBold(s, true);
475
                    sd.setCharacterAttributes(pos, lineEnd, s, false);
476
                    int msglen = commitMessage.length();
477
                    int doclen = sd.getLength();
478
479
                    if (nlc > 0 && !item.messageExpanded) {
480
                        l = linkerSupport.getLinker(ExpandMsgHyperlink.class, id);
481
                        if (l == null) {
482
                            l = new ExpandMsgHyperlink(item, sd.getLength(), id);
483
                            linkerSupport.add(l, id);
484
                        }
485
                        l.insertString(sd, linkStyle);
486
                    }
487
                    
488
489
                    if (!selected) {
490
                        for (SearchHighlight highlight : highlights) {
491
                            if (highlight.getKind() == SearchHighlight.Kind.MESSAGE) {
492
                                String highlightMessage = highlight.getSearchText();
493
                                int idx = commitMessage.toLowerCase().indexOf(highlightMessage);
494
                                if (idx == -1) {
495
                                    if (nlc > 0 && !item.messageExpanded && entry.getMessage().toLowerCase().contains(highlightMessage)) {
496
                                        sd.setCharacterAttributes(doclen, sd.getLength(), hiliteStyle, false);
497
                                    }
498
                                } else {
499
                                    sd.setCharacterAttributes(doclen - msglen + idx, highlightMessage.length(), hiliteStyle, false);
500
                                }
501
                            }
502
                        }
503
                    }
504
505
                    if (selected) {
506
                        sd.setCharacterAttributes(0, Integer.MAX_VALUE, style, false);
507
                    }
508
                } catch (BadLocationException e) {
322
                } catch (BadLocationException e) {
509
                    ErrorManager.getDefault().notify(e);
323
                    ErrorManager.getDefault().notify(e);
510
                }
324
                }
511
            }
325
            }
512
            lastWidth = resizePane(textPane.getText(), list, lastWidth);
326
            lastWidth = resizePane(revisionCell.getCommitMessageControl().getText(), list, lastWidth);
513
327
514
            return this;
328
            return this;
515
        }
329
        }
516
        
330
517
        @SuppressWarnings("empty-statement")
331
        @SuppressWarnings("empty-statement")
518
        private int resizePane(String text, JList list, int lastWidth) {
332
        private int resizePane (String text, JList list, int lastWidth) {
519
            if(text == null) {
333
            if (text == null) {
520
                text = ""; //NOI18N
334
                text = ""; //NOI18N
521
            }
335
            }
522
            int width = summaryView.getMaster().getComponent().getWidth();
336
            int width = summaryView.getMaster().getComponent().getWidth();
Lines 525-549 Link Here
525
                FontMetrics fm = list.getFontMetrics(list.getFont());
339
                FontMetrics fm = list.getFontMetrics(list.getFont());
526
                int lines = 0;
340
                int lines = 0;
527
                for (String row : rows) {
341
                for (String row : rows) {
528
                    Rectangle2D rect = fm.getStringBounds(row, textPane.getGraphics());
342
                    Rectangle2D rect = fm.getStringBounds(row, revisionCell.getGraphics());
529
                    lines += (int) (rect.getWidth() / (width - 80) + 1);
343
                    lines += (int) (rect.getWidth() / (width - 80) + 1);
530
                }
344
                }
531
                int ph = fm.getHeight() * lines + 9;
345
                int ph = fm.getHeight() * (lines + 1) + 4;
532
                textPane.setPreferredSize(new Dimension(width - 50 - ICON_COLLAPSED.getIconWidth(), ph));
346
                revisionCell.setPreferredSize(new Dimension(width - 50 - ICON_COLLAPSED.getIconWidth(), ph));
533
                setPreferredSize(textPane.getPreferredSize());
347
                setPreferredSize(revisionCell.getPreferredSize());
534
            }
348
            }
535
            return width;
349
            return width;
536
        }
350
        }
351
352
        private void addRevision (JTextPane pane, RevisionItem item, boolean selected, Collection<SearchHighlight> highlights) throws BadLocationException {
353
            StyledDocument sd = pane.getStyledDocument();
354
            // clear document
355
            clearSD(pane, sd);
356
357
            Style selectedStyle = createSelectedStyle(pane);
358
            Style normalStyle = createNormalStyle(pane);
359
            Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs);
360
            Style style;
361
            if (selected) {
362
                style = selectedStyle;
363
            } else {
364
                style = normalStyle;
365
            }
366
367
368
            // add revision
369
            sd.insertString(0, item.getUserData().getRevision(), style);
370
            if (!selected) {
371
                for (AbstractSummaryView.LogEntry.RevisionHighlight highlight : item.getUserData().getRevisionHighlights()) {
372
                    Style s = pane.addStyle(null, normalStyle);
373
                    StyleConstants.setForeground(s, highlight.getForeground());
374
                    StyleConstants.setBackground(s, highlight.getBackground());
375
                    sd.setCharacterAttributes(highlight.getStart(), highlight.getLength(), s, false);
376
                }
377
                for (SearchHighlight highlight : highlights) {
378
                    if (highlight.getKind() == SearchHighlight.Kind.REVISION) {
379
                        int doclen = sd.getLength();
380
                        String highlightMessage = highlight.getSearchText();
381
                        String revisionText = item.getUserData().getRevision().toLowerCase();
382
                        int idx = revisionText.indexOf(highlightMessage);
383
                        if (idx > -1) {
384
                            sd.setCharacterAttributes(doclen - revisionText.length() + idx, highlightMessage.length(), hiliteStyle, false);
385
                        }
386
                    }
387
                }
388
            }
389
        }
390
391
        private void addAuthor (JTextPane pane, RevisionItem item, boolean selected, Collection<SearchHighlight> highlights) throws BadLocationException {
392
            LogEntry entry = item.getUserData();
393
            StyledDocument sd = pane.getStyledDocument();
394
            clearSD(pane, sd);
395
            Style selectedStyle = createSelectedStyle(pane);
396
            Style normalStyle = createNormalStyle(pane);
397
            Style style;
398
            if (selected) {
399
                style = selectedStyle;
400
            } else {
401
                style = normalStyle;
402
            }
403
            Style authorStyle = createAuthorStyle(pane, normalStyle);
404
            Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs);
405
            String author = entry.getAuthor();
406
            VCSHyperlinkSupport.StyledDocumentHyperlink l = linkerSupport.getLinker(VCSHyperlinkSupport.AuthorLinker.class, id);
407
            if (l == null) {
408
                VCSKenaiAccessor.KenaiUser kenaiUser = getKenaiUser(author);
409
                if (kenaiUser != null) {
410
                    AuthorLinker a = new VCSHyperlinkSupport.AuthorLinker(kenaiUser, authorStyle, sd, author);
411
                    l=a;
412
                    linkerSupport.add(l, id);
413
                }
414
            }
415
            int pos = sd.getLength();
416
            if (l != null) {
417
                l.insertString(sd, selected ? style : null);
418
            } else {
419
                sd.insertString(sd.getLength(), author, style);
420
            }
421
            if (!selected) {
422
                for (SearchHighlight highlight : highlights) {
423
                    if (highlight.getKind() == SearchHighlight.Kind.AUTHOR) {
424
                        int doclen = sd.getLength();
425
                        String highlightMessage = highlight.getSearchText();
426
                        String authorText = sd.getText(pos, doclen - pos).toLowerCase();
427
                        int idx = authorText.indexOf(highlightMessage);
428
                        if (idx > -1) {
429
                            sd.setCharacterAttributes(doclen - authorText.length() + idx, highlightMessage.length(), hiliteStyle, false);
430
                        }
431
                    }
432
                }
433
            }
434
        }
435
436
        private void addDate (JTextPane pane, RevisionItem item, boolean selected, Collection<SearchHighlight> highlights) throws BadLocationException {
437
438
            LogEntry entry = item.getUserData();
439
            StyledDocument sd = pane.getStyledDocument();
440
            // clear document
441
            clearSD(pane, sd);
442
443
            Style selectedStyle = createSelectedStyle(pane);
444
            Style normalStyle = createNormalStyle(pane);
445
            Style style;
446
            if (selected) {
447
                style = selectedStyle;
448
            } else {
449
                style = normalStyle;
450
            }
451
452
            // add date
453
            sd.insertString(sd.getLength(), entry.getDate(), style);
454
        }
455
456
        private void addCommitMessage (JTextPane pane, RevisionItem item, boolean selected, Collection<SearchHighlight> highlights) throws BadLocationException {
457
            LogEntry entry = item.getUserData();
458
            StyledDocument sd = pane.getStyledDocument();
459
            clearSD(pane, sd);
460
            Style selectedStyle = createSelectedStyle(pane);
461
            Style normalStyle = createNormalStyle(pane);
462
            Style linkStyle = createLinkStyle(pane, normalStyle);
463
            Style hiliteStyle = createHiliteStyleStyle(pane, normalStyle, searchHiliteAttrs);
464
            Style issueHyperlinkStyle = createIssueHyperlinkStyle(pane, normalStyle);
465
            Style style;
466
            if (selected) {
467
                style = selectedStyle;
468
            } else {
469
                style = normalStyle;
470
            }
471
            boolean messageChanged = !entry.getMessage().isEmpty();
472
            String commitMessage = entry.getMessage().trim();
473
            int nlc;
474
            int i;
475
            for (i = 0, nlc = -1; i != -1; i = commitMessage.indexOf('\n', i + 1), nlc++);
476
            
477
            if (nlc > 0 && !item.messageExpanded) {
478
                //get first line of comment if collapsed
479
                commitMessage = commitMessage.substring(0, commitMessage.indexOf("\n")); //NOI18N
480
            }
481
            IssueLinker l = linkerSupport.getLinker(VCSHyperlinkSupport.IssueLinker.class, id);
482
            if (messageChanged) {
483
                lastWidth = -1;
484
                if (l != null) {
485
                    // must reinitialize issue linker to paint the new message
486
                    linkerSupport.remove(l, id);
487
                    l = null;
488
                }
489
            }
490
            if (l == null) {
491
                for (VCSHyperlinkProvider hp : getHyperlinkProviders()) {
492
                    l = VCSHyperlinkSupport.IssueLinker.create(hp, issueHyperlinkStyle, summaryView.getRoot(), sd, commitMessage);
493
                    if (l != null) {
494
                        linkerSupport.add(l, id);
495
                        break; // got the first one
496
                    }
497
                }
498
            }
499
            if (l != null) {
500
                l.insertString(sd, style);
501
            } else {
502
                sd.insertString(0, commitMessage, style);
503
            }
504
505
            {
506
                //make the first line bold
507
                int lineEnd = sd.getText(0, sd.getLength()).indexOf("\n");
508
                if (lineEnd == -1) {
509
                    lineEnd = sd.getLength();
510
                }
511
                Style s = pane.addStyle(null, style);
512
                StyleConstants.setBold(s, true);
513
                sd.setCharacterAttributes(0, lineEnd, s, false);
514
            }
515
            
516
            int msglen = commitMessage.length();
517
            int doclen = sd.getLength();
518
519
            // remove previous tooltips
520
            {
521
                MessageTooltip mtt = linkerSupport.getLinker(MessageTooltip.class, id);
522
                linkerSupport.remove(mtt, id);
523
            }
524
            
525
            // insert message tooltip and expand link, only if the commit message has more than one line 
526
            // AND if it is not fully visible
527
            if (nlc > 0 && !item.messageExpanded) {
528
                //insert expand link
529
                ExpandMsgHyperlink el = linkerSupport.getLinker(ExpandMsgHyperlink.class, id);
530
                if (el == null) {
531
                    el = new ExpandMsgHyperlink(item, sd.getLength(), id);
532
                    el.setReferenceComponent(revisionCell);
533
                    linkerSupport.add(el, id);
534
                }
535
                el.insertString(sd, linkStyle);
536
537
                //insert commit message tooltip
538
                MessageTooltip messageTooltip = new MessageTooltip(entry.getMessage(), 0, sd.getLength());
539
                messageTooltip.setReferenceComponent(revisionCell);
540
                linkerSupport.add(messageTooltip, id);
541
            }
542
            
543
            if (!selected) {
544
                for (SearchHighlight highlight : highlights) {
545
                    if (highlight.getKind() == SearchHighlight.Kind.MESSAGE) {
546
                        String highlightMessage = highlight.getSearchText();
547
                        int idx = commitMessage.toLowerCase().indexOf(highlightMessage);
548
                        if (idx == -1) {
549
                            if (nlc > 0 && !item.messageExpanded && entry.getMessage().toLowerCase().contains(highlightMessage)) {
550
                                sd.setCharacterAttributes(doclen, sd.getLength(), hiliteStyle, false);
551
                            }
552
                        } else {
553
                            sd.setCharacterAttributes(doclen - msglen + idx, highlightMessage.length(), hiliteStyle, false);
554
                        }
555
                    }
556
                }
557
            }
558
            if (selected) {
559
                sd.setCharacterAttributes(0, Integer.MAX_VALUE, style, false);
560
            }
561
        }
562
563
        private Style createNormalStyle (JTextPane textPane) {
564
            Style normalStyle = textPane.addStyle("normal", null); //NOI18N
565
            StyleConstants.setForeground(normalStyle, UIManager.getColor("List.foreground")); //NOI18N
566
            return normalStyle;
567
        }
568
569
        private Style createIssueHyperlinkStyle (JTextPane textPane, Style normalStyle) {
570
            Style issueHyperlinkStyle = textPane.addStyle("issuehyperlink", normalStyle); //NOI18N
571
            StyleConstants.setForeground(issueHyperlinkStyle, Color.BLUE);
572
            StyleConstants.setUnderline(issueHyperlinkStyle, true);
573
            return issueHyperlinkStyle;
574
        }
575
576
        private Style createAuthorStyle (JTextPane textPane, Style normalStyle) {
577
            Style authorStyle = textPane.addStyle("author", normalStyle); //NOI18N
578
            StyleConstants.setForeground(authorStyle, Color.BLUE);
579
            return authorStyle;
580
        }
581
582
        private Style createLinkStyle (JTextPane textPane, Style normalStyle) {
583
            Style linkStyle = textPane.addStyle("link", normalStyle); //NOI18N
584
            StyleConstants.setForeground(linkStyle, Color.BLUE);
585
            StyleConstants.setBold(linkStyle, true);
586
            return linkStyle;
587
        }
588
589
        private Style createNoindentStyle (JTextPane textPane) {
590
            Style noindentStyle = textPane.addStyle("noindent", null); //NOI18N
591
            StyleConstants.setLeftIndent(noindentStyle, 0);
592
            return noindentStyle;
593
        }
594
595
        private Style createSelectedStyle (JTextPane textPane) {
596
            Style selectedStyle = textPane.addStyle("selected", null); //NOI18N
597
            StyleConstants.setForeground(selectedStyle, selectionForeground);
598
            StyleConstants.setBackground(selectedStyle, selectionBackground);
599
            return selectedStyle;
600
        }
601
602
        private Style createHiliteStyleStyle (JTextPane textPane, Style normalStyle, AttributeSet searchHiliteAttrs) {
603
            Style hiliteStyle = textPane.addStyle("hilite", normalStyle); //NOI18N
604
605
            Color c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Background);
606
            if (c != null) {
607
                StyleConstants.setBackground(hiliteStyle, c);
608
            }
609
            c = (Color) searchHiliteAttrs.getAttribute(StyleConstants.Foreground);
610
            if (c != null) {
611
                StyleConstants.setForeground(hiliteStyle, c);
612
            }
613
614
            return hiliteStyle;
615
        }
537
        
616
        
538
        @Override
617
        @Override
539
        public void paint(Graphics g) {
618
        public void paint(Graphics g) {
540
            super.paint(g);
619
            super.paint(g);
541
            linkerSupport.computeBounds(textPane, id);
620
            AuthorLinker author = linkerSupport.getLinker(AuthorLinker.class, id);
621
            if (author != null) {
622
                author.computeBounds(revisionCell.getAuthorControl(), revisionCell);
623
            }
624
            IssueLinker issue = linkerSupport.getLinker(IssueLinker.class, id);
625
            if (issue != null) {
626
                issue.computeBounds(revisionCell.getCommitMessageControl(), revisionCell);
627
            }
628
            ExpandMsgHyperlink expandMsg = linkerSupport.getLinker(ExpandMsgHyperlink.class, id);
629
            if (expandMsg != null) {
630
                expandMsg.computeBounds(revisionCell.getCommitMessageControl());
631
            }
632
            MessageTooltip tt = linkerSupport.getLinker(MessageTooltip.class, id);
633
            if (tt != null) {
634
                tt.computeBounds(revisionCell.getCommitMessageControl());
635
            }
542
            ExpandLink link = linkerSupport.getLinker(ExpandLink.class, id);
636
            ExpandLink link = linkerSupport.getLinker(ExpandLink.class, id);
543
            if (link != null) {
637
            if (link != null) {
544
                link.computeBounds(expandButton);
638
                link.computeBounds(expandButton);
545
            }
639
            }
546
        }
640
        }
641
642
        private void clearSD (JTextPane pane, StyledDocument sd) {
643
            try {
644
                Style noindentStyle = createNoindentStyle(pane);
645
                sd.remove(0, sd.getLength());
646
                sd.setParagraphAttributes(0, sd.getLength(), noindentStyle, false);
647
            } catch (BadLocationException ex) {
648
                Exceptions.printStackTrace(ex);
649
            }
650
        }
547
    }
651
    }
548
    
652
    
549
    private static class MessageTooltip extends VCSHyperlinkSupport.Hyperlink {
653
    private static class MessageTooltip extends VCSHyperlinkSupport.Hyperlink {
Lines 551-556 Link Here
551
        private final int start;
655
        private final int start;
552
        private final int end;
656
        private final int end;
553
        private final String text;
657
        private final String text;
658
        private RevisionItemCell referenceComponent;
659
660
        public void setReferenceComponent(RevisionItemCell referenceComponent) {
661
            this.referenceComponent = referenceComponent;
662
        }
554
663
555
        private MessageTooltip (String text, int start, int end) {
664
        private MessageTooltip (String text, int start, int end) {
556
            this.start = start;
665
            this.start = start;
Lines 560-566 Link Here
560
        
669
        
561
        @Override
670
        @Override
562
        public boolean mouseMoved (Point p, JComponent component) {
671
        public boolean mouseMoved (Point p, JComponent component) {
563
            if (bounds != null && component.getToolTipText() == null) {
672
            if (bounds != null /*&& component.getToolTipText() == null*/) {
564
                for (Rectangle b : bounds) {
673
                for (Rectangle b : bounds) {
565
                    if (b.contains(p)) {
674
                    if (b.contains(p)) {
566
                        component.setToolTipText(text);
675
                        component.setToolTipText(text);
Lines 568-573 Link Here
568
                    }
677
                    }
569
                }
678
                }
570
            }
679
            }
680
            component.setToolTipText(null);
571
            return false;
681
            return false;
572
        }
682
        }
573
683
Lines 588-593 Link Here
588
                for (int pos = start; pos <= end; ++pos) {
698
                for (int pos = start; pos <= end; ++pos) {
589
                    Rectangle startr = tui.modelToView(textPane, pos, Position.Bias.Forward);
699
                    Rectangle startr = tui.modelToView(textPane, pos, Position.Bias.Forward);
590
                    Rectangle endr = tui.modelToView(textPane, pos + 1, Position.Bias.Backward);
700
                    Rectangle endr = tui.modelToView(textPane, pos + 1, Position.Bias.Backward);
701
                    //prevent NPE if width is too small
702
                    if (null == startr) {continue;}
703
                    if (null == endr) {continue;}
591
                    if (startr.y > lastY) {
704
                    if (startr.y > lastY) {
592
                        rects.add(rec);
705
                        rects.add(rec);
593
                        rec = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
706
                        rec = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
Lines 596-601 Link Here
596
                        rec.setSize(rec.width + endr.x - startr.x, rec.height);
709
                        rec.setSize(rec.width + endr.x - startr.x, rec.height);
597
                    }
710
                    }
598
                }
711
                }
712
                // NOTE the textPane is positioned within a parent panel so the relative bound has to be modified too
713
                // FIXME this is an ugly hack because the public API of org.netbeans.modules.versioning.util.VCSHyperlinkSupport should stay stable
714
                // SOLUTION: provide a reference component which is injected via setter and
715
                // translate from bottom to top of the component hiearchy
716
                if (null != referenceComponent){
717
                    referenceComponent.correctTranslation(textPane, rec);
718
                }
599
                rects.add(rec);
719
                rects.add(rec);
600
                rects.remove(0);
720
                rects.remove(0);
601
                bounds = rects.toArray(new Rectangle[rects.size()]);
721
                bounds = rects.toArray(new Rectangle[rects.size()]);
Lines 1000-1012 Link Here
1000
        }
1120
        }
1001
    }
1121
    }
1002
1122
1003
    private static final String LINK_STRING = "..."; //NOI18N
1123
    private static final String LINK_STRING = " ..."; //NOI18N
1004
    private static final int LINK_STRING_LEN = LINK_STRING.length();
1124
    private static final int LINK_STRING_LEN = LINK_STRING.length();
1005
    private class ExpandMsgHyperlink extends VCSHyperlinkSupport.StyledDocumentHyperlink {
1125
    private class ExpandMsgHyperlink extends VCSHyperlinkSupport.StyledDocumentHyperlink {
1006
        private Rectangle bounds;
1126
        private Rectangle bounds;
1007
        private final int startoffset;
1127
        private final int startoffset;
1008
        private final AbstractSummaryView.RevisionItem item;
1128
        private final AbstractSummaryView.RevisionItem item;
1009
        private final String revision;
1129
        private final String revision;
1130
        private RevisionItemCell referenceComponent;
1131
1132
        public void setReferenceComponent (RevisionItemCell referenceComponent) {
1133
            this.referenceComponent = referenceComponent;
1134
        }
1010
1135
1011
        public ExpandMsgHyperlink (AbstractSummaryView.RevisionItem item, int startoffset, String revision) {
1136
        public ExpandMsgHyperlink (AbstractSummaryView.RevisionItem item, int startoffset, String revision) {
1012
            this.startoffset = startoffset;
1137
            this.startoffset = startoffset;
Lines 1049-1054 Link Here
1049
                Rectangle endr = mtv.getBounds();
1174
                Rectangle endr = mtv.getBounds();
1050
1175
1051
                bounds = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
1176
                bounds = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
1177
                if (null != referenceComponent) {
1178
                    referenceComponent.correctTranslation(textPane, bounds);
1179
                }
1052
            } catch (BadLocationException ex) {
1180
            } catch (BadLocationException ex) {
1053
                throw new RuntimeException(ex);
1181
                throw new RuntimeException(ex);
1054
            }
1182
            }
(-)a/versioning.util/src/org/netbeans/modules/versioning/util/VCSHyperlinkSupport.java (+26 lines)
Lines 42-47 Link Here
42
42
43
package org.netbeans.modules.versioning.util;
43
package org.netbeans.modules.versioning.util;
44
44
45
import java.awt.Container;
45
import java.awt.Cursor;
46
import java.awt.Cursor;
46
import java.awt.Point;
47
import java.awt.Point;
47
import java.awt.Rectangle;
48
import java.awt.Rectangle;
Lines 232-237 Link Here
232
233
233
        @Override
234
        @Override
234
        public void computeBounds(JTextPane textPane) {
235
        public void computeBounds(JTextPane textPane) {
236
            computeBounds(textPane, null);
237
        }
238
        
239
        public void computeBounds(JTextPane textPane, BoundsTranslator translator) {
235
            Rectangle tpBounds = textPane.getBounds();
240
            Rectangle tpBounds = textPane.getBounds();
236
            TextUI tui = textPane.getUI();
241
            TextUI tui = textPane.getUI();
237
            this.bounds = new Rectangle[length];
242
            this.bounds = new Rectangle[length];
Lines 240-245 Link Here
240
                    Rectangle startr = tui.modelToView(textPane, docstart[i], Position.Bias.Forward).getBounds();
245
                    Rectangle startr = tui.modelToView(textPane, docstart[i], Position.Bias.Forward).getBounds();
241
                    Rectangle endr = tui.modelToView(textPane, docend[i], Position.Bias.Backward).getBounds();
246
                    Rectangle endr = tui.modelToView(textPane, docend[i], Position.Bias.Backward).getBounds();
242
                    this.bounds[i] = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
247
                    this.bounds[i] = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
248
                    //NOTE the textPane is positioned within a parent panel so the origin has to be modified too
249
                    if (null != translator) {
250
                        translator.correctTranslation(textPane, this.bounds[i]);
251
                    }
243
                } catch (BadLocationException ex) { }
252
                } catch (BadLocationException ex) { }
244
            }
253
            }
245
        }
254
        }
Lines 305-310 Link Here
305
314
306
        @Override
315
        @Override
307
        public void computeBounds(JTextPane textPane) {
316
        public void computeBounds(JTextPane textPane) {
317
            computeBounds(textPane, null);
318
        }
319
        
320
        public void computeBounds(JTextPane textPane, BoundsTranslator translator) {
308
            Rectangle tpBounds = textPane.getBounds();
321
            Rectangle tpBounds = textPane.getBounds();
309
            TextUI tui = textPane.getUI();
322
            TextUI tui = textPane.getUI();
310
            this.bounds = new Rectangle();
323
            this.bounds = new Rectangle();
Lines 315-320 Link Here
315
                    endr.x += kenaiUser.getIcon().getIconWidth();
328
                    endr.x += kenaiUser.getIcon().getIconWidth();
316
                }
329
                }
317
                this.bounds = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
330
                this.bounds = new Rectangle(tpBounds.x + startr.x, startr.y, endr.x - startr.x, startr.height);
331
                
332
                if (null != translator) {
333
                    translator.correctTranslation(textPane, this.bounds);
334
                }
318
            } catch (BadLocationException ex) {
335
            } catch (BadLocationException ex) {
319
                Exceptions.printStackTrace(ex);
336
                Exceptions.printStackTrace(ex);
320
            }
337
            }
Lines 360-364 Link Here
360
            sd.insertString(sd.getLength(), " ", iconStyle);
377
            sd.insertString(sd.getLength(), " ", iconStyle);
361
        }
378
        }
362
    }
379
    }
380
381
    public static interface BoundsTranslator {
382
        /**
383
         * Corrects the bounding rectangle of nested textpanes.
384
         * @param startComponent
385
         * @param r 
386
         */
387
        public void correctTranslation (final Container startComponent, final Rectangle r);
388
    }
363
}
389
}
364
390

Return to bug 218850