Source Code Cross Referenced for AnnotationBar.java in  » IDE-Netbeans » subversion » org » netbeans » modules » subversion » ui » blame » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » subversion » org.netbeans.modules.subversion.ui.blame 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:
0042:        package org.netbeans.modules.subversion.ui.blame;
0043:
0044:        import org.netbeans.editor.*;
0045:        import org.netbeans.editor.Utilities;
0046:        import org.netbeans.api.editor.fold.*;
0047:        import org.netbeans.api.diff.*;
0048:        import org.netbeans.spi.diff.*;
0049:        import org.netbeans.modules.subversion.ui.update.RevertModifications;
0050:        import org.netbeans.modules.subversion.ui.update.RevertModificationsAction;
0051:        import org.netbeans.modules.subversion.ui.diff.DiffAction;
0052:        import org.netbeans.modules.subversion.RepositoryFile;
0053:        import org.netbeans.modules.subversion.Subversion;
0054:        import org.netbeans.modules.subversion.client.SvnProgressSupport;
0055:        import org.netbeans.modules.subversion.util.SvnUtils;
0056:        import org.netbeans.modules.subversion.util.Context;
0057:        import org.netbeans.modules.versioning.util.Utils;
0058:        import org.openide.*;
0059:        import org.openide.loaders.*;
0060:        import org.openide.filesystems.*;
0061:        import org.openide.text.*;
0062:        import org.openide.util.*;
0063:        import org.openide.xml.*;
0064:        import org.tigris.subversion.svnclientadapter.SVNUrl;
0065:        import org.tigris.subversion.svnclientadapter.SVNRevision;
0066:        import javax.swing.*;
0067:        import javax.swing.Timer;
0068:        import javax.swing.event.*;
0069:        import javax.swing.text.*;
0070:        import javax.accessibility.Accessible;
0071:        import java.awt.*;
0072:        import java.awt.event.*;
0073:        import java.beans.*;
0074:        import java.util.*;
0075:        import java.util.List;
0076:        import java.io.*;
0077:        import java.text.DateFormat;
0078:        import java.text.MessageFormat;
0079:        import java.util.logging.Level;
0080:        import org.netbeans.modules.subversion.client.SvnClientExceptionHandler;
0081:        import org.tigris.subversion.svnclientadapter.ISVNNotifyListener;
0082:        import org.tigris.subversion.svnclientadapter.SVNClientException;
0083:
0084:        /**
0085:         * Represents annotation sidebar componnet in editor. It's
0086:         * created by {@link AnnotationBarManager}.
0087:         *
0088:         * <p>It reponds to following external signals:
0089:         * <ul>
0090:         *   <li> {@link #annotate} message
0091:         * </ul>
0092:         *
0093:         * @author Petr Kuzel
0094:         */
0095:        final class AnnotationBar extends JComponent implements  Accessible,
0096:                PropertyChangeListener, DocumentListener, ChangeListener,
0097:                ActionListener, Runnable, ComponentListener {
0098:
0099:            /**
0100:             * Target text component for which the annotation bar is aiming.
0101:             */
0102:            private final JTextComponent textComponent;
0103:
0104:            /**
0105:             * User interface related to the target text component.
0106:             */
0107:            private final EditorUI editorUI;
0108:
0109:            /**
0110:             * Fold hierarchy of the text component user interface.
0111:             */
0112:            private final FoldHierarchy foldHierarchy;
0113:
0114:            /** 
0115:             * Document related to the target text component.
0116:             */
0117:            private final BaseDocument doc;
0118:
0119:            /**
0120:             * Caret of the target text component.
0121:             */
0122:            private final Caret caret;
0123:
0124:            /**
0125:             * Caret batch timer launched on receiving
0126:             * annotation data structures (AnnotateLine).
0127:             */
0128:            private Timer caretTimer;
0129:
0130:            /**
0131:             * Controls annotation bar visibility.
0132:             */
0133:            private boolean annotated;
0134:
0135:            /**
0136:             * Maps document {@link javax.swing.text.Element}s (representing lines) to
0137:             * {@link AnnotateLine}. <code>null</code> means that
0138:             * no data are available, yet. So alternative
0139:             * {@link #elementAnnotationsSubstitute} text shoudl be used.
0140:             *
0141:             * @thread it is accesed from multiple threads all mutations
0142:             * and iterations must be under elementAnnotations lock,
0143:             */
0144:            private Map<Element, AnnotateLine> elementAnnotations;
0145:
0146:            /**
0147:             * Represents text that should be displayed in
0148:             * visible bar with yet <code>null</code> elementAnnotations.
0149:             */
0150:            private String elementAnnotationsSubstitute;
0151:
0152:            private Color backgroundColor = Color.WHITE;
0153:            private Color foregroundColor = Color.BLACK;
0154:            private Color selectedColor = Color.BLUE;
0155:
0156:            /**
0157:             * Most recent status message.
0158:             */
0159:            private String recentStatusMessage;
0160:
0161:            /**
0162:             * Revision associated with caret line.
0163:             */
0164:            private String recentRevision;
0165:
0166:            /**
0167:             * Request processor to create threads that may be cancelled.
0168:             */
0169:            RequestProcessor requestProcessor = null;
0170:
0171:            /**
0172:             * Latest annotation comment fetching task launched.
0173:             */
0174:            private RequestProcessor.Task latestAnnotationTask = null;
0175:
0176:            /**
0177:             * Holds false if Rollback Changes action is NOT valid for current revision, true otherwise. 
0178:             */
0179:            private boolean recentRevisionCanBeRolledBack;
0180:
0181:            /**
0182:             * Rendering hints for annotations sidebar inherited from editor settings.
0183:             */
0184:            private final Map renderingHints;
0185:
0186:            /**
0187:             * Creates new instance initializing final fields.
0188:             */
0189:            public AnnotationBar(JTextComponent target) {
0190:                this .textComponent = target;
0191:                this .editorUI = Utilities.getEditorUI(target);
0192:                this .foldHierarchy = FoldHierarchy.get(editorUI.getComponent());
0193:                this .doc = editorUI.getDocument();
0194:                this .caret = textComponent.getCaret();
0195:                if (textComponent instanceof  JEditorPane) {
0196:                    JEditorPane jep = (JEditorPane) textComponent;
0197:                    Class kitClass = jep.getEditorKit().getClass();
0198:                    Object userSetHints = Settings.getValue(kitClass,
0199:                            SettingsNames.RENDERING_HINTS);
0200:                    renderingHints = (userSetHints instanceof  Map && ((Map) userSetHints)
0201:                            .size() > 0) ? (Map) userSetHints : null;
0202:                } else {
0203:                    renderingHints = null;
0204:                }
0205:            }
0206:
0207:            // public contract ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0208:
0209:            /**
0210:             * Makes the bar visible and sensitive to
0211:             * LogOutoutListener events that should deliver
0212:             * actual content to be displayed.
0213:             */
0214:            public void annotate() {
0215:                annotated = true;
0216:                elementAnnotations = null;
0217:
0218:                doc.addDocumentListener(this );
0219:                textComponent.addComponentListener(this );
0220:                editorUI.addPropertyChangeListener(this );
0221:
0222:                revalidate(); // resize the component
0223:            }
0224:
0225:            public void setAnnotationMessage(String message) {
0226:                elementAnnotationsSubstitute = message;
0227:                revalidate();
0228:            }
0229:
0230:            /**
0231:             * Result computed show it...
0232:             * Takes AnnotateLines and shows them.
0233:             */
0234:            public void annotationLines(File file,
0235:                    List<AnnotateLine> annotateLines) {
0236:                List<AnnotateLine> lines = new LinkedList<AnnotateLine>(
0237:                        annotateLines);
0238:                int lineCount = lines.size();
0239:                /** 0 based line numbers => 1 based line numbers*/
0240:                int ann2editorPermutation[] = new int[lineCount];
0241:                for (int i = 0; i < lineCount; i++) {
0242:                    ann2editorPermutation[i] = i + 1;
0243:                }
0244:
0245:                DiffProvider diff = (DiffProvider) Lookup.getDefault().lookup(
0246:                        DiffProvider.class);
0247:                if (diff != null) {
0248:                    Reader r = new LinesReader(lines);
0249:                    Reader docReader = Utils.getDocumentReader(doc);
0250:                    try {
0251:
0252:                        Difference[] differences = diff.computeDiff(r,
0253:                                docReader);
0254:
0255:                        // customize annotation line numbers to match different reality
0256:                        // compule line permutation
0257:
0258:                        for (int i = 0; i < differences.length; i++) {
0259:                            Difference d = differences[i];
0260:                            if (d.getType() == Difference.ADD)
0261:                                continue;
0262:
0263:                            int editorStart;
0264:                            int firstShift = d.getFirstEnd()
0265:                                    - d.getFirstStart() + 1;
0266:                            if (d.getType() == Difference.CHANGE) {
0267:                                int firstLen = d.getFirstEnd()
0268:                                        - d.getFirstStart();
0269:                                int secondLen = d.getSecondEnd()
0270:                                        - d.getSecondStart();
0271:                                if (secondLen >= firstLen)
0272:                                    continue; // ADD or pure CHANGE
0273:                                editorStart = d.getSecondStart();
0274:                                firstShift = firstLen - secondLen;
0275:                            } else { // DELETE
0276:                                editorStart = d.getSecondStart() + 1;
0277:                            }
0278:
0279:                            for (int c = editorStart + firstShift - 1; c < lineCount; c++) {
0280:                                ann2editorPermutation[c] -= firstShift;
0281:                            }
0282:                        }
0283:
0284:                        for (int i = differences.length - 1; i >= 0; i--) {
0285:                            Difference d = differences[i];
0286:                            if (d.getType() == Difference.DELETE)
0287:                                continue;
0288:
0289:                            int firstStart;
0290:                            int firstShift = d.getSecondEnd()
0291:                                    - d.getSecondStart() + 1;
0292:                            if (d.getType() == Difference.CHANGE) {
0293:                                int firstLen = d.getFirstEnd()
0294:                                        - d.getFirstStart();
0295:                                int secondLen = d.getSecondEnd()
0296:                                        - d.getSecondStart();
0297:                                if (secondLen <= firstLen)
0298:                                    continue; // REMOVE or pure CHANGE
0299:                                firstShift = secondLen - firstLen;
0300:                                firstStart = d.getFirstStart();
0301:                            } else {
0302:                                firstStart = d.getFirstStart() + 1;
0303:                            }
0304:
0305:                            for (int k = firstStart - 1; k < lineCount; k++) {
0306:                                ann2editorPermutation[k] += firstShift;
0307:                            }
0308:                        }
0309:
0310:                    } catch (IOException e) {
0311:                        Subversion.LOG
0312:                                .log(
0313:                                        Level.INFO,
0314:                                        "Cannot compute local diff required for annotations, ignoring...",
0315:                                        e);
0316:                    }
0317:                }
0318:
0319:                try {
0320:                    doc.atomicLock();
0321:                    StyledDocument sd = (StyledDocument) doc;
0322:                    Iterator<AnnotateLine> it = lines.iterator();
0323:                    elementAnnotations = Collections
0324:                            .synchronizedMap(new HashMap<Element, AnnotateLine>(
0325:                                    lines.size()));
0326:                    while (it.hasNext()) {
0327:                        AnnotateLine line = it.next();
0328:                        int lineNum = ann2editorPermutation[line.getLineNum() - 1];
0329:                        try {
0330:                            int lineOffset = NbDocument.findLineOffset(sd,
0331:                                    lineNum - 1);
0332:                            Element element = sd
0333:                                    .getParagraphElement(lineOffset);
0334:                            elementAnnotations.put(element, line);
0335:                        } catch (IndexOutOfBoundsException ex) {
0336:                            // TODO how could I get line behind document end?
0337:                            // furtunately user does not spot it
0338:                            Subversion.LOG.log(Level.INFO, null, ex);
0339:                        }
0340:                    }
0341:                } finally {
0342:                    doc.atomicUnlock();
0343:                }
0344:
0345:                // lazy listener registration
0346:                caret.addChangeListener(this );
0347:                this .caretTimer = new Timer(500, this );
0348:                caretTimer.setRepeats(false);
0349:
0350:                onCurrentLine();
0351:                revalidate();
0352:                repaint();
0353:            }
0354:
0355:            private ISVNNotifyListener svnClientListener;
0356:
0357:            void setSVNClienListener(ISVNNotifyListener svnClientListener) {
0358:                this .svnClientListener = svnClientListener;
0359:
0360:                File file = getCurrentFile();
0361:                Subversion.getInstance()
0362:                        .addSVNNotifyListener(svnClientListener);
0363:            }
0364:
0365:            // implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0366:
0367:            /**
0368:             * Gets a the file related to the document
0369:             *
0370:             * @return the file related to the document, <code>null</code> if none
0371:             * exists.
0372:             */
0373:            private File getCurrentFile() {
0374:                File result = null;
0375:
0376:                DataObject dobj = (DataObject) doc
0377:                        .getProperty(Document.StreamDescriptionProperty);
0378:                if (dobj != null) {
0379:                    FileObject fo = dobj.getPrimaryFile();
0380:                    result = FileUtil.toFile(fo);
0381:                }
0382:
0383:                return result;
0384:            }
0385:
0386:            /**
0387:             * Registers "close" popup menu, tooltip manager
0388:             * and repaint on documet change manager.
0389:             */
0390:            public void addNotify() {
0391:                super .addNotify();
0392:
0393:                this .addMouseListener(new MouseAdapter() {
0394:                    public void mousePressed(MouseEvent e) {
0395:                        maybeShowPopup(e);
0396:                    }
0397:
0398:                    public void mouseReleased(MouseEvent e) {
0399:                        maybeShowPopup(e);
0400:                    }
0401:
0402:                    private void maybeShowPopup(MouseEvent e) {
0403:                        if (e.isPopupTrigger()) {
0404:                            e.consume();
0405:                            createPopup().show(e.getComponent(), e.getX(),
0406:                                    e.getY());
0407:                        }
0408:                    }
0409:                });
0410:
0411:                // register with tooltip manager
0412:                setToolTipText(""); // NOI18N
0413:
0414:            }
0415:
0416:            private JPopupMenu createPopup() {
0417:                final ResourceBundle loc = NbBundle
0418:                        .getBundle(AnnotationBar.class);
0419:                final JPopupMenu popupMenu = new JPopupMenu();
0420:
0421:                final File file = getCurrentFile();
0422:
0423:                final JMenuItem diffMenu = new JMenuItem(loc
0424:                        .getString("CTL_MenuItem_DiffToRevision"));
0425:                diffMenu.addActionListener(new ActionListener() {
0426:                    public void actionPerformed(ActionEvent e) {
0427:                        if (recentRevision != null) {
0428:                            if (getPreviousRevision(recentRevision) != null) {
0429:                                DiffAction.diff(file,
0430:                                        getPreviousRevision(recentRevision),
0431:                                        recentRevision);
0432:                            }
0433:                        }
0434:                    }
0435:                });
0436:                popupMenu.add(diffMenu);
0437:
0438:                JMenuItem rollbackMenu = new JMenuItem(loc
0439:                        .getString("CTL_MenuItem_Revert"));
0440:                rollbackMenu.addActionListener(new ActionListener() {
0441:                    public void actionPerformed(ActionEvent e) {
0442:                        revert(file, recentRevision);
0443:                    }
0444:                });
0445:                popupMenu.add(rollbackMenu);
0446:                rollbackMenu.setEnabled(recentRevisionCanBeRolledBack);
0447:
0448:                JMenuItem menu;
0449:                menu = new JMenuItem(loc
0450:                        .getString("CTL_MenuItem_CloseAnnotations"));
0451:                menu.addActionListener(new ActionListener() {
0452:                    public void actionPerformed(ActionEvent e) {
0453:                        hideBar();
0454:                    }
0455:                });
0456:                popupMenu.addSeparator();
0457:                popupMenu.add(menu);
0458:
0459:                diffMenu.setVisible(false);
0460:                rollbackMenu.setVisible(false);
0461:                if (recentRevision != null) {
0462:                    if (getPreviousRevision(recentRevision) != null) {
0463:                        String format = loc
0464:                                .getString("CTL_MenuItem_DiffToRevision");
0465:                        diffMenu.setText(MessageFormat.format(format,
0466:                                new Object[] { recentRevision,
0467:                                        getPreviousRevision(recentRevision) }));
0468:                        diffMenu.setVisible(true);
0469:                    }
0470:                    rollbackMenu.setVisible(true);
0471:                }
0472:
0473:                return popupMenu;
0474:            }
0475:
0476:            private void revert(File file, String revision) {
0477:                final Context ctx = new Context(file);
0478:
0479:                final SVNUrl url;
0480:                try {
0481:                    url = SvnUtils.getRepositoryRootUrl(file);
0482:                } catch (SVNClientException ex) {
0483:                    SvnClientExceptionHandler.notifyException(ex, true, true);
0484:                    return;
0485:                }
0486:                final RepositoryFile repositoryFile = new RepositoryFile(url,
0487:                        url, SVNRevision.HEAD);
0488:
0489:                final RevertModifications revertModifications = new RevertModifications(
0490:                        repositoryFile, revision);
0491:                if (!revertModifications.showDialog()) {
0492:                    return;
0493:                }
0494:
0495:                RequestProcessor rp = Subversion.getInstance()
0496:                        .getRequestProcessor(url);
0497:                SvnProgressSupport support = new SvnProgressSupport() {
0498:                    public void perform() {
0499:                        RevertModificationsAction
0500:                                .performRevert(revertModifications
0501:                                        .getRevisionInterval(),
0502:                                        revertModifications.revertNewFiles(),
0503:                                        ctx, this );
0504:                    }
0505:                };
0506:                support.start(rp, url, NbBundle.getMessage(AnnotationBar.class,
0507:                        "MSG_Revert_Progress")); // NOI18N
0508:            }
0509:
0510:            private String getPreviousRevision(String revision) {
0511:                return Long.toString(Long.parseLong(revision) - 1);
0512:            }
0513:
0514:            /**
0515:             * Hides the annotation bar from user. 
0516:             */
0517:            void hideBar() {
0518:                annotated = false;
0519:                revalidate();
0520:                release();
0521:            }
0522:
0523:            /**
0524:             * Gets a request processor which is able to cancel tasks.
0525:             */
0526:            private RequestProcessor getRequestProcessor() {
0527:                if (requestProcessor == null) {
0528:                    requestProcessor = new RequestProcessor("AnnotationBarRP",
0529:                            1, true); // NOI18N
0530:                }
0531:
0532:                return requestProcessor;
0533:            }
0534:
0535:            /**
0536:             * Shows commit message in status bar and or revision change repaints side
0537:             * bar (to highlight same revision). This process is started in a
0538:             * seperate thread.
0539:             */
0540:            private void onCurrentLine() {
0541:                if (latestAnnotationTask != null) {
0542:                    latestAnnotationTask.cancel();
0543:                }
0544:
0545:                latestAnnotationTask = getRequestProcessor().post(this );
0546:            }
0547:
0548:            // latestAnnotationTask business logic
0549:            public void run() {
0550:                // get resource bundle
0551:                ResourceBundle loc = NbBundle.getBundle(AnnotationBar.class);
0552:                // give status bar "wait" indication
0553:                StatusBar statusBar = editorUI.getStatusBar();
0554:                recentStatusMessage = loc
0555:                        .getString("CTL_StatusBar_WaitFetchAnnotation");
0556:                statusBar.setText(StatusBar.CELL_MAIN, recentStatusMessage);
0557:
0558:                recentRevisionCanBeRolledBack = false;
0559:                // determine current line
0560:                int line = -1;
0561:                int offset = caret.getDot();
0562:                try {
0563:                    line = Utilities.getLineOffset(doc, offset);
0564:                } catch (BadLocationException ex) {
0565:                    Subversion.LOG.log(Level.SEVERE,
0566:                            "Can not get line for caret at offset " + offset,
0567:                            ex); // NOI18N
0568:                    clearRecentFeedback();
0569:                    return;
0570:                }
0571:
0572:                // handle locally modified lines
0573:                AnnotateLine al = getAnnotateLine(line);
0574:                if (al == null) {
0575:                    AnnotationMarkProvider amp = AnnotationMarkInstaller
0576:                            .getMarkProvider(textComponent);
0577:                    if (amp != null) {
0578:                        amp.setMarks(Collections.<AnnotationMark> emptyList());
0579:                    }
0580:                    clearRecentFeedback();
0581:                    if (recentRevision != null) {
0582:                        recentRevision = null;
0583:                        repaint();
0584:                    }
0585:                    return;
0586:                }
0587:
0588:                recentRevisionCanBeRolledBack = al.canBeRolledBack();
0589:
0590:                // handle unchanged lines
0591:                String revision = al.getRevision();
0592:                if (revision.equals(recentRevision) == false) {
0593:                    recentRevision = revision;
0594:                    repaint();
0595:
0596:                    AnnotationMarkProvider amp = AnnotationMarkInstaller
0597:                            .getMarkProvider(textComponent);
0598:                    if (amp != null) {
0599:
0600:                        List<AnnotationMark> marks = new ArrayList<AnnotationMark>(
0601:                                elementAnnotations.size());
0602:                        // I cannot affort to lock elementAnnotations for long time
0603:                        // it's accessed from editor thread too
0604:                        Iterator<Map.Entry<Element, AnnotateLine>> it2;
0605:                        synchronized (elementAnnotations) {
0606:                            it2 = new HashSet<Map.Entry<Element, AnnotateLine>>(
0607:                                    elementAnnotations.entrySet()).iterator();
0608:                        }
0609:                        while (it2.hasNext()) {
0610:                            Map.Entry<Element, AnnotateLine> next = it2.next();
0611:                            AnnotateLine annotateLine = next.getValue();
0612:                            if (revision.equals(annotateLine.getRevision())) {
0613:                                Element element = next.getKey();
0614:                                if (elementAnnotations.containsKey(element) == false) {
0615:                                    continue;
0616:                                }
0617:                                int elementOffset = element.getStartOffset();
0618:                                int lineNumber = NbDocument.findLineNumber(
0619:                                        (StyledDocument) doc, elementOffset);
0620:                                AnnotationMark mark = new AnnotationMark(
0621:                                        lineNumber, revision);
0622:                                marks.add(mark);
0623:                            }
0624:
0625:                            if (Thread.interrupted()) {
0626:                                clearRecentFeedback();
0627:                                return;
0628:                            }
0629:                        }
0630:                        amp.setMarks(marks);
0631:                    }
0632:                }
0633:
0634:                if (al.getCommitMessage() != null) {
0635:                    recentStatusMessage = al.getCommitMessage();
0636:                    statusBar.setText(StatusBar.CELL_MAIN, al.getAuthor()
0637:                            + ": " + recentStatusMessage); // NOI18N
0638:                } else {
0639:                    clearRecentFeedback();
0640:                }
0641:                ;
0642:            }
0643:
0644:            /**
0645:             * Clears the status bar if it contains the latest status message
0646:             * displayed by this annotation bar.
0647:             */
0648:            private void clearRecentFeedback() {
0649:                StatusBar statusBar = editorUI.getStatusBar();
0650:                if (statusBar.getText(StatusBar.CELL_MAIN) == recentStatusMessage) {
0651:                    statusBar.setText(StatusBar.CELL_MAIN, ""); // NOI18N
0652:                }
0653:            }
0654:
0655:            /**
0656:             * Components created by SibeBarFactory are positioned
0657:             * using a Layout manager that determines componnet size
0658:             * by retireving preferred size.
0659:             *
0660:             * <p>Once componnet needs resizing it simply calls
0661:             * {@link #revalidate} that triggers new layouting
0662:             * that consults prefered size.
0663:             */
0664:            public Dimension getPreferredSize() {
0665:                Dimension dim = textComponent.getSize();
0666:                int width = annotated ? getBarWidth() : 0;
0667:                dim.width = width;
0668:                dim.height *= 2; // XXX
0669:                return dim;
0670:            }
0671:
0672:            /**
0673:             * Gets the maximum size of this component.
0674:             *
0675:             * @return the maximum size of this component
0676:             */
0677:            public Dimension getMaximumSize() {
0678:                return getPreferredSize();
0679:            }
0680:
0681:            /**
0682:             * Gets the preferred width of this component.
0683:             *
0684:             * @return the preferred width of this component
0685:             */
0686:            private int getBarWidth() {
0687:                String longestString = ""; // NOI18N
0688:                if (elementAnnotations == null) {
0689:                    longestString = elementAnnotationsSubstitute;
0690:                } else {
0691:                    synchronized (elementAnnotations) {
0692:                        Iterator<AnnotateLine> it = elementAnnotations.values()
0693:                                .iterator();
0694:                        while (it.hasNext()) {
0695:                            AnnotateLine line = it.next();
0696:                            String displayName = getDisplayName(line); // NOI18N
0697:                            if (displayName.length() > longestString.length()) {
0698:                                longestString = displayName;
0699:                            }
0700:                        }
0701:                    }
0702:                }
0703:                char[] data = longestString.toCharArray();
0704:                Graphics g = getGraphics();
0705:                if (g != null) {
0706:                    int w = g.getFontMetrics().charsWidth(data, 0, data.length);
0707:                    return w + 4;
0708:                } else {
0709:                    return 0;
0710:                }
0711:
0712:            }
0713:
0714:            private String getDisplayName(AnnotateLine line) {
0715:                return line.getRevision() + "  " + line.getAuthor(); // NOI18N
0716:            }
0717:
0718:            /**
0719:             * Pair method to {@link #annotate}. It releases
0720:             * all resources.
0721:             */
0722:            private void release() {
0723:                editorUI.removePropertyChangeListener(this );
0724:                textComponent.removeComponentListener(this );
0725:                doc.removeDocumentListener(this );
0726:                caret.removeChangeListener(this );
0727:                if (caretTimer != null) {
0728:                    caretTimer.removeActionListener(this );
0729:                }
0730:                elementAnnotations = null;
0731:                // cancel running annotation task if active
0732:                if (latestAnnotationTask != null) {
0733:                    latestAnnotationTask.cancel();
0734:                }
0735:                AnnotationMarkProvider amp = AnnotationMarkInstaller
0736:                        .getMarkProvider(textComponent);
0737:                if (amp != null) {
0738:                    amp.setMarks(Collections.<AnnotationMark> emptyList());
0739:                }
0740:
0741:                Subversion.getInstance().removeSVNNotifyListener(
0742:                        svnClientListener);
0743:
0744:                clearRecentFeedback();
0745:
0746:            }
0747:
0748:            /**
0749:             * Paints one view that corresponds to a line (or
0750:             * multiple lines if folding takes effect).
0751:             */
0752:            private void paintView(View view, Graphics g, int yBase) {
0753:                JTextComponent component = editorUI.getComponent();
0754:                if (component == null)
0755:                    return;
0756:                BaseTextUI textUI = (BaseTextUI) component.getUI();
0757:
0758:                Element rootElem = textUI.getRootView(component).getElement();
0759:                int line = rootElem.getElementIndex(view.getStartOffset());
0760:
0761:                String annotation = ""; // NOI18N
0762:                AnnotateLine al = null;
0763:                if (elementAnnotations != null) {
0764:                    al = getAnnotateLine(line);
0765:                    if (al != null) {
0766:                        annotation = getDisplayName(al); // NOI18N
0767:                    }
0768:                } else {
0769:                    annotation = elementAnnotationsSubstitute;
0770:                }
0771:
0772:                if (al != null && al.getRevision().equals(recentRevision)) {
0773:                    g.setColor(selectedColor());
0774:                } else {
0775:                    g.setColor(foregroundColor());
0776:                }
0777:                g.drawString(annotation, 2, yBase + editorUI.getLineAscent());
0778:            }
0779:
0780:            /**
0781:             * Presents commit message as tooltips.
0782:             */
0783:            public String getToolTipText(MouseEvent e) {
0784:                if (editorUI == null)
0785:                    return null;
0786:                int line = getLineFromMouseEvent(e);
0787:
0788:                StringBuffer annotation = new StringBuffer();
0789:                if (elementAnnotations != null) {
0790:                    AnnotateLine al = getAnnotateLine(line);
0791:
0792:                    if (al != null) {
0793:                        String escapedAuthor = NbBundle.getMessage(
0794:                                AnnotationBar.class, "TT_Annotation"); // NOI18N
0795:                        try {
0796:                            escapedAuthor = XMLUtil.toElementContent(al
0797:                                    .getAuthor());
0798:                        } catch (CharConversionException e1) {
0799:                            Subversion.LOG.log(Level.INFO,
0800:                                    " can not HTML escape: " + al.getAuthor(),
0801:                                    e1);
0802:                        }
0803:
0804:                        // always return unique string to avoid tooltip sharing on mouse move over same revisions -->
0805:                        annotation.append("<html><!-- line=" + line++ + " -->"
0806:                                + al.getRevision() + " - <b>" + escapedAuthor
0807:                                + "</b>"); // NOI18N
0808:                        if (al.getDate() != null) {
0809:                            annotation.append(" "
0810:                                    + DateFormat.getDateInstance().format(
0811:                                            al.getDate())); // NOI18N                    
0812:                        }
0813:                        if (al.getCommitMessage() != null) {
0814:                            String escaped = null;
0815:                            try {
0816:                                escaped = XMLUtil.toElementContent(al
0817:                                        .getCommitMessage());
0818:                            } catch (CharConversionException e1) {
0819:                                Subversion.LOG.log(Level.INFO,
0820:                                        " can not HTML escape: "
0821:                                                + al.getCommitMessage(), e1); // NOI18N
0822:                            }
0823:                            if (escaped != null) {
0824:                                String lined = escaped.replaceAll(System
0825:                                        .getProperty("line.separator"), "<br>"); // NOI18N
0826:                                annotation.append("<p>" + lined); // NOI18N
0827:                            }
0828:                        }
0829:                    }
0830:                } else {
0831:                    annotation.append(elementAnnotationsSubstitute);
0832:                }
0833:
0834:                return annotation.toString();
0835:            }
0836:
0837:            /**
0838:             * Locates AnnotateLine associated with given line. The
0839:             * line is translated to Element that is used as map lookup key.
0840:             * The map is initially filled up with Elements sampled on
0841:             * annotate() method.
0842:             *
0843:             * <p>Key trick is that Element's identity is maintained
0844:             * until line removal (and is restored on undo).
0845:             *
0846:             * @param line
0847:             * @return found AnnotateLine or <code>null</code>
0848:             */
0849:            private AnnotateLine getAnnotateLine(int line) {
0850:                StyledDocument sd = (StyledDocument) doc;
0851:                int lineOffset = NbDocument.findLineOffset(sd, line);
0852:                Element element = sd.getParagraphElement(lineOffset);
0853:                AnnotateLine al = elementAnnotations.get(element);
0854:
0855:                if (al != null) {
0856:                    int startOffset = element.getStartOffset();
0857:                    int endOffset = element.getEndOffset();
0858:                    try {
0859:                        int len = endOffset - startOffset;
0860:                        String text = doc.getText(startOffset, len - 1);
0861:                        String content = al.getContent();
0862:                        if (text.equals(content)) {
0863:                            return al;
0864:                        }
0865:                    } catch (BadLocationException e) {
0866:                        Subversion.LOG.log(Level.INFO,
0867:                                " can not locate line annotation.", e); // NOI18N
0868:                    }
0869:                }
0870:
0871:                return null;
0872:            }
0873:
0874:            /**
0875:             * GlyphGutter copy pasted bolerplate method.
0876:             * It invokes {@link #paintView} that contains
0877:             * actual business logic.
0878:             */
0879:            public void paintComponent(Graphics g) {
0880:                super .paintComponent(g);
0881:
0882:                Rectangle clip = g.getClipBounds();
0883:
0884:                JTextComponent component = editorUI.getComponent();
0885:                if (component == null)
0886:                    return;
0887:
0888:                BaseTextUI textUI = (BaseTextUI) component.getUI();
0889:                View rootView = Utilities.getDocumentView(component);
0890:                if (rootView == null)
0891:                    return;
0892:
0893:                g.setColor(backgroundColor());
0894:                g.fillRect(clip.x, clip.y, clip.width, clip.height);
0895:
0896:                if (renderingHints != null) {
0897:                    ((Graphics2D) g).addRenderingHints(renderingHints);
0898:                }
0899:
0900:                AbstractDocument doc = (AbstractDocument) component
0901:                        .getDocument();
0902:                doc.readLock();
0903:                try {
0904:                    foldHierarchy.lock();
0905:                    try {
0906:                        int startPos = textUI.getPosFromY(clip.y);
0907:                        int startViewIndex = rootView.getViewIndex(startPos,
0908:                                Position.Bias.Forward);
0909:                        int rootViewCount = rootView.getViewCount();
0910:
0911:                        if (startViewIndex >= 0
0912:                                && startViewIndex < rootViewCount) {
0913:                            // find the nearest visible line with an annotation
0914:                            Rectangle rec = textUI.modelToView(component,
0915:                                    rootView.getView(startViewIndex)
0916:                                            .getStartOffset());
0917:                            int y = (rec == null) ? 0 : rec.y;
0918:
0919:                            int clipEndY = clip.y + clip.height;
0920:                            for (int i = startViewIndex; i < rootViewCount; i++) {
0921:                                View view = rootView.getView(i);
0922:                                paintView(view, g, y);
0923:                                y += editorUI.getLineHeight();
0924:                                if (y >= clipEndY) {
0925:                                    break;
0926:                                }
0927:                            }
0928:                        }
0929:
0930:                    } finally {
0931:                        foldHierarchy.unlock();
0932:                    }
0933:                } catch (BadLocationException ble) {
0934:                    Subversion.LOG.log(Level.SEVERE, null, ble);
0935:                } finally {
0936:                    doc.readUnlock();
0937:                }
0938:            }
0939:
0940:            private Color backgroundColor() {
0941:                if (textComponent != null) {
0942:                    return textComponent.getBackground();
0943:                }
0944:                return backgroundColor;
0945:            }
0946:
0947:            private Color foregroundColor() {
0948:                if (textComponent != null) {
0949:                    return textComponent.getForeground();
0950:                }
0951:                return foregroundColor;
0952:            }
0953:
0954:            private Color selectedColor() {
0955:                if (backgroundColor == backgroundColor()) {
0956:                    return selectedColor;
0957:                }
0958:                if (textComponent != null) {
0959:                    return textComponent.getForeground();
0960:                }
0961:                return selectedColor;
0962:
0963:            }
0964:
0965:            /** GlyphGutter copy pasted utility method. */
0966:            private int getLineFromMouseEvent(MouseEvent e) {
0967:                int line = -1;
0968:                if (editorUI != null) {
0969:                    try {
0970:                        JTextComponent component = editorUI.getComponent();
0971:                        BaseTextUI textUI = (BaseTextUI) component.getUI();
0972:                        int clickOffset = textUI.viewToModel(component,
0973:                                new Point(0, e.getY()));
0974:                        line = Utilities.getLineOffset(doc, clickOffset);
0975:                    } catch (BadLocationException ble) {
0976:                    }
0977:                }
0978:                return line;
0979:            }
0980:
0981:            /** Implementation */
0982:            public void propertyChange(PropertyChangeEvent evt) {
0983:                if (evt == null)
0984:                    return;
0985:                String id = evt.getPropertyName();
0986:                if (EditorUI.COMPONENT_PROPERTY.equals(id)) { // NOI18N
0987:                    if (evt.getNewValue() == null) {
0988:                        // component deinstalled, lets uninstall all isteners
0989:                        release();
0990:                    }
0991:                }
0992:
0993:            }
0994:
0995:            /** Implementation */
0996:            public void changedUpdate(DocumentEvent e) {
0997:            }
0998:
0999:            /** Implementation */
1000:            public void insertUpdate(DocumentEvent e) {
1001:                // handle new lines,  Enter hit at end of line changes
1002:                // the line element instance
1003:                // XXX Actually NB document implementation triggers this method two times
1004:                //  - first time with one removed and two added lines
1005:                //  - second time with two removed and two added lines
1006:                if (elementAnnotations != null) {
1007:                    Element[] elements = e.getDocument().getRootElements();
1008:                    synchronized (elementAnnotations) { // atomic change
1009:                        for (int i = 0; i < elements.length; i++) {
1010:                            Element element = elements[i];
1011:                            DocumentEvent.ElementChange change = e
1012:                                    .getChange(element);
1013:                            if (change == null)
1014:                                continue;
1015:                            Element[] removed = change.getChildrenRemoved();
1016:                            Element[] added = change.getChildrenAdded();
1017:
1018:                            if (removed.length == added.length) {
1019:                                for (int c = 0; c < removed.length; c++) {
1020:                                    AnnotateLine recent = elementAnnotations
1021:                                            .get(removed[c]);
1022:                                    if (recent != null) {
1023:                                        elementAnnotations.remove(removed[c]);
1024:                                        elementAnnotations
1025:                                                .put(added[c], recent);
1026:                                    }
1027:                                }
1028:                            } else if (removed.length == 1 && added.length > 0) {
1029:                                Element key = removed[0];
1030:                                AnnotateLine recent = elementAnnotations
1031:                                        .get(key);
1032:                                if (recent != null) {
1033:                                    elementAnnotations.remove(key);
1034:                                    elementAnnotations.put(added[0], recent);
1035:                                }
1036:                            }
1037:                        }
1038:                    }
1039:                }
1040:                repaint();
1041:            }
1042:
1043:            /** Implementation */
1044:            public void removeUpdate(DocumentEvent e) {
1045:                if (e.getDocument().getLength() == 0) { // external reload
1046:                    hideBar();
1047:                }
1048:                repaint();
1049:            }
1050:
1051:            /** Caret */
1052:            public void stateChanged(ChangeEvent e) {
1053:                assert e.getSource() == caret;
1054:                caretTimer.restart();
1055:            }
1056:
1057:            /** Timer */
1058:            public void actionPerformed(ActionEvent e) {
1059:                assert e.getSource() == caretTimer;
1060:                onCurrentLine();
1061:            }
1062:
1063:            /** on JTextPane */
1064:            public void componentHidden(ComponentEvent e) {
1065:            }
1066:
1067:            /** on JTextPane */
1068:            public void componentMoved(ComponentEvent e) {
1069:            }
1070:
1071:            /** on JTextPane */
1072:            public void componentResized(ComponentEvent e) {
1073:                revalidate();
1074:            }
1075:
1076:            /** on JTextPane */
1077:            public void componentShown(ComponentEvent e) {
1078:            }
1079:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.