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.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.modules.mercurial.ui.annotate;
43
import org.netbeans.modules.versioning.spi.VCSContext;
48
import java.awt.event.ActionEvent;
49
import org.openide.nodes.Node;
50
import org.openide.cookies.EditorCookie;
51
import org.openide.util.NbBundle;
52
import org.openide.util.RequestProcessor;
53
import org.openide.windows.WindowManager;
54
import org.netbeans.modules.mercurial.HgException;
55
import org.netbeans.modules.mercurial.HgProgressSupport;
56
import org.netbeans.modules.mercurial.Mercurial;
57
import org.netbeans.modules.mercurial.FileStatusCache;
58
import org.netbeans.modules.mercurial.FileInformation;
59
import org.netbeans.modules.mercurial.util.HgUtils;
60
import org.openide.filesystems.FileObject;
61
import org.openide.filesystems.FileUtil;
62
import org.openide.loaders.DataObject;
63
import org.netbeans.modules.mercurial.util.HgCommand;
64
import org.netbeans.modules.mercurial.util.HgLogMessage;
65
import org.openide.windows.TopComponent;
66
import java.util.logging.Level;
67
import org.openide.DialogDisplayer;
68
import org.openide.NotifyDescriptor;
71
* Annotate action for mercurial:
72
* hg annotate - show changeset information per file line
76
public class AnnotateAction extends AbstractAction {
78
private final VCSContext context;
80
public AnnotateAction(String name, VCSContext context) {
81
this.context = context;
82
putValue(Action.NAME, name);
85
public boolean isEnabled() {
86
File repository = HgUtils.getRootFile(context);
87
if (repository == null) return false;
89
Node [] nodes = context.getElements().lookupAll(Node.class).toArray(new Node[0]);
90
if (context.getRootFiles().size() > 0 && activatedEditorCookie(nodes) != null) {
91
FileStatusCache cache = Mercurial.getInstance().getFileStatusCache();
92
File file = activatedFile(nodes);
93
int status = cache.getStatus(file).getStatus();
94
if (status == FileInformation.STATUS_NOTVERSIONED_NEWLOCALLY ||
95
status == FileInformation.STATUS_NOTVERSIONED_EXCLUDED) {
105
public void actionPerformed(ActionEvent e) {
106
Node [] nodes = context.getElements().lookupAll(Node.class).toArray(new Node[0]);
107
if (visible(nodes)) {
108
JEditorPane pane = activatedEditorPane(nodes);
109
AnnotationBarManager.hideAnnotationBar(pane);
111
EditorCookie ec = activatedEditorCookie(nodes);
112
if (ec == null) return;
114
final File file = activatedFile(nodes);
116
JEditorPane[] panes = ec.getOpenedPanes();
121
panes = ec.getOpenedPanes();
125
final JEditorPane currentPane = panes[0];
126
final TopComponent tc = (TopComponent) SwingUtilities.getAncestorOfClass(TopComponent.class, currentPane);
129
final AnnotationBar ab = AnnotationBarManager.showAnnotationBar(currentPane);
130
ab.setAnnotationMessage(NbBundle.getMessage(AnnotateAction.class, "CTL_AnnotationSubstitute")); // NOI18N;
132
final File repository = HgUtils.getRootFile(context);
133
if (repository == null) return;
135
RequestProcessor rp = Mercurial.getInstance().getRequestProcessor(repository);
136
HgProgressSupport support = new HgProgressSupport() {
137
public void perform() {
138
HgUtils.outputMercurialTabInRed(
139
NbBundle.getMessage(AnnotateAction.class,
140
"MSG_ANNOTATE_TITLE")); // NOI18N
141
HgUtils.outputMercurialTabInRed(
142
NbBundle.getMessage(AnnotateAction.class,
143
"MSG_ANNOTATE_TITLE_SEP")); // NOI18N
144
computeAnnotations(repository, file, this, ab);
145
HgUtils.outputMercurialTab("\t" + file.getAbsolutePath()); // NOI18N
146
HgUtils.outputMercurialTabInRed(
147
NbBundle.getMessage(AnnotateAction.class,
148
"MSG_ANNOTATE_DONE")); // NOI18N
151
support.start(rp, repository.getAbsolutePath(), NbBundle.getMessage(AnnotateAction.class, "MSG_Annotation_Progress")); // NOI18N
155
private void computeAnnotations(File repository, File file, HgProgressSupport progress, AnnotationBar ab) {
156
List<String> list = null;
158
list = HgCommand.doAnnotate(repository, file);
159
} catch (HgException ex) {
160
NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
161
DialogDisplayer.getDefault().notifyLater(e);
163
if (progress.isCanceled()) {
164
ab.setAnnotationMessage(NbBundle.getMessage(AnnotateAction.class, "CTL_AnnotationFailed")); // NOI18N;
167
if (list == null) return;
168
AnnotateLine [] lines = toAnnotateLines(list);
169
ab.annotationLines(file, Arrays.asList(lines));
171
list = HgCommand.doLogShort(repository, file);
172
} catch (HgException ex) {
173
NotifyDescriptor.Exception e = new NotifyDescriptor.Exception(ex);
174
DialogDisplayer.getDefault().notifyLater(e);
176
if (progress.isCanceled()) {
179
HgLogMessage [] logs = toHgLogMessages(list);
180
if (logs == null) return;
181
fillCommitMessages(lines, logs);
185
private static void fillCommitMessages(AnnotateLine [] annotations, HgLogMessage [] logs) {
186
long lowestRevisionNumber = Long.MAX_VALUE;
187
for (int i = 0; i < annotations.length; i++) {
188
AnnotateLine annotation = annotations[i];
189
for (int j = 0; j < logs.length; j++) {
190
HgLogMessage log = logs[j];
191
if (log.getRevision() < lowestRevisionNumber) {
192
lowestRevisionNumber = log.getRevision();
194
if (annotation.getRevision().equals(log.getRevision().toString()
196
annotation.setDate(log.getDate());
197
annotation.setCommitMessage(log.getCommitMessage());
201
String lowestRev = Long.toString(lowestRevisionNumber);
202
for (int i = 0; i < annotations.length; i++) {
203
AnnotateLine annotation = annotations[i];
204
annotation.setCanBeRolledBack(!annotation.getRevision().equals(lowestRev));
208
private static HgLogMessage [] toHgLogMessages(List<String> lines) {
209
List <HgLogMessage> logs = new ArrayList<HgLogMessage>();
210
HgLogMessage log = null;
213
for (Iterator j = lines.iterator(); j.hasNext();) {
214
String line = (String) j.next();
216
log = new HgLogMessage();
218
log.setRevision(Long.parseLong(line));
219
} catch (java.lang.Exception e) {
220
Mercurial.LOG.log(Level.SEVERE, "Caught Exception while parsing revision", e); // NOI18N
222
} else if (i % 4 == 1) {
223
log.setCommitMessage(line);
224
} else if (i % 4 == 2) {
225
String splits[] = line.split(" ", 2); // NOI18N
227
Date date = new Date(Long.parseLong(splits[0]) * 1000);
228
log.setDate(new Date(Long.parseLong(splits[0].trim()) * 1000));
229
} catch (java.lang.Exception e) {
230
Mercurial.LOG.log(Level.SEVERE, "Caught Exception while parsing date", e); // NOI18N
232
log.setTimeZoneOffset(splits[1]);
233
} else if (i % 4 == 3) {
234
log.setChangeSet(line);
239
return logs.toArray(new HgLogMessage[logs.size()]);
242
private static AnnotateLine [] toAnnotateLines(List<String> annotations)
244
AnnotateLine [] lines = new AnnotateLine[annotations.size()];
245
int n = annotations.size();
247
for (String line : annotations) {
248
int endAuthor = line.indexOf(" "); // NOI18N
249
int endFile = line.indexOf(":", endAuthor + 1); // NOI18N
250
int endRevision = line.indexOf(" ", endAuthor + 1); // NOI18N
251
lines[i] = new AnnotateLine();
252
lines[i].setAuthor(line.substring(0, endAuthor));
253
lines[i].setContent(line.substring(endFile + 2));
254
lines[i].setFileName(line.substring(endRevision + 1, endFile).trim());
255
lines[i].setLineNum(i + 1);
256
lines[i].setRevision(line.substring(endAuthor, endRevision).trim());
263
* @param nodes or null (then taken from windowsystem, it may be wrong on editor tabs #66700).
265
public boolean visible(Node[] nodes) {
266
JEditorPane currentPane = activatedEditorPane(nodes);
267
return AnnotationBarManager.annotationBarVisible(currentPane);
272
* @return active editor pane or null if selected node
273
* does not have any or more nodes selected.
275
private JEditorPane activatedEditorPane(Node[] nodes) {
276
EditorCookie ec = activatedEditorCookie(nodes);
278
JEditorPane[] panes = ec.getOpenedPanes();
279
if (panes != null && panes.length > 0) {
286
private EditorCookie activatedEditorCookie(Node[] nodes) {
288
nodes = WindowManager.getDefault().getRegistry().getActivatedNodes();
290
if (nodes.length == 1) {
291
Node node = nodes[0];
292
return (EditorCookie) node.getCookie(EditorCookie.class);
297
private File activatedFile(Node[] nodes) {
298
if (nodes.length == 1) {
299
Node node = nodes[0];
300
DataObject dobj = (DataObject) node.getCookie(DataObject.class);
302
FileObject fo = dobj.getPrimaryFile();
303
return FileUtil.toFile(fo);