2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
29 * Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
31 * If you wish your version of this file to be governed by only the CDDL
32 * or only the GPL Version 2, indicate your decision by adding
33 * "[Contributor] elects to include this software in this distribution
34 * under the [CDDL or GPL Version 2] license." If you do not indicate a
35 * single choice of license, a recipient has the option to distribute
36 * your version of this file under either the CDDL, the GPL Version 2 or
37 * to extend the choice of license to its licensees as provided above.
38 * However, if you add GPL Version 2 code and therefore, elected the GPL
39 * Version 2 license, then the option applies only if the new code is
40 * made subject to such option by the copyright holder.
42 package org
.nbgit
.ui
.log
;
44 import java
.awt
.BorderLayout
;
45 import java
.awt
.Color
;
46 import java
.awt
.Component
;
47 import java
.awt
.Cursor
;
48 import java
.awt
.Dimension
;
49 import java
.awt
.FlowLayout
;
50 import java
.awt
.FontMetrics
;
51 import java
.awt
.Graphics
;
52 import java
.awt
.Point
;
53 import java
.awt
.Rectangle
;
54 import java
.awt
.event
.ActionEvent
;
55 import java
.awt
.event
.ComponentEvent
;
56 import java
.awt
.event
.ComponentListener
;
57 import java
.awt
.event
.KeyEvent
;
58 import java
.awt
.event
.MouseEvent
;
59 import java
.awt
.event
.MouseListener
;
60 import java
.awt
.event
.MouseMotionListener
;
61 import java
.awt
.geom
.Rectangle2D
;
63 import java
.io
.IOException
;
64 import java
.text
.DateFormat
;
65 import java
.util
.ArrayList
;
66 import java
.util
.Collection
;
67 import java
.util
.HashMap
;
68 import java
.util
.HashSet
;
69 import java
.util
.List
;
72 import javax
.swing
.AbstractAction
;
73 import javax
.swing
.AbstractListModel
;
74 import javax
.swing
.BorderFactory
;
75 import javax
.swing
.JComponent
;
76 import javax
.swing
.JLabel
;
77 import javax
.swing
.JList
;
78 import javax
.swing
.JMenuItem
;
79 import javax
.swing
.JPanel
;
80 import javax
.swing
.JPopupMenu
;
81 import javax
.swing
.JScrollPane
;
82 import javax
.swing
.JTextPane
;
83 import javax
.swing
.KeyStroke
;
84 import javax
.swing
.ListCellRenderer
;
85 import javax
.swing
.UIManager
;
86 import javax
.swing
.text
.AttributeSet
;
87 import javax
.swing
.text
.BadLocationException
;
88 import javax
.swing
.text
.Style
;
89 import javax
.swing
.text
.StyleConstants
;
90 import javax
.swing
.text
.StyledDocument
;
92 import org
.nbgit
.GitModuleConfig
;
93 import org
.nbgit
.GitProgressSupport
;
94 import org
.nbgit
.ui
.diff
.DiffSetupSource
;
95 import org
.nbgit
.ui
.update
.RevertModificationsAction
;
96 import org
.nbgit
.util
.GitUtils
;
97 import org
.netbeans
.api
.editor
.mimelookup
.MimeLookup
;
98 import org
.netbeans
.api
.editor
.mimelookup
.MimePath
;
99 import org
.netbeans
.api
.editor
.settings
.FontColorSettings
;
100 import org
.openide
.ErrorManager
;
101 import org
.openide
.filesystems
.FileObject
;
102 import org
.openide
.filesystems
.FileUtil
;
103 import org
.openide
.nodes
.Node
;
104 import org
.openide
.util
.NbBundle
;
105 import org
.openide
.util
.RequestProcessor
;
106 import org
.openide
.windows
.TopComponent
;
109 * Shows Search History results in a JList.
111 * @author Maros Sandor
113 class SummaryView
implements MouseListener
, ComponentListener
, MouseMotionListener
, DiffSetupSource
{
115 private static final String SUMMARY_DIFF_PROPERTY
= "Summary-Diff-";
116 private static final String SUMMARY_REVERT_PROPERTY
= "Summary-Revert-";
117 private static final String SUMMARY_EXPORTDIFFS_PROPERTY
= "Summary-ExportDiffs-";
118 private final SearchHistoryPanel master
;
119 private JList resultsList
;
120 private JScrollPane scrollPane
;
121 private final List dispResults
;
122 private String message
;
123 private AttributeSet searchHiliteAttrs
;
124 private List
<RepositoryRevision
> results
;
126 public SummaryView(SearchHistoryPanel master
, List
<RepositoryRevision
> results
)
128 this.master
= master
;
129 this.results
= results
;
130 this.dispResults
= expandResults(results
);
131 FontColorSettings fcs
= MimeLookup
.getLookup(MimePath
.get("text/x-java")).lookup(FontColorSettings
.class); // NOI18N
132 searchHiliteAttrs
= fcs
.getFontColors("highlight-search"); // NOI18N
133 message
= master
.getCriteria().getCommitMessage();
134 resultsList
= new JList(new SummaryListModel());
135 resultsList
.setFixedCellHeight(-1);
136 resultsList
.addMouseListener(this);
137 resultsList
.addMouseMotionListener(this);
138 resultsList
.setCellRenderer(new SummaryCellRenderer());
139 resultsList
.getAccessibleContext().setAccessibleName(NbBundle
.getMessage(SummaryView
.class, "ACSN_SummaryView_List")); // NOI18N
140 resultsList
.getAccessibleContext().setAccessibleDescription(NbBundle
.getMessage(SummaryView
.class, "ACSD_SummaryView_List")); // NOI18N
141 scrollPane
= new JScrollPane(resultsList
, JScrollPane
.VERTICAL_SCROLLBAR_ALWAYS
, JScrollPane
.HORIZONTAL_SCROLLBAR_NEVER
);
142 master
.addComponentListener(this);
143 resultsList
.getInputMap(JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
).put(
144 KeyStroke
.getKeyStroke(KeyEvent
.VK_F10
, KeyEvent
.SHIFT_DOWN_MASK
), "org.openide.actions.PopupAction");
145 resultsList
.getActionMap().put("org.openide.actions.PopupAction", new AbstractAction() {
147 public void actionPerformed(ActionEvent e
)
149 onPopup(org
.netbeans
.modules
.versioning
.util
.Utils
.getPositionForPopup(resultsList
));
155 public void componentResized(ComponentEvent e
)
157 int[] selection
= resultsList
.getSelectedIndices();
158 resultsList
.setModel(new SummaryListModel());
159 resultsList
.setSelectedIndices(selection
);
162 public void componentHidden(ComponentEvent e
)
167 public void componentMoved(ComponentEvent e
)
172 public void componentShown(ComponentEvent e
)
177 @SuppressWarnings("unchecked")
178 private List
expandResults(List
<RepositoryRevision
> results
)
180 ArrayList newResults
= new ArrayList(results
.size());
181 for (RepositoryRevision repositoryRevision
: results
) {
182 newResults
.add(repositoryRevision
);
183 List
<RepositoryRevision
.Event
> events
= repositoryRevision
.getEvents();
184 for (RepositoryRevision
.Event event
: events
) {
185 newResults
.add(event
);
191 public void mouseClicked(MouseEvent e
)
193 int idx
= resultsList
.locationToIndex(e
.getPoint());
196 Rectangle rect
= resultsList
.getCellBounds(idx
, idx
);
197 Point p
= new Point(e
.getX() - rect
.x
, e
.getY() - rect
.y
);
198 Rectangle diffBounds
= (Rectangle
) resultsList
.getClientProperty(SUMMARY_DIFF_PROPERTY
+ idx
); // NOI18N
199 if (diffBounds
!= null && diffBounds
.contains(p
))
201 diffBounds
= (Rectangle
) resultsList
.getClientProperty(SUMMARY_REVERT_PROPERTY
+ idx
); // NOI18N
202 if (diffBounds
!= null && diffBounds
.contains(p
))
203 revertModifications(new int[]{idx
});
206 public void mouseEntered(MouseEvent e
)
211 public void mouseExited(MouseEvent e
)
216 public void mousePressed(MouseEvent e
)
218 if (!master
.isIncomingSearch() && e
.isPopupTrigger())
222 public void mouseReleased(MouseEvent e
)
224 if (!master
.isIncomingSearch() && e
.isPopupTrigger())
228 public void mouseDragged(MouseEvent e
)
232 public void mouseMoved(MouseEvent e
)
234 int idx
= resultsList
.locationToIndex(e
.getPoint());
237 Rectangle rect
= resultsList
.getCellBounds(idx
, idx
);
238 Point p
= new Point(e
.getX() - rect
.x
, e
.getY() - rect
.y
);
239 Rectangle diffBounds
= (Rectangle
) resultsList
.getClientProperty(SUMMARY_DIFF_PROPERTY
+ idx
); // NOI18N
240 if (diffBounds
!= null && diffBounds
.contains(p
)) {
241 resultsList
.setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
244 diffBounds
= (Rectangle
) resultsList
.getClientProperty(SUMMARY_REVERT_PROPERTY
+ idx
); // NOI18N
245 if (diffBounds
!= null && diffBounds
.contains(p
)) {
246 resultsList
.setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
249 diffBounds
= (Rectangle
) resultsList
.getClientProperty(SUMMARY_EXPORTDIFFS_PROPERTY
+ idx
); // NOI18N
250 if (diffBounds
!= null && diffBounds
.contains(p
)) {
251 resultsList
.setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
254 resultsList
.setCursor(Cursor
.getPredefinedCursor(Cursor
.DEFAULT_CURSOR
));
257 public Collection
getSetups()
259 Node
[] nodes
= TopComponent
.getRegistry().getActivatedNodes();
260 if (nodes
.length
== 0)
261 return master
.getSetups(results
.toArray(new RepositoryRevision
[results
.size()]), new RepositoryRevision
.Event
[0]);
263 Set
<RepositoryRevision
.Event
> events
= new HashSet
<RepositoryRevision
.Event
>();
264 Set
<RepositoryRevision
> revisions
= new HashSet
<RepositoryRevision
>();
266 int[] sel
= resultsList
.getSelectedIndices();
268 Object revCon
= dispResults
.get(i
);
269 if (revCon
instanceof RepositoryRevision
)
270 revisions
.add((RepositoryRevision
) revCon
);
272 events
.add((RepositoryRevision
.Event
) revCon
);
274 return master
.getSetups(revisions
.toArray(new RepositoryRevision
[revisions
.size()]), events
.toArray(new RepositoryRevision
.Event
[events
.size()]));
277 public String
getSetupDisplayName()
282 private void onPopup(MouseEvent e
)
284 onPopup(e
.getPoint());
287 private void onPopup(Point p
)
289 int[] sel
= resultsList
.getSelectedIndices();
290 if (sel
.length
== 0) {
291 int idx
= resultsList
.locationToIndex(p
);
294 resultsList
.setSelectedIndex(idx
);
295 sel
= new int[]{idx
};
297 final int[] selection
= sel
;
299 JPopupMenu menu
= new JPopupMenu();
301 String previousRevision
= null;
302 RepositoryRevision container
= null;
303 final RepositoryRevision
.Event
[] drev
;
305 Object revCon
= dispResults
.get(selection
[0]);
308 boolean noExDeletedExistingFiles
= true;
309 boolean revisionSelected
;
310 boolean missingFile
= false;
311 boolean oneRevisionMultiselected
= true;
313 if (revCon
instanceof RepositoryRevision
) {
314 revisionSelected
= true;
315 container
= (RepositoryRevision
) dispResults
.get(selection
[0]);
316 drev
= new RepositoryRevision
.Event
[0];
317 oneRevisionMultiselected
= true;
318 noExDeletedExistingFiles
= true;
320 revisionSelected
= false;
321 drev
= new RepositoryRevision
.Event
[selection
.length
];
323 for (int i
= 0; i
< selection
.length
; i
++) {
324 drev
[i
] = (RepositoryRevision
.Event
) dispResults
.get(selection
[i
]);
326 if (!missingFile
&& drev
[i
].getFile() == null)
328 if (oneRevisionMultiselected
&& i
> 0 &&
329 drev
[0].getLogInfoHeader().getRevision().equals(drev
[i
].getLogInfoHeader().getRevision()))
330 oneRevisionMultiselected
= false;
331 if (drev
[i
].getFile() != null && drev
[i
].getFile().exists() && drev
[i
].getChangedPath().getAction() == 'D')
332 noExDeletedExistingFiles
= false;
334 container
= drev
[0].getLogInfoHeader();
336 long revision
= 0; //Long.parseLong(container.getLog().getRevision());
338 final boolean rollbackToEnabled
= !missingFile
&& !revisionSelected
&& oneRevisionMultiselected
;
339 final boolean rollbackChangeEnabled
= !missingFile
&& oneRevisionMultiselected
&& (drev
.length
== 0 || noExDeletedExistingFiles
); // drev.length == 0 => the whole revision was selected
340 final boolean viewEnabled
= selection
.length
== 1 && !revisionSelected
&& drev
[0].getFile() != null && drev
[0].getFile().exists() && !drev
[0].getFile().isDirectory();
341 final boolean diffToPrevEnabled
= selection
.length
== 1;
344 menu
.add(new JMenuItem(new AbstractAction(NbBundle
.getMessage(SummaryView
.class, "CTL_SummaryView_DiffToPrevious", "" + previousRevision
)) { // NOI18N
347 setEnabled(diffToPrevEnabled
);
350 public void actionPerformed(ActionEvent e
)
352 diffPrevious(selection
[0]);
357 /* TBD - Support Rollback Changes:
358 * Need to figure out how to implement SVN functionality to allow you to rollback a change
359 * currently for svn this cmd runs: merge -R <revX>:<revY> it basically diffs the two revs
360 * and creates a patch which is then applied ot the file in the working dir, effectively removing
361 * the change applied between revX and revY
362 * In hg we'd need to do a hg diff -r revX -r revY, generate a patch and then apply it to the working dir copy
364 menu.add(new JMenuItem(new AbstractAction(NbBundle.getMessage(SummaryView.class, "CTL_SummaryView_RollbackChange")) { // NOI18N
366 setEnabled(false); // rollbackChangeEnabled);
368 public void actionPerformed(ActionEvent e) {
369 revertModifications(selection);
373 if (!revisionSelected
) {
374 menu
.add(new JMenuItem(new AbstractAction(NbBundle
.getMessage(SummaryView
.class, "CTL_SummaryView_RollbackTo", "" + revision
)) { // NOI18N
377 setEnabled(rollbackToEnabled
);
380 public void actionPerformed(ActionEvent e
)
382 revertModifications(selection
);
385 /*public void actionPerformed(ActionEvent e) {
386 RequestProcessor.getDefault().post(new Runnable() {
393 menu
.add(new JMenuItem(new AbstractAction(NbBundle
.getMessage(SummaryView
.class, "CTL_SummaryView_View")) { // NOI18N
396 setEnabled(viewEnabled
);
399 public void actionPerformed(ActionEvent e
)
401 RequestProcessor
.getDefault().post(new Runnable() {
414 menu
.show(resultsList
, p
.x
, p
.y
);
418 * Overwrites local file with this revision.
422 static void rollback(RepositoryRevision
.Event event
)
424 rollback(new RepositoryRevision
.Event
[]{event
});
428 * Overwrites local file with this revision.
432 static void rollback(final RepositoryRevision
.Event
[] events
)
434 String repository
= events
[0].getLogInfoHeader().getRepositoryRootUrl();
435 RequestProcessor rp
= Git
.getInstance().getRequestProcessor(repository
);
436 GitProgressSupport support
= new GitProgressSupport() {
438 public void perform()
440 for (RepositoryRevision
.Event event
: events
) {
441 rollback(event
, this);
446 support
.start(rp
, repository
, NbBundle
.getMessage(SummaryView
.class, "MSG_Rollback_Progress")); // NOI18N
450 private static void rollback(RepositoryRevision
.Event event
, GitProgressSupport progress
)
452 File file
= event
.getFile();
453 File parent
= file
.getParentFile();
456 File oldFile
= GitUtils
.getFileRevision(event
.getFile(), event
.getLogInfoHeader().getRevision());
458 FileUtil
.copyFile(FileUtil
.toFileObject(oldFile
), FileUtil
.toFileObject(parent
), file
.getName(), "");
459 } catch (IOException e
) {
460 ErrorManager
.getDefault().notify(e
);
464 private void revertModifications(int[] selection
)
466 Set
<RepositoryRevision
.Event
> events
= new HashSet
<RepositoryRevision
.Event
>();
467 Set
<RepositoryRevision
> revisions
= new HashSet
<RepositoryRevision
>();
468 for (int idx
: selection
) {
469 Object o
= dispResults
.get(idx
);
470 if (o
instanceof RepositoryRevision
)
471 revisions
.add((RepositoryRevision
) o
);
473 events
.add((RepositoryRevision
.Event
) o
);
475 revert(master
, revisions
.toArray(new RepositoryRevision
[revisions
.size()]), events
.toArray(new RepositoryRevision
.Event
[events
.size()]));
478 static void revert(final SearchHistoryPanel master
, final RepositoryRevision
[] revisions
, final RepositoryRevision
.Event
[] events
)
480 String url
= master
.getSearchRepositoryRootUrl();
481 RequestProcessor rp
= Git
.getInstance().getRequestProcessor(url
);
482 GitProgressSupport support
= new GitProgressSupport() {
484 public void perform()
486 revertImpl(master
, revisions
, events
, this);
490 support
.start(rp
, url
, NbBundle
.getMessage(SummaryView
.class, "MSG_Revert_Progress")); // NOI18N
493 private static void revertImpl(SearchHistoryPanel master
, RepositoryRevision
[] revisions
, RepositoryRevision
.Event
[] events
, GitProgressSupport progress
)
495 List
<File
> revertFiles
= new ArrayList
<File
>();
496 boolean doBackup
= GitModuleConfig
.getDefault().getBackupOnRevertModifications();
497 for (RepositoryRevision revision
: revisions
) {
498 File root
= new File(revision
.getRepositoryRootUrl());
499 for (RepositoryRevision
.Event event
: revision
.getEvents()) {
500 if (event
.getFile() == null)
502 revertFiles
.add(event
.getFile());
504 RevertModificationsAction
.performRevert(
505 root
, revision
.getRevision(), revertFiles
, doBackup
, progress
.getLogger());
510 Map
<File
, List
<RepositoryRevision
.Event
>> revertMap
= new HashMap
<File
, List
<RepositoryRevision
.Event
>>();
511 for (RepositoryRevision
.Event event
: events
) {
512 if (event
.getFile() == null)
515 File root
= Git
.getInstance().getTopmostManagedParent(event
.getFile());
516 if (revertMap
== null)
517 revertMap
= new HashMap
<File
, List
<RepositoryRevision
.Event
>>();
518 List
<RepositoryRevision
.Event
> revEvents
= revertMap
.get(root
);
519 if (revEvents
== null) {
520 revEvents
= new ArrayList
<RepositoryRevision
.Event
>();
521 revertMap
.put(root
, revEvents
);
523 revEvents
.add(event
);
525 if (events
!= null && events
.length
> 0 && revertMap
!= null && !revertMap
.isEmpty()) {
526 Set
<File
> roots
= revertMap
.keySet();
527 for (File root
: roots
) {
528 List
<RepositoryRevision
.Event
> revEvents
= revertMap
.get(root
);
529 for (RepositoryRevision
.Event event
: revEvents
) {
530 if (event
.getFile() == null)
532 revertFiles
.add(event
.getFile());
534 if (revEvents
!= null && !revEvents
.isEmpty()) {
535 // Assuming all files in a given repository reverting to same revision
537 RevertModificationsAction.performRevert(
538 root, revEvents.get(0).getLogInfoHeader().getLog().getRevision(), revertFiles, doBackup, progress.getLogger());
546 private void view(int idx
)
548 Object o
= dispResults
.get(idx
);
549 if (o
instanceof RepositoryRevision
.Event
)
551 RepositoryRevision
.Event drev
= (RepositoryRevision
.Event
) o
;
552 File file
= GitUtils
.getFileRevision(drev
.getFile(), drev
.getLogInfoHeader().getRevision());
554 FileObject fo
= FileUtil
.toFileObject(FileUtil
.normalizeFile(file
));
555 org
.netbeans
.modules
.versioning
.util
.Utils
.openFile(fo
, drev
.getLogInfoHeader().getRevision());
556 } catch (IOException ex
) {
557 // Ignore if file not available in cache
561 private void diffPrevious(int idx
)
563 Object o
= dispResults
.get(idx
);
564 if (o
instanceof RepositoryRevision
.Event
) {
565 RepositoryRevision
.Event drev
= (RepositoryRevision
.Event
) o
;
566 master
.showDiff(drev
);
568 RepositoryRevision container
= (RepositoryRevision
) o
;
569 master
.showDiff(container
);
573 public JComponent
getComponent()
578 private class SummaryListModel
extends AbstractListModel
{
582 return dispResults
.size();
585 public Object
getElementAt(int index
)
587 return dispResults
.get(index
);
592 private class SummaryCellRenderer
extends JPanel
implements ListCellRenderer
{
594 private static final String FIELDS_SEPARATOR
= " "; // NOI18N
595 private static final double DARKEN_FACTOR
= 0.95;
596 private Style selectedStyle
;
597 private Style normalStyle
;
598 private Style filenameStyle
;
599 private Style indentStyle
;
600 private Style noindentStyle
;
601 private Style hiliteStyle
;
602 private JTextPane textPane
= new JTextPane();
603 private JPanel actionsPane
= new JPanel();
604 private DateFormat defaultFormat
;
606 private HyperlinkLabel diffLink
;
607 private HyperlinkLabel revertLink
;
609 public SummaryCellRenderer()
611 selectedStyle
= textPane
.addStyle("selected", null); // NOI18N
612 StyleConstants
.setForeground(selectedStyle
, UIManager
.getColor("List.selectionForeground")); // NOI18N
613 normalStyle
= textPane
.addStyle("normal", null); // NOI18N
614 StyleConstants
.setForeground(normalStyle
, UIManager
.getColor("List.foreground")); // NOI18N
615 filenameStyle
= textPane
.addStyle("filename", normalStyle
); // NOI18N
616 StyleConstants
.setBold(filenameStyle
, true);
617 indentStyle
= textPane
.addStyle("indent", null); // NOI18N
618 StyleConstants
.setLeftIndent(indentStyle
, 50);
619 noindentStyle
= textPane
.addStyle("noindent", null); // NOI18N
620 StyleConstants
.setLeftIndent(noindentStyle
, 0);
621 defaultFormat
= DateFormat
.getDateTimeInstance();
623 hiliteStyle
= textPane
.addStyle("hilite", normalStyle
); // NOI18N
624 Color c
= (Color
) searchHiliteAttrs
.getAttribute(StyleConstants
.Background
);
626 StyleConstants
.setBackground(hiliteStyle
, c
);
627 c
= (Color
) searchHiliteAttrs
.getAttribute(StyleConstants
.Foreground
);
629 StyleConstants
.setForeground(hiliteStyle
, c
);
631 setLayout(new BorderLayout());
633 add(actionsPane
, BorderLayout
.PAGE_END
);
634 actionsPane
.setLayout(new FlowLayout(FlowLayout
.TRAILING
, 2, 5));
636 diffLink
= new HyperlinkLabel();
637 diffLink
.setBorder(BorderFactory
.createEmptyBorder(0, 0, 0, 8));
638 actionsPane
.add(diffLink
);
640 revertLink
= new HyperlinkLabel();
641 revertLink
.setBorder(BorderFactory
.createEmptyBorder(0, 0, 0, 8));
642 //actionsPane.add(revertLink);
644 textPane
.setBorder(null);
647 public Color
darker(Color c
)
649 return new Color(Math
.max((int) (c
.getRed() * DARKEN_FACTOR
), 0),
650 Math
.max((int) (c
.getGreen() * DARKEN_FACTOR
), 0),
651 Math
.max((int) (c
.getBlue() * DARKEN_FACTOR
), 0));
654 public Component
getListCellRendererComponent(JList list
, Object value
, int index
, boolean isSelected
, boolean cellHasFocus
)
656 if (value
instanceof RepositoryRevision
)
657 renderContainer(list
, (RepositoryRevision
) value
, index
, isSelected
);
659 renderRevision(list
, (RepositoryRevision
.Event
) value
, index
, isSelected
);
663 private void renderContainer(JList list
, RepositoryRevision container
, int index
, boolean isSelected
)
666 StyledDocument sd
= textPane
.getStyledDocument();
669 Color backgroundColor
;
670 Color foregroundColor
;
673 foregroundColor
= UIManager
.getColor("List.selectionForeground"); // NOI18N
674 backgroundColor
= UIManager
.getColor("List.selectionBackground"); // NOI18N
675 style
= selectedStyle
;
677 foregroundColor
= UIManager
.getColor("List.foreground"); // NOI18N
678 backgroundColor
= UIManager
.getColor("List.background"); // NOI18N
679 backgroundColor
= darker(backgroundColor
);
682 textPane
.setBackground(backgroundColor
);
683 actionsPane
.setBackground(backgroundColor
);
688 sd
.remove(0, sd
.getLength());
689 sd
.setParagraphAttributes(0, sd
.getLength(), noindentStyle
, false);
691 sd
.insertString(0, container
.getRevision(), null); // NOI18N
692 sd
.setCharacterAttributes(0, sd
.getLength(), filenameStyle
, false);
693 sd
.insertString(sd
.getLength(), FIELDS_SEPARATOR
+ container
.getAuthor(), null);
694 sd
.insertString(sd
.getLength(), FIELDS_SEPARATOR
+ defaultFormat
.format(container
.getDate()), null);
696 String commitMessage
= container
.getMessage();
697 if (commitMessage
.endsWith("\n"))
698 commitMessage
= commitMessage
.substring(0, commitMessage
.length() - 1); // NOI18N
699 sd
.insertString(sd
.getLength(), "\n", null);
701 sd
.insertString(sd
.getLength(), commitMessage
, null);
703 if (message
!= null && !isSelected
) {
704 int idx
= commitMessage
.indexOf(message
);
706 int len
= commitMessage
.length();
707 int doclen
= sd
.getLength();
708 sd
.setCharacterAttributes(doclen
- len
+ idx
, message
.length(), hiliteStyle
, false);
712 resizePane(commitMessage
, list
.getFontMetrics(list
.getFont()));
713 sd
.setCharacterAttributes(0, Integer
.MAX_VALUE
, style
, false);
714 } catch (BadLocationException e
) {
715 ErrorManager
.getDefault().notify(e
);
718 actionsPane
.setVisible(true);
719 if (!master
.isIncomingSearch()) {
720 diffLink
.set(NbBundle
.getMessage(SummaryView
.class, "CTL_Action_Diff"), foregroundColor
, backgroundColor
);// NOI18N
721 revertLink
.set(NbBundle
.getMessage(SummaryView
.class, "CTL_Action_Revert"), foregroundColor
, backgroundColor
); // NOI18N
725 private void renderRevision(JList list
, RepositoryRevision
.Event dispRevision
, final int index
, boolean isSelected
)
728 StyledDocument sd
= textPane
.getStyledDocument();
730 Color backgroundColor
;
731 Color foregroundColor
;
734 foregroundColor
= UIManager
.getColor("List.selectionForeground"); // NOI18N
735 backgroundColor
= UIManager
.getColor("List.selectionBackground"); // NOI18N
736 style
= selectedStyle
;
738 foregroundColor
= UIManager
.getColor("List.foreground"); // NOI18N
739 backgroundColor
= UIManager
.getColor("List.background"); // NOI18N
742 textPane
.setBackground(backgroundColor
);
743 actionsPane
.setVisible(false);
747 sd
.remove(0, sd
.getLength());
748 sd
.setParagraphAttributes(0, sd
.getLength(), indentStyle
, false);
750 sd
.insertString(sd
.getLength(), String
.valueOf(dispRevision
.getChangedPath().getAction()), null);
751 sd
.insertString(sd
.getLength(), FIELDS_SEPARATOR
+ dispRevision
.getChangedPath().getPath(), null);
753 sd
.setCharacterAttributes(0, Integer
.MAX_VALUE
, style
, false);
754 resizePane(sd
.getText(0, sd
.getLength() - 1), list
.getFontMetrics(list
.getFont()));
755 } catch (BadLocationException e
) {
756 ErrorManager
.getDefault().notify(e
);
760 @SuppressWarnings("empty-statement")
761 private void resizePane(String text
, FontMetrics fm
)
765 int width
= master
.getWidth();
767 Rectangle2D rect
= fm
.getStringBounds(text
, textPane
.getGraphics());
769 for (nlc
= -1 , i
= 0; i
!= -1; i
= text
.indexOf('\n', i
+ 1), nlc
++);
771 int lines
= (int) (rect
.getWidth() / (width
- 80) + 1);
772 int ph
= fm
.getHeight() * (lines
+ nlc
) + 0;
773 textPane
.setPreferredSize(new Dimension(width
- 50, ph
));
778 protected void paintComponent(Graphics g
)
780 super.paintComponent(g
);
783 Rectangle apb
= actionsPane
.getBounds();
786 Rectangle bounds
= diffLink
.getBounds();
787 bounds
.setBounds(bounds
.x
, bounds
.y
+ apb
.y
, bounds
.width
, bounds
.height
);
788 resultsList
.putClientProperty(SUMMARY_DIFF_PROPERTY
+ index
, bounds
); // NOI18N
791 Rectangle bounds
= revertLink
.getBounds();
792 bounds
.setBounds(bounds
.x
, bounds
.y
+ apb
.y
, bounds
.width
, bounds
.height
);
793 resultsList
.putClientProperty(SUMMARY_REVERT_PROPERTY
+ index
, bounds
); // NOI18N
798 private static class HyperlinkLabel
extends JLabel
{
800 public HyperlinkLabel()
802 setCursor(Cursor
.getPredefinedCursor(Cursor
.HAND_CURSOR
));
805 public void set(String text
, Color foreground
, Color background
)
807 StringBuilder sb
= new StringBuilder(100);
808 if (foreground
.equals(UIManager
.getColor("List.foreground"))) { // NOI18N
809 sb
.append("<html><a href=\"\">"); // NOI18N
811 sb
.append("</a>"); // NOI18N
813 sb
.append("<html><a href=\"\" style=\"color:"); // NOI18N
814 sb
.append("rgb("); // NOI18N
815 sb
.append(foreground
.getRed());
816 sb
.append(","); // NOI18N
817 sb
.append(foreground
.getGreen());
818 sb
.append(","); // NOI18N
819 sb
.append(foreground
.getBlue());
820 sb
.append(")"); // NOI18N
821 sb
.append("\">"); // NOI18N
823 sb
.append("</a>"); // NOI18N
825 setText(sb
.toString());
826 setBackground(background
);