Source Code Cross Referenced for RevisionPainter.java in  » IDE-Eclipse » jface » org » eclipse » jface » internal » text » revisions » 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 Eclipse » jface » org.eclipse.jface.internal.text.revisions 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*******************************************************************************
0002:         * Copyright (c) 2006, 2007 IBM Corporation and others.
0003:         * All rights reserved. This program and the accompanying materials
0004:         * are made available under the terms of the Eclipse Public License v1.0
0005:         * which accompanies this distribution, and is available at
0006:         * http://www.eclipse.org/legal/epl-v10.html
0007:         *
0008:         * Contributors:
0009:         *     IBM Corporation - initial API and implementation
0010:         *******************************************************************************/package org.eclipse.jface.internal.text.revisions;
0011:
0012:        import java.util.ArrayList;
0013:        import java.util.Arrays;
0014:        import java.util.Collections;
0015:        import java.util.HashMap;
0016:        import java.util.Iterator;
0017:        import java.util.List;
0018:        import java.util.ListIterator;
0019:        import java.util.Map;
0020:        import java.util.Map.Entry;
0021:
0022:        import org.eclipse.swt.SWT;
0023:        import org.eclipse.swt.custom.StyledText;
0024:        import org.eclipse.swt.events.DisposeEvent;
0025:        import org.eclipse.swt.events.DisposeListener;
0026:        import org.eclipse.swt.events.MouseEvent;
0027:        import org.eclipse.swt.events.MouseListener;
0028:        import org.eclipse.swt.events.MouseMoveListener;
0029:        import org.eclipse.swt.events.MouseTrackListener;
0030:        import org.eclipse.swt.graphics.Color;
0031:        import org.eclipse.swt.graphics.FontMetrics;
0032:        import org.eclipse.swt.graphics.GC;
0033:        import org.eclipse.swt.graphics.Point;
0034:        import org.eclipse.swt.graphics.RGB;
0035:        import org.eclipse.swt.graphics.Rectangle;
0036:        import org.eclipse.swt.widgets.Canvas;
0037:        import org.eclipse.swt.widgets.Control;
0038:        import org.eclipse.swt.widgets.Display;
0039:        import org.eclipse.swt.widgets.Event;
0040:        import org.eclipse.swt.widgets.Listener;
0041:        import org.eclipse.swt.widgets.Shell;
0042:
0043:        import org.eclipse.core.runtime.Assert;
0044:        import org.eclipse.core.runtime.ListenerList;
0045:        import org.eclipse.core.runtime.Platform;
0046:
0047:        import org.eclipse.jface.internal.text.html.BrowserInformationControl;
0048:        import org.eclipse.jface.internal.text.html.HTMLPrinter;
0049:        import org.eclipse.jface.internal.text.html.HTMLTextPresenter;
0050:
0051:        import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
0052:        import org.eclipse.jface.text.BadLocationException;
0053:        import org.eclipse.jface.text.DefaultInformationControl;
0054:        import org.eclipse.jface.text.IDocument;
0055:        import org.eclipse.jface.text.IInformationControl;
0056:        import org.eclipse.jface.text.IInformationControlCreator;
0057:        import org.eclipse.jface.text.IRegion;
0058:        import org.eclipse.jface.text.ITextViewer;
0059:        import org.eclipse.jface.text.ITextViewerExtension5;
0060:        import org.eclipse.jface.text.JFaceTextUtil;
0061:        import org.eclipse.jface.text.Position;
0062:        import org.eclipse.jface.text.Region;
0063:        import org.eclipse.jface.text.information.IInformationProviderExtension2;
0064:        import org.eclipse.jface.text.revisions.IRevisionListener;
0065:        import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension;
0066:        import org.eclipse.jface.text.revisions.Revision;
0067:        import org.eclipse.jface.text.revisions.RevisionEvent;
0068:        import org.eclipse.jface.text.revisions.RevisionInformation;
0069:        import org.eclipse.jface.text.revisions.RevisionRange;
0070:        import org.eclipse.jface.text.revisions.IRevisionRulerColumnExtension.RenderingMode;
0071:        import org.eclipse.jface.text.source.Annotation;
0072:        import org.eclipse.jface.text.source.CompositeRuler;
0073:        import org.eclipse.jface.text.source.IAnnotationHover;
0074:        import org.eclipse.jface.text.source.IAnnotationHoverExtension;
0075:        import org.eclipse.jface.text.source.IAnnotationHoverExtension2;
0076:        import org.eclipse.jface.text.source.IAnnotationModel;
0077:        import org.eclipse.jface.text.source.IAnnotationModelExtension;
0078:        import org.eclipse.jface.text.source.IAnnotationModelListener;
0079:        import org.eclipse.jface.text.source.IChangeRulerColumn;
0080:        import org.eclipse.jface.text.source.ILineDiffer;
0081:        import org.eclipse.jface.text.source.ILineRange;
0082:        import org.eclipse.jface.text.source.ISharedTextColors;
0083:        import org.eclipse.jface.text.source.ISourceViewer;
0084:        import org.eclipse.jface.text.source.IVerticalRulerColumn;
0085:        import org.eclipse.jface.text.source.LineRange;
0086:
0087:        /**
0088:         * A strategy for painting the live annotate colors onto the vertical ruler column. It also manages
0089:         * the revision hover.
0090:         * 
0091:         * @since 3.2
0092:         */
0093:        public final class RevisionPainter {
0094:            /** Tells whether this class is in debug mode. */
0095:            private static boolean DEBUG = "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text.source/debug/RevisionRulerColumn")); //$NON-NLS-1$//$NON-NLS-2$
0096:
0097:            // RGBs provided by UI Designer
0098:            private static final RGB BY_DATE_START_COLOR = new RGB(199, 134, 57);
0099:            private static final RGB BY_DATE_END_COLOR = new RGB(241, 225, 206);
0100:
0101:            /**
0102:             * The annotations created to show a revision in the overview ruler.
0103:             */
0104:            private static final class RevisionAnnotation extends Annotation {
0105:                public RevisionAnnotation(String text) {
0106:                    super (
0107:                            "org.eclipse.ui.workbench.texteditor.revisionAnnotation", false, text); //$NON-NLS-1$
0108:                }
0109:            }
0110:
0111:            /**
0112:             * The color tool manages revision colors and computes shaded colors based on the relative age
0113:             * and author of a revision.
0114:             */
0115:            private final class ColorTool {
0116:                /**
0117:                 * The average perceived intensity of a base color. 0 means black, 1 means white. A base
0118:                 * revision color perceived as light such as yellow will be darkened, while colors perceived
0119:                 * as dark such as blue will be lightened up.
0120:                 */
0121:                private static final float AVERAGE_INTENSITY = 0.5f;
0122:                /**
0123:                 * The maximum shading in [0, 1] - this is the shade that the most recent revision will
0124:                 * receive.
0125:                 */
0126:                private static final float MAX_SHADING = 0.7f;
0127:                /**
0128:                 * The minimum shading in [0, 1] - this is the shade that the oldest revision will receive.
0129:                 */
0130:                private static final float MIN_SHADING = 0.2f;
0131:                /**
0132:                 * The shade for the focus boxes.
0133:                 */
0134:                private static final float FOCUS_COLOR_SHADING = 1f;
0135:
0136:                /**
0137:                 * A list of {@link Long}, storing the age of each revision in a sorted list.
0138:                 */
0139:                private List fRevisions;
0140:                /**
0141:                 * The stored shaded colors.
0142:                 */
0143:                private final Map fColors = new HashMap();
0144:                /**
0145:                 * The stored focus colors.
0146:                 */
0147:                private final Map fFocusColors = new HashMap();
0148:
0149:                /**
0150:                 * Sets the revision information, which is needed to compute the relative age of a revision.
0151:                 * 
0152:                 * @param info the new revision info, <code>null</code> for none.
0153:                 */
0154:                public void setInfo(RevisionInformation info) {
0155:                    fRevisions = null;
0156:                    fColors.clear();
0157:                    fFocusColors.clear();
0158:
0159:                    if (info == null)
0160:                        return;
0161:                    List revisions = new ArrayList();
0162:                    for (Iterator it = info.getRevisions().iterator(); it
0163:                            .hasNext();) {
0164:                        Revision revision = (Revision) it.next();
0165:                        revisions.add(new Long(computeAge(revision)));
0166:                    }
0167:                    Collections.sort(revisions);
0168:                    fRevisions = revisions;
0169:                }
0170:
0171:                private RGB adaptColor(Revision revision, boolean focus) {
0172:                    RGB rgb;
0173:                    float scale;
0174:                    if (fRenderingMode == IRevisionRulerColumnExtension.AGE) {
0175:                        int index = computeAgeIndex(revision);
0176:                        if (index == -1 || fRevisions.size() == 0) {
0177:                            rgb = getBackground().getRGB();
0178:                        } else {
0179:                            // gradient from intense red for most recent to faint yellow for oldest
0180:                            RGB[] gradient = Colors.palette(
0181:                                    BY_DATE_START_COLOR, BY_DATE_END_COLOR,
0182:                                    fRevisions.size());
0183:                            rgb = gradient[gradient.length - index - 1];
0184:                        }
0185:                        scale = 0.99f;
0186:                    } else if (fRenderingMode == IRevisionRulerColumnExtension.AUTHOR) {
0187:                        rgb = revision.getColor();
0188:                        rgb = Colors.adjustBrightness(rgb, AVERAGE_INTENSITY);
0189:                        scale = 0.6f;
0190:                    } else if (fRenderingMode == IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE) {
0191:                        rgb = revision.getColor();
0192:                        rgb = Colors.adjustBrightness(rgb, AVERAGE_INTENSITY);
0193:                        int index = computeAgeIndex(revision);
0194:                        int size = fRevisions.size();
0195:                        // relative age: newest is 0, oldest is 1
0196:                        // if there is only one revision, use an intermediate value to avoid extreme coloring
0197:                        if (index == -1 || size < 2)
0198:                            scale = 0.5f;
0199:                        else
0200:                            scale = (float) index / (size - 1);
0201:                    } else {
0202:                        Assert.isTrue(false);
0203:                        return null; // dummy
0204:                    }
0205:                    rgb = getShadedColor(rgb, scale, focus);
0206:                    return rgb;
0207:                }
0208:
0209:                private int computeAgeIndex(Revision revision) {
0210:                    long age = computeAge(revision);
0211:                    int index = fRevisions.indexOf(new Long(age));
0212:                    return index;
0213:                }
0214:
0215:                private RGB getShadedColor(RGB color, float scale, boolean focus) {
0216:                    Assert.isLegal(scale >= 0.0);
0217:                    Assert.isLegal(scale <= 1.0);
0218:                    RGB background = getBackground().getRGB();
0219:
0220:                    // normalize to lie within [MIN_SHADING, MAX_SHADING]
0221:                    // use more intense colors if the ruler is narrow (i.e. not showing line numbers)
0222:                    boolean makeIntense = getWidth() <= 15;
0223:                    float intensityShift = makeIntense ? 0.3f : 0f;
0224:                    float max = MAX_SHADING + intensityShift;
0225:                    float min = MIN_SHADING + intensityShift;
0226:                    scale = (max - min) * scale + min;
0227:
0228:                    // focus coloring
0229:                    if (focus) {
0230:                        scale += FOCUS_COLOR_SHADING;
0231:                        if (scale > 1) {
0232:                            background = new RGB(255 - background.red,
0233:                                    255 - background.green,
0234:                                    255 - background.blue);
0235:                            scale = 2 - scale;
0236:                        }
0237:                    }
0238:
0239:                    return Colors.blend(background, color, scale);
0240:                }
0241:
0242:                private long computeAge(Revision revision) {
0243:                    return revision.getDate().getTime();
0244:                }
0245:
0246:                /**
0247:                 * Returns the color for a revision based on relative age and author.
0248:                 * 
0249:                 * @param revision the revision
0250:                 * @param focus <code>true</code> to return the focus color
0251:                 * @return the color for a revision
0252:                 */
0253:                public RGB getColor(Revision revision, boolean focus) {
0254:                    Map map = focus ? fFocusColors : fColors;
0255:                    RGB color = (RGB) map.get(revision);
0256:                    if (color != null)
0257:                        return color;
0258:
0259:                    color = adaptColor(revision, focus);
0260:                    map.put(revision, color);
0261:                    return color;
0262:                }
0263:            }
0264:
0265:            /**
0266:             * Handles all the mouse interaction in this line number ruler column.
0267:             */
0268:            private class MouseHandler implements  MouseListener,
0269:                    MouseMoveListener, MouseTrackListener, Listener {
0270:
0271:                private RevisionRange fMouseDownRegion;
0272:
0273:                /*
0274:                 * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
0275:                 */
0276:                public void mouseUp(MouseEvent e) {
0277:                    if (e.button == 1) {
0278:                        RevisionRange upRegion = fFocusRange;
0279:                        RevisionRange downRegion = fMouseDownRegion;
0280:                        fMouseDownRegion = null;
0281:
0282:                        if (upRegion == downRegion) {
0283:                            Revision revision = upRegion == null ? null
0284:                                    : upRegion.getRevision();
0285:                            if (revision == fSelectedRevision)
0286:                                revision = null; // deselect already selected revision
0287:                            handleRevisionSelected(revision);
0288:                        }
0289:                    }
0290:                }
0291:
0292:                /*
0293:                 * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
0294:                 */
0295:                public void mouseDown(MouseEvent e) {
0296:                    if (e.button == 3)
0297:                        updateFocusRevision(null); // kill any focus as the ctx menu is going to show
0298:                    if (e.button == 1) {
0299:                        fMouseDownRegion = fFocusRange;
0300:                        postRedraw();
0301:                    }
0302:                }
0303:
0304:                /*
0305:                 * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
0306:                 */
0307:                public void mouseDoubleClick(MouseEvent e) {
0308:                }
0309:
0310:                /*
0311:                 * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
0312:                 */
0313:                public void handleEvent(Event event) {
0314:                    Assert.isTrue(event.type == SWT.MouseWheel);
0315:                    handleMouseWheel(event);
0316:                }
0317:
0318:                /*
0319:                 * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
0320:                 */
0321:                public void mouseEnter(MouseEvent e) {
0322:                    updateFocusLine(toDocumentLineNumber(e.y));
0323:                }
0324:
0325:                /*
0326:                 * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
0327:                 */
0328:                public void mouseExit(MouseEvent e) {
0329:                    updateFocusLine(-1);
0330:                }
0331:
0332:                /*
0333:                 * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
0334:                 */
0335:                public void mouseHover(MouseEvent e) {
0336:                }
0337:
0338:                /*
0339:                 * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
0340:                 */
0341:                public void mouseMove(MouseEvent e) {
0342:                    updateFocusLine(toDocumentLineNumber(e.y));
0343:                }
0344:            }
0345:
0346:            /**
0347:             * Internal listener class that will update the ruler when the underlying model changes.
0348:             */
0349:            private class AnnotationListener implements 
0350:                    IAnnotationModelListener {
0351:                /*
0352:                 * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
0353:                 */
0354:                public void modelChanged(IAnnotationModel model) {
0355:                    clearRangeCache();
0356:                    postRedraw();
0357:                }
0358:
0359:            }
0360:
0361:            /**
0362:             * The information control creator.
0363:             */
0364:            private static final class HoverInformationControlCreator extends
0365:                    AbstractReusableInformationControlCreator {
0366:                private boolean fIsFocusable;
0367:
0368:                public HoverInformationControlCreator(boolean isFocusable) {
0369:                    fIsFocusable = isFocusable;
0370:                }
0371:
0372:                /*
0373:                 * @see org.eclipse.jface.internal.text.revisions.AbstractReusableInformationControlCreator#doCreateInformationControl(org.eclipse.swt.widgets.Shell)
0374:                 */
0375:                protected IInformationControl doCreateInformationControl(
0376:                        Shell parent) {
0377:                    int style = fIsFocusable ? SWT.V_SCROLL | SWT.H_SCROLL
0378:                            : SWT.NONE;
0379:
0380:                    if (BrowserInformationControl.isAvailable(parent)) {
0381:                        final int shellStyle = SWT.TOOL
0382:                                | (fIsFocusable ? SWT.RESIZE : SWT.NO_TRIM);
0383:                        return new BrowserInformationControl(parent,
0384:                                shellStyle, style) {
0385:                            /*
0386:                             * @see org.eclipse.jface.internal.text.html.BrowserInformationControl#setInformation(java.lang.String)
0387:                             * @since 3.3
0388:                             */
0389:                            public void setInformation(String content) {
0390:                                content = addCSSToHTMLFragment(content);
0391:                                super .setInformation(content);
0392:                            }
0393:
0394:                            /**
0395:                             * Adds a HTML header and CSS info if <code>html</code> is only an HTML fragment (has no
0396:                             * &lt;html&gt; section).
0397:                             * 
0398:                             * @param html the html / text produced by a revision
0399:                             * @return modified html
0400:                             */
0401:                            private String addCSSToHTMLFragment(String html) {
0402:                                int max = Math.min(100, html.length());
0403:                                if (html.substring(0, max).indexOf("<html>") != -1) //$NON-NLS-1$
0404:                                    // there is already a header
0405:                                    return html;
0406:
0407:                                StringBuffer info = new StringBuffer(512 + html
0408:                                        .length());
0409:                                HTMLPrinter.insertPageProlog(info, 0,
0410:                                        fgStyleSheet);
0411:                                info.append(html);
0412:                                HTMLPrinter.addPageEpilog(info);
0413:                                return info.toString();
0414:                            }
0415:
0416:                        };
0417:                    }
0418:                    return new DefaultInformationControl(parent, style,
0419:                            new HTMLTextPresenter());
0420:                }
0421:            }
0422:
0423:            private static final String fgStyleSheet = "/* Font definitions */\n" + //$NON-NLS-1$
0424:                    "body, h1, h2, h3, h4, h5, h6, p, table, td, caption, th, ul, ol, dl, li, dd, dt {font-family: sans-serif; font-size: 9pt }\n"
0425:                    + //$NON-NLS-1$ 
0426:                    "pre				{ font-family: monospace; font-size: 9pt }\n"
0427:                    + //$NON-NLS-1$
0428:                    "\n"
0429:                    + //$NON-NLS-1$
0430:                    "/* Margins */\n"
0431:                    + //$NON-NLS-1$
0432:                    "body	     { overflow: auto; margin-top: 0; margin-bottom: 4; margin-left: 3; margin-right: 0 }\n"
0433:                    + //$NON-NLS-1$ 
0434:                    "h1           { margin-top: 5; margin-bottom: 1 }	\n"
0435:                    + //$NON-NLS-1$
0436:                    "h2           { margin-top: 25; margin-bottom: 3 }\n"
0437:                    + //$NON-NLS-1$
0438:                    "h3           { margin-top: 20; margin-bottom: 3 }\n"
0439:                    + //$NON-NLS-1$
0440:                    "h4           { margin-top: 20; margin-bottom: 3 }\n"
0441:                    + //$NON-NLS-1$
0442:                    "h5           { margin-top: 0; margin-bottom: 0 }\n"
0443:                    + //$NON-NLS-1$
0444:                    "p            { margin-top: 10px; margin-bottom: 10px }\n"
0445:                    + //$NON-NLS-1$
0446:                    "pre	         { margin-left: 6 }\n"
0447:                    + //$NON-NLS-1$
0448:                    "ul	         { margin-top: 0; margin-bottom: 10 }\n"
0449:                    + //$NON-NLS-1$ 
0450:                    "li	         { margin-top: 0; margin-bottom: 0 } \n"
0451:                    + //$NON-NLS-1$
0452:                    "li p	     { margin-top: 0; margin-bottom: 0 } \n"
0453:                    + //$NON-NLS-1$
0454:                    "ol	         { margin-top: 0; margin-bottom: 10 }\n"
0455:                    + //$NON-NLS-1$
0456:                    "dl	         { margin-top: 0; margin-bottom: 10 }\n"
0457:                    + //$NON-NLS-1$
0458:                    "dt	         { margin-top: 0; margin-bottom: 0; font-weight: bold }\n"
0459:                    + //$NON-NLS-1$ 
0460:                    "dd	         { margin-top: 0; margin-bottom: 0 }\n" + //$NON-NLS-1$
0461:                    "\n" + //$NON-NLS-1$
0462:                    "/* Styles and colors */\n" + //$NON-NLS-1$
0463:                    "a:link	     { color: #0000FF }\n" + //$NON-NLS-1$
0464:                    "a:hover	     { color: #000080 }\n" + //$NON-NLS-1$
0465:                    "a:visited    { text-decoration: underline }\n" + //$NON-NLS-1$
0466:                    "h4           { font-style: italic }\n" + //$NON-NLS-1$ 
0467:                    "strong	     { font-weight: bold }\n" + //$NON-NLS-1$
0468:                    "em	         { font-style: italic }\n" + //$NON-NLS-1$
0469:                    "var	         { font-style: italic }\n" + //$NON-NLS-1$
0470:                    "th	         { font-weight: bold }\n" + //$NON-NLS-1$
0471:                    ""; //$NON-NLS-1$
0472:
0473:            /**
0474:             * The revision hover displays information about the currently selected revision.
0475:             */
0476:            private final class RevisionHover implements  IAnnotationHover,
0477:                    IAnnotationHoverExtension, IAnnotationHoverExtension2,
0478:                    IInformationProviderExtension2 {
0479:
0480:                /*
0481:                 * @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
0482:                 *      int)
0483:                 */
0484:                public String getHoverInfo(ISourceViewer sourceViewer,
0485:                        int lineNumber) {
0486:                    Object info = getHoverInfo(sourceViewer, getHoverLineRange(
0487:                            sourceViewer, lineNumber), 0);
0488:                    return info == null ? null : info.toString();
0489:                }
0490:
0491:                /*
0492:                 * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
0493:                 */
0494:                public IInformationControlCreator getHoverControlCreator() {
0495:                    RevisionInformation revisionInfo = fRevisionInfo;
0496:                    if (revisionInfo != null) {
0497:                        IInformationControlCreator creator = revisionInfo
0498:                                .getHoverControlCreator();
0499:                        if (creator != null)
0500:                            return creator;
0501:                    }
0502:                    return new HoverInformationControlCreator(false);
0503:                }
0504:
0505:                /*
0506:                 * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
0507:                 */
0508:                public boolean canHandleMouseCursor() {
0509:                    return false;
0510:                }
0511:
0512:                /*
0513:                 * @see org.eclipse.jface.text.source.IAnnotationHoverExtension2#canHandleMouseWheel()
0514:                 */
0515:                public boolean canHandleMouseWheel() {
0516:                    return true;
0517:                }
0518:
0519:                /*
0520:                 * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer,
0521:                 *      org.eclipse.jface.text.source.ILineRange, int)
0522:                 */
0523:                public Object getHoverInfo(ISourceViewer sourceViewer,
0524:                        ILineRange lineRange, int visibleNumberOfLines) {
0525:                    RevisionRange range = getRange(lineRange.getStartLine());
0526:                    Object info = range == null ? null : range.getRevision()
0527:                            .getHoverInfo();
0528:                    return info;
0529:                }
0530:
0531:                /*
0532:                 * @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer,
0533:                 *      int)
0534:                 */
0535:                public ILineRange getHoverLineRange(ISourceViewer viewer,
0536:                        int lineNumber) {
0537:                    RevisionRange range = getRange(lineNumber);
0538:                    return range == null ? null : new LineRange(lineNumber, 1);
0539:                }
0540:
0541:                /*
0542:                 * @see org.eclipse.jface.text.information.IInformationProviderExtension2#getInformationPresenterControlCreator()
0543:                 */
0544:                public IInformationControlCreator getInformationPresenterControlCreator() {
0545:                    RevisionInformation revisionInfo = fRevisionInfo;
0546:                    if (revisionInfo != null) {
0547:                        IInformationControlCreator creator = revisionInfo
0548:                                .getInformationPresenterControlCreator();
0549:                        if (creator != null)
0550:                            return creator;
0551:                    }
0552:                    return new HoverInformationControlCreator(true);
0553:                }
0554:            }
0555:
0556:            /* Listeners and helpers. */
0557:
0558:            /** The shared color provider. */
0559:            private final ISharedTextColors fSharedColors;
0560:            /** The color tool. */
0561:            private final ColorTool fColorTool = new ColorTool();
0562:            /** The mouse handler. */
0563:            private final MouseHandler fMouseHandler = new MouseHandler();
0564:            /** The hover. */
0565:            private final RevisionHover fHover = new RevisionHover();
0566:            /** The annotation listener. */
0567:            private final AnnotationListener fAnnotationListener = new AnnotationListener();
0568:            /** The selection provider. */
0569:            private final RevisionSelectionProvider fRevisionSelectionProvider = new RevisionSelectionProvider(
0570:                    this );
0571:            /**
0572:             * The list of revision listeners.
0573:             * @since 3.3.
0574:             */
0575:            private final ListenerList fRevisionListeners = new ListenerList();
0576:
0577:            /* The context - column and viewer we are connected to. */
0578:
0579:            /** The vertical ruler column that delegates painting to this painter. */
0580:            private final IVerticalRulerColumn fColumn;
0581:            /** The parent ruler. */
0582:            private CompositeRuler fParentRuler;
0583:            /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */
0584:            private Control fControl;
0585:            /** The text viewer that the column is attached to. */
0586:            private ITextViewer fViewer;
0587:            /** The viewer's text widget. */
0588:            private StyledText fWidget;
0589:
0590:            /* The models we operate on. */
0591:
0592:            /** The revision model object. */
0593:            private RevisionInformation fRevisionInfo;
0594:            /** The line differ. */
0595:            private ILineDiffer fLineDiffer = null;
0596:            /** The annotation model. */
0597:            private IAnnotationModel fAnnotationModel = null;
0598:            /** The background color, possibly <code>null</code>. */
0599:            private Color fBackground;
0600:
0601:            /* Cache. */
0602:
0603:            /** The cached list of ranges adapted to quick diff. */
0604:            private List fRevisionRanges = null;
0605:            /** The annotations created for the overview ruler temporary display. */
0606:            private List fAnnotations = new ArrayList();
0607:
0608:            /* State */
0609:
0610:            /** The current focus line, -1 for none. */
0611:            private int fFocusLine = -1;
0612:            /** The current focus region, <code>null</code> if none. */
0613:            private RevisionRange fFocusRange = null;
0614:            /** The current focus revision, <code>null</code> if none. */
0615:            private Revision fFocusRevision = null;
0616:            /**
0617:             * The currently selected revision, <code>null</code> if none. The difference between
0618:             * {@link #fFocusRevision} and {@link #fSelectedRevision} may not be obvious: the focus revision
0619:             * is the one focused by the mouse (by hovering over a block of the revision), while the
0620:             * selected revision is sticky, i.e. is not removed when the mouse leaves the ruler.
0621:             *
0622:             * @since 3.3
0623:             */
0624:            private Revision fSelectedRevision = null;
0625:            /** <code>true</code> if the mouse wheel handler is installed, <code>false</code> otherwise. */
0626:            private boolean fWheelHandlerInstalled = false;
0627:            /**
0628:             * The revision rendering mode.
0629:             */
0630:            private RenderingMode fRenderingMode = IRevisionRulerColumnExtension.AUTHOR_SHADED_BY_AGE;
0631:            /**
0632:             * The required with in characters.
0633:             * @since 3.3
0634:             */
0635:            private int fRequiredWidth = -1;
0636:            /**
0637:             * The width of the revision field in chars to compute {@link #fAuthorInset} from.
0638:             * @since 3.3
0639:             */
0640:            private int fRevisionIdChars = 0;
0641:            /**
0642:             * <code>true</code> to show revision ids, <code>false</code> otherwise.
0643:             * @since 3.3
0644:             */
0645:            private boolean fShowRevision = false;
0646:            /**
0647:             * <code>true</code> to show the author, <code>false</code> otherwise.
0648:             * @since 3.3
0649:             */
0650:            private boolean fShowAuthor = false;
0651:            /**
0652:             * The author inset in pixels for when author *and* revision id are shown.
0653:             * @since 3.3
0654:             */
0655:            private int fAuthorInset;
0656:            /**
0657:             * The remembered ruler width (as changing the ruler width triggers recomputation of the colors.
0658:             * @since 3.3
0659:             */
0660:            private int fLastWidth = -1;
0661:
0662:            /**
0663:             * Creates a new revision painter for a vertical ruler column.
0664:             * 
0665:             * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the
0666:             *        newly created painter.
0667:             * @param sharedColors a shared colors object to store shaded colors in
0668:             */
0669:            public RevisionPainter(IVerticalRulerColumn column,
0670:                    ISharedTextColors sharedColors) {
0671:                Assert.isLegal(column != null);
0672:                Assert.isLegal(sharedColors != null);
0673:                fColumn = column;
0674:                fSharedColors = sharedColors;
0675:            }
0676:
0677:            /**
0678:             * Sets the revision information to be drawn and triggers a redraw.
0679:             * 
0680:             * @param info the revision information to show, <code>null</code> to draw none
0681:             */
0682:            public void setRevisionInformation(RevisionInformation info) {
0683:                if (fRevisionInfo != info) {
0684:                    fRequiredWidth = -1;
0685:                    fRevisionIdChars = 0;
0686:                    fRevisionInfo = info;
0687:                    clearRangeCache();
0688:                    updateFocusRange(null);
0689:                    handleRevisionSelected((Revision) null);
0690:                    fColorTool.setInfo(info);
0691:                    postRedraw();
0692:                    informListeners();
0693:                }
0694:            }
0695:
0696:            /**
0697:             * Changes the rendering mode and triggers redrawing if needed.
0698:             *  
0699:             * @param renderingMode the rendering mode
0700:             * @since 3.3
0701:             */
0702:            public void setRenderingMode(RenderingMode renderingMode) {
0703:                Assert.isLegal(renderingMode != null);
0704:                if (fRenderingMode != renderingMode) {
0705:                    fRenderingMode = renderingMode;
0706:                    fColorTool.setInfo(fRevisionInfo);
0707:                    postRedraw();
0708:                }
0709:            }
0710:
0711:            /**
0712:             * Sets the background color.
0713:             * 
0714:             * @param background the background color, <code>null</code> for the platform's list
0715:             *        background
0716:             */
0717:            public void setBackground(Color background) {
0718:                fBackground = background;
0719:            }
0720:
0721:            /**
0722:             * Sets the parent ruler - the delegating column must call this method as soon as it creates its
0723:             * control.
0724:             * 
0725:             * @param parentRuler the parent ruler
0726:             */
0727:            public void setParentRuler(CompositeRuler parentRuler) {
0728:                fParentRuler = parentRuler;
0729:            }
0730:
0731:            /**
0732:             * Delegates the painting of the quick diff colors to this painter. The painter will draw the
0733:             * color boxes onto the passed {@link GC} for all model (document) lines in
0734:             * <code>visibleModelLines</code>.
0735:             * 
0736:             * @param gc the {@link GC} to draw onto
0737:             * @param visibleLines the lines (in document offsets) that are currently (perhaps only
0738:             *        partially) visible
0739:             */
0740:            public void paint(GC gc, ILineRange visibleLines) {
0741:                connectIfNeeded();
0742:                if (!isConnected())
0743:                    return;
0744:
0745:                // compute the horizontal indent of the author for the case that we show revision
0746:                // and author
0747:                if (fShowAuthor && fShowRevision) {
0748:                    char[] string = new char[fRevisionIdChars + 1];
0749:                    Arrays.fill(string, '9');
0750:                    if (string.length > 1) {
0751:                        string[0] = '.';
0752:                        string[1] = ' ';
0753:                    }
0754:                    fAuthorInset = gc.stringExtent(new String(string)).x;
0755:                }
0756:
0757:                // recompute colors (show intense colors if ruler is narrow)
0758:                int width = getWidth();
0759:                if (width != fLastWidth) {
0760:                    fColorTool.setInfo(fRevisionInfo);
0761:                    fLastWidth = width;
0762:                }
0763:
0764:                // draw change regions
0765:                List/* <RevisionRange> */ranges = getRanges(visibleLines);
0766:                for (Iterator it = ranges.iterator(); it.hasNext();) {
0767:                    RevisionRange region = (RevisionRange) it.next();
0768:                    paintRange(region, gc);
0769:                }
0770:            }
0771:
0772:            /**
0773:             * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is
0774:             * visible.
0775:             */
0776:            private void connectIfNeeded() {
0777:                if (isConnected() || fParentRuler == null)
0778:                    return;
0779:
0780:                fViewer = fParentRuler.getTextViewer();
0781:                if (fViewer == null)
0782:                    return;
0783:
0784:                fWidget = fViewer.getTextWidget();
0785:                if (fWidget == null)
0786:                    return;
0787:
0788:                fControl = fColumn.getControl();
0789:                if (fControl == null)
0790:                    return;
0791:
0792:                fControl.addMouseTrackListener(fMouseHandler);
0793:                fControl.addMouseMoveListener(fMouseHandler);
0794:                fControl.addMouseListener(fMouseHandler);
0795:                fControl.addDisposeListener(new DisposeListener() {
0796:                    /*
0797:                     * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
0798:                     */
0799:                    public void widgetDisposed(DisposeEvent e) {
0800:                        handleDispose();
0801:                    }
0802:                });
0803:
0804:                fRevisionSelectionProvider.install(fViewer);
0805:            }
0806:
0807:            /**
0808:             * Returns <code>true</code> if the column is fully connected.
0809:             * 
0810:             * @return <code>true</code> if the column is fully connected, false otherwise
0811:             */
0812:            private boolean isConnected() {
0813:                return fControl != null;
0814:            }
0815:
0816:            /**
0817:             * Sets the annotation model.
0818:             * 
0819:             * @param model the annotation model, possibly <code>null</code>
0820:             * @see IVerticalRulerColumn#setModel(IAnnotationModel)
0821:             */
0822:            public void setModel(IAnnotationModel model) {
0823:                IAnnotationModel diffModel;
0824:                if (model instanceof  IAnnotationModelExtension)
0825:                    diffModel = ((IAnnotationModelExtension) model)
0826:                            .getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
0827:                else
0828:                    diffModel = model;
0829:
0830:                setDiffer(diffModel);
0831:                setAnnotationModel(model);
0832:            }
0833:
0834:            /**
0835:             * Sets the annotation model.
0836:             * 
0837:             * @param model the annotation model.
0838:             */
0839:            private void setAnnotationModel(IAnnotationModel model) {
0840:                if (fAnnotationModel != model)
0841:                    fAnnotationModel = model;
0842:            }
0843:
0844:            /**
0845:             * Sets the line differ.
0846:             * 
0847:             * @param differ the line differ or <code>null</code> if none
0848:             */
0849:            private void setDiffer(IAnnotationModel differ) {
0850:                if (differ instanceof  ILineDiffer || differ == null) {
0851:                    if (fLineDiffer != differ) {
0852:                        if (fLineDiffer != null)
0853:                            ((IAnnotationModel) fLineDiffer)
0854:                                    .removeAnnotationModelListener(fAnnotationListener);
0855:                        fLineDiffer = (ILineDiffer) differ;
0856:                        if (fLineDiffer != null)
0857:                            ((IAnnotationModel) fLineDiffer)
0858:                                    .addAnnotationModelListener(fAnnotationListener);
0859:                    }
0860:                }
0861:            }
0862:
0863:            /**
0864:             * Disposes of the painter's resources.
0865:             */
0866:            private void handleDispose() {
0867:                updateFocusLine(-1);
0868:
0869:                if (fLineDiffer != null) {
0870:                    ((IAnnotationModel) fLineDiffer)
0871:                            .removeAnnotationModelListener(fAnnotationListener);
0872:                    fLineDiffer = null;
0873:                }
0874:
0875:                fRevisionSelectionProvider.uninstall();
0876:            }
0877:
0878:            /**
0879:             * Paints a single change region onto <code>gc</code>.
0880:             * 
0881:             * @param range the range to paint
0882:             * @param gc the {@link GC} to paint on
0883:             */
0884:            private void paintRange(RevisionRange range, GC gc) {
0885:                ILineRange widgetRange = modelLinesToWidgetLines(range);
0886:                if (widgetRange == null)
0887:                    return;
0888:
0889:                Revision revision = range.getRevision();
0890:                boolean drawArmedFocus = range == fMouseHandler.fMouseDownRegion;
0891:                boolean drawSelection = !drawArmedFocus
0892:                        && revision == fSelectedRevision;
0893:                boolean drawFocus = !drawSelection && !drawArmedFocus
0894:                        && revision == fFocusRevision;
0895:                Rectangle box = computeBoxBounds(widgetRange);
0896:
0897:                gc.setBackground(lookupColor(revision, false));
0898:                if (drawArmedFocus) {
0899:                    Color foreground = gc.getForeground();
0900:                    Color focusColor = lookupColor(revision, true);
0901:                    gc.setForeground(focusColor);
0902:                    gc.fillRectangle(box);
0903:                    gc.drawRectangle(box.x, box.y, box.width - 1,
0904:                            box.height - 1); // highlight box
0905:                    gc.drawRectangle(box.x + 1, box.y + 1, box.width - 3,
0906:                            box.height - 3); // inner highlight box
0907:                    gc.setForeground(foreground);
0908:                } else if (drawFocus || drawSelection) {
0909:                    Color foreground = gc.getForeground();
0910:                    Color focusColor = lookupColor(revision, true);
0911:                    gc.setForeground(focusColor);
0912:                    gc.fillRectangle(box);
0913:                    gc.drawRectangle(box.x, box.y, box.width - 1,
0914:                            box.height - 1); // highlight box
0915:                    gc.setForeground(foreground);
0916:                } else {
0917:                    gc.fillRectangle(box);
0918:                }
0919:
0920:                if ((fShowAuthor || fShowRevision)) {
0921:                    int indentation = 1;
0922:                    int baselineBias = getBaselineBias(gc, widgetRange
0923:                            .getStartLine());
0924:                    if (fShowAuthor && fShowRevision) {
0925:                        gc.drawString(revision.getId(), indentation, box.y
0926:                                + baselineBias, true);
0927:                        gc.drawString(revision.getAuthor(), fAuthorInset, box.y
0928:                                + baselineBias, true);
0929:                    } else if (fShowAuthor) {
0930:                        gc.drawString(revision.getAuthor(), indentation, box.y
0931:                                + baselineBias, true);
0932:                    } else if (fShowRevision) {
0933:                        gc.drawString(revision.getId(), indentation, box.y
0934:                                + baselineBias, true);
0935:                    }
0936:                }
0937:            }
0938:
0939:            /**
0940:             * Returns the difference between the baseline of the widget and the
0941:             * baseline as specified by the font for <code>gc</code>. When drawing
0942:             * line numbers, the returned bias should be added to obtain text lined up
0943:             * on the correct base line of the text widget.
0944:             *
0945:             * @param gc the <code>GC</code> to get the font metrics from
0946:             * @param widgetLine the widget line
0947:             * @return the baseline bias to use when drawing text that is lined up with
0948:             *         <code>fCachedTextWidget</code>
0949:             * @since 3.3
0950:             */
0951:            private int getBaselineBias(GC gc, int widgetLine) {
0952:                /*
0953:                 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951
0954:                 * widget line height may be more than the font height used for the
0955:                 * line numbers, since font styles (bold, italics...) can have larger
0956:                 * font metrics than the simple font used for the numbers.
0957:                 */
0958:                int offset = fWidget.getOffsetAtLine(widgetLine);
0959:                int widgetBaseline = fWidget.getBaseline(offset);
0960:
0961:                FontMetrics fm = gc.getFontMetrics();
0962:                int fontBaseline = fm.getAscent() + fm.getLeading();
0963:                int baselineBias = widgetBaseline - fontBaseline;
0964:                return Math.max(0, baselineBias);
0965:            }
0966:
0967:            /**
0968:             * Looks up the color for a certain revision.
0969:             * 
0970:             * @param revision the revision to get the color for
0971:             * @param focus <code>true</code> if it is the focus revision
0972:             * @return the color for the revision
0973:             */
0974:            private Color lookupColor(Revision revision, boolean focus) {
0975:                return fSharedColors.getColor(fColorTool.getColor(revision,
0976:                        focus));
0977:            }
0978:
0979:            /**
0980:             * Returns the revision range that contains the given line, or
0981:             * <code>null</code> if there is none.
0982:             * 
0983:             * @param line the line of interest
0984:             * @return the corresponding <code>RevisionRange</code> or <code>null</code>
0985:             */
0986:            private RevisionRange getRange(int line) {
0987:                List ranges = getRangeCache();
0988:
0989:                if (ranges.isEmpty() || line == -1)
0990:                    return null;
0991:
0992:                for (Iterator it = ranges.iterator(); it.hasNext();) {
0993:                    RevisionRange range = (RevisionRange) it.next();
0994:                    if (contains(range, line))
0995:                        return range;
0996:                }
0997:
0998:                // line may be right after the last region
0999:                RevisionRange lastRegion = (RevisionRange) ranges.get(ranges
1000:                        .size() - 1);
1001:                if (line == end(lastRegion))
1002:                    return lastRegion;
1003:                return null;
1004:            }
1005:
1006:            /**
1007:             * Returns the sublist of all <code>RevisionRange</code>s that intersect with the given lines.
1008:             * 
1009:             * @param lines the model based lines of interest
1010:             * @return elementType: RevisionRange
1011:             */
1012:            private List getRanges(ILineRange lines) {
1013:                List ranges = getRangeCache();
1014:
1015:                // return the interesting subset
1016:                int end = end(lines);
1017:                int first = -1, last = -1;
1018:                for (int i = 0; i < ranges.size(); i++) {
1019:                    RevisionRange range = (RevisionRange) ranges.get(i);
1020:                    int rangeEnd = end(range);
1021:                    if (first == -1 && rangeEnd > lines.getStartLine())
1022:                        first = i;
1023:                    if (first != -1 && rangeEnd > end) {
1024:                        last = i;
1025:                        break;
1026:                    }
1027:                }
1028:                if (first == -1)
1029:                    return Collections.EMPTY_LIST;
1030:                if (last == -1)
1031:                    last = ranges.size() - 1; // bottom index may be one too much
1032:
1033:                return ranges.subList(first, last + 1);
1034:            }
1035:
1036:            /**
1037:             * Gets all change ranges of the revisions in the revision model and adapts them to the current
1038:             * quick diff information. The list is cached.
1039:             * 
1040:             * @return the list of all change regions, with diff information applied
1041:             */
1042:            private List getRangeCache() {
1043:                if (fRevisionRanges == null) {
1044:                    if (fRevisionInfo == null) {
1045:                        fRevisionRanges = Collections.EMPTY_LIST;
1046:                    } else {
1047:                        Hunk[] hunks = HunkComputer.computeHunks(fLineDiffer,
1048:                                fViewer.getDocument().getNumberOfLines());
1049:                        fRevisionInfo.applyDiff(hunks);
1050:                        fRevisionRanges = fRevisionInfo.getRanges();
1051:                        updateOverviewAnnotations();
1052:                        informListeners();
1053:                    }
1054:                }
1055:
1056:                return fRevisionRanges;
1057:            }
1058:
1059:            /**
1060:             * Clears the range cache.
1061:             *
1062:             * @since 3.3
1063:             */
1064:            private void clearRangeCache() {
1065:                fRevisionRanges = null;
1066:            }
1067:
1068:            /**
1069:             * Returns <code>true</code> if <code>range</code> contains <code>line</code>. A line is
1070:             * not contained in a range if it is the range's exclusive end line.
1071:             * 
1072:             * @param range the range to check whether it contains <code>line</code>
1073:             * @param line the line the line
1074:             * @return <code>true</code> if <code>range</code> contains <code>line</code>,
1075:             *         <code>false</code> if not
1076:             */
1077:            private static boolean contains(ILineRange range, int line) {
1078:                return range.getStartLine() <= line && end(range) > line;
1079:            }
1080:
1081:            /**
1082:             * Computes the end index of a line range.
1083:             * 
1084:             * @param range a line range
1085:             * @return the last line (exclusive) of <code>range</code>
1086:             */
1087:            private static int end(ILineRange range) {
1088:                return range.getStartLine() + range.getNumberOfLines();
1089:            }
1090:
1091:            /**
1092:             * Returns the visible extent of a document line range in widget lines.
1093:             * 
1094:             * @param range the document line range
1095:             * @return the visible extent of <code>range</code> in widget lines
1096:             */
1097:            private ILineRange modelLinesToWidgetLines(ILineRange range) {
1098:                int widgetStartLine = -1;
1099:                int widgetEndLine = -1;
1100:                if (fViewer instanceof  ITextViewerExtension5) {
1101:                    ITextViewerExtension5 extension = (ITextViewerExtension5) fViewer;
1102:                    int modelEndLine = end(range);
1103:                    for (int modelLine = range.getStartLine(); modelLine < modelEndLine; modelLine++) {
1104:                        int widgetLine = extension
1105:                                .modelLine2WidgetLine(modelLine);
1106:                        if (widgetLine != -1) {
1107:                            if (widgetStartLine == -1)
1108:                                widgetStartLine = widgetLine;
1109:                            widgetEndLine = widgetLine;
1110:                        }
1111:                    }
1112:                } else {
1113:                    IRegion region = fViewer.getVisibleRegion();
1114:                    IDocument document = fViewer.getDocument();
1115:                    try {
1116:                        int visibleStartLine = document.getLineOfOffset(region
1117:                                .getOffset());
1118:                        int visibleEndLine = document.getLineOfOffset(region
1119:                                .getOffset()
1120:                                + region.getLength());
1121:                        widgetStartLine = Math.max(0, range.getStartLine()
1122:                                - visibleStartLine);
1123:                        widgetEndLine = Math
1124:                                .min(visibleEndLine, end(range) - 1);
1125:                    } catch (BadLocationException x) {
1126:                        x.printStackTrace();
1127:                        // ignore and return null
1128:                    }
1129:                }
1130:                if (widgetStartLine == -1 || widgetEndLine == -1)
1131:                    return null;
1132:                return new LineRange(widgetStartLine, widgetEndLine
1133:                        - widgetStartLine + 1);
1134:            }
1135:
1136:            /**
1137:             * Returns the revision hover.
1138:             * 
1139:             * @return the revision hover
1140:             */
1141:            public IAnnotationHover getHover() {
1142:                return fHover;
1143:            }
1144:
1145:            /**
1146:             * Computes and returns the bounds of the rectangle corresponding to a widget line range. The
1147:             * rectangle is in pixel coordinates relative to the text widget's
1148:             * {@link StyledText#getClientArea() client area} and has the width of the ruler.
1149:             * 
1150:             * @param range the widget line range
1151:             * @return the box bounds corresponding to <code>range</code>
1152:             */
1153:            private Rectangle computeBoxBounds(ILineRange range) {
1154:                int y1 = fWidget.getLinePixel(range.getStartLine());
1155:                int y2 = fWidget.getLinePixel(range.getStartLine()
1156:                        + range.getNumberOfLines());
1157:
1158:                return new Rectangle(0, y1, getWidth(), y2 - y1 - 1);
1159:            }
1160:
1161:            /**
1162:             * Shows (or hides) the overview annotations.
1163:             */
1164:            private void updateOverviewAnnotations() {
1165:                if (fAnnotationModel == null)
1166:                    return;
1167:
1168:                Revision revision = fFocusRevision != null ? fFocusRevision
1169:                        : fSelectedRevision;
1170:
1171:                Map added = null;
1172:                if (revision != null) {
1173:                    added = new HashMap();
1174:                    for (Iterator it = revision.getRegions().iterator(); it
1175:                            .hasNext();) {
1176:                        RevisionRange range = (RevisionRange) it.next();
1177:                        try {
1178:                            IRegion charRegion = toCharRegion(range);
1179:                            Position position = new Position(charRegion
1180:                                    .getOffset(), charRegion.getLength());
1181:                            Annotation annotation = new RevisionAnnotation(
1182:                                    revision.getId());
1183:                            added.put(annotation, position);
1184:                        } catch (BadLocationException x) {
1185:                            // ignore - document was changed, show no annotations
1186:                        }
1187:                    }
1188:                }
1189:
1190:                if (fAnnotationModel instanceof  IAnnotationModelExtension) {
1191:                    IAnnotationModelExtension ext = (IAnnotationModelExtension) fAnnotationModel;
1192:                    ext.replaceAnnotations((Annotation[]) fAnnotations
1193:                            .toArray(new Annotation[fAnnotations.size()]),
1194:                            added);
1195:                } else {
1196:                    for (Iterator it = fAnnotations.iterator(); it.hasNext();) {
1197:                        Annotation annotation = (Annotation) it.next();
1198:                        fAnnotationModel.removeAnnotation(annotation);
1199:                    }
1200:                    if (added != null) {
1201:                        for (Iterator it = added.entrySet().iterator(); it
1202:                                .hasNext();) {
1203:                            Entry entry = (Entry) it.next();
1204:                            fAnnotationModel.addAnnotation((Annotation) entry
1205:                                    .getKey(), (Position) entry.getValue());
1206:                        }
1207:                    }
1208:                }
1209:                fAnnotations.clear();
1210:                if (added != null)
1211:                    fAnnotations.addAll(added.keySet());
1212:
1213:            }
1214:
1215:            /**
1216:             * Returns the character offset based region of a line range.
1217:             * 
1218:             * @param lines the line range to convert
1219:             * @return the character offset range corresponding to <code>range</code>
1220:             * @throws BadLocationException if the line range is not within the document bounds
1221:             */
1222:            private IRegion toCharRegion(ILineRange lines)
1223:                    throws BadLocationException {
1224:                IDocument document = fViewer.getDocument();
1225:                int offset = document.getLineOffset(lines.getStartLine());
1226:                int nextLine = end(lines);
1227:                int endOffset;
1228:                if (nextLine >= document.getNumberOfLines())
1229:                    endOffset = document.getLength();
1230:                else
1231:                    endOffset = document.getLineOffset(nextLine);
1232:                return new Region(offset, endOffset - offset);
1233:            }
1234:
1235:            /**
1236:             * Handles the selection of a revision and informs listeners.
1237:             * 
1238:             * @param revision the selected revision, <code>null</code> for none
1239:             */
1240:            void handleRevisionSelected(Revision revision) {
1241:                fSelectedRevision = revision;
1242:                fRevisionSelectionProvider.revisionSelected(revision);
1243:                updateOverviewAnnotations();
1244:                postRedraw();
1245:            }
1246:
1247:            /**
1248:             * Handles the selection of a revision id and informs listeners
1249:             * 
1250:             * @param id the selected revision id
1251:             */
1252:            void handleRevisionSelected(String id) {
1253:                Assert.isLegal(id != null);
1254:                if (fRevisionInfo == null)
1255:                    return;
1256:
1257:                for (Iterator it = fRevisionInfo.getRevisions().iterator(); it
1258:                        .hasNext();) {
1259:                    Revision revision = (Revision) it.next();
1260:                    if (id.equals(revision.getId())) {
1261:                        handleRevisionSelected(revision);
1262:                        return;
1263:                    }
1264:                }
1265:
1266:                // clear selection if it does not exist
1267:                handleRevisionSelected((Revision) null);
1268:            }
1269:
1270:            /**
1271:             * Returns the selection provider.
1272:             * 
1273:             * @return the selection provider
1274:             */
1275:            public RevisionSelectionProvider getRevisionSelectionProvider() {
1276:                return fRevisionSelectionProvider;
1277:            }
1278:
1279:            /**
1280:             * Updates the focus line with a new line.
1281:             * 
1282:             * @param line the new focus line, -1 for no focus
1283:             */
1284:            private void updateFocusLine(int line) {
1285:                if (fFocusLine != line)
1286:                    onFocusLineChanged(fFocusLine, line);
1287:            }
1288:
1289:            /**
1290:             * Handles a changing focus line.
1291:             * 
1292:             * @param previousLine the old focus line (-1 for no focus)
1293:             * @param nextLine the new focus line (-1 for no focus)
1294:             */
1295:            private void onFocusLineChanged(int previousLine, int nextLine) {
1296:                if (DEBUG)
1297:                    System.out
1298:                            .println("line: " + previousLine + " > " + nextLine); //$NON-NLS-1$ //$NON-NLS-2$
1299:                fFocusLine = nextLine;
1300:                RevisionRange region = getRange(nextLine);
1301:                updateFocusRange(region);
1302:            }
1303:
1304:            /**
1305:             * Updates the focus range.
1306:             * 
1307:             * @param range the new focus range, <code>null</code> for no focus
1308:             */
1309:            private void updateFocusRange(RevisionRange range) {
1310:                if (range != fFocusRange)
1311:                    onFocusRangeChanged(fFocusRange, range);
1312:            }
1313:
1314:            /**
1315:             * Handles a changing focus range.
1316:             * 
1317:             * @param previousRange the old focus range (<code>null</code> for no focus)
1318:             * @param nextRange the new focus range (<code>null</code> for no focus)
1319:             */
1320:            private void onFocusRangeChanged(RevisionRange previousRange,
1321:                    RevisionRange nextRange) {
1322:                if (DEBUG)
1323:                    System.out
1324:                            .println("range: " + previousRange + " > " + nextRange); //$NON-NLS-1$ //$NON-NLS-2$
1325:                fFocusRange = nextRange;
1326:                Revision revision = nextRange == null ? null : nextRange
1327:                        .getRevision();
1328:                updateFocusRevision(revision);
1329:            }
1330:
1331:            private void updateFocusRevision(Revision revision) {
1332:                if (fFocusRevision != revision)
1333:                    onFocusRevisionChanged(fFocusRevision, revision);
1334:            }
1335:
1336:            /**
1337:             * Handles a changing focus revision.
1338:             * 
1339:             * @param previousRevision the old focus revision (<code>null</code> for no focus)
1340:             * @param nextRevision the new focus revision (<code>null</code> for no focus)
1341:             */
1342:            private void onFocusRevisionChanged(Revision previousRevision,
1343:                    Revision nextRevision) {
1344:                if (DEBUG)
1345:                    System.out
1346:                            .println("revision: " + previousRevision + " > " + nextRevision); //$NON-NLS-1$ //$NON-NLS-2$
1347:                fFocusRevision = nextRevision;
1348:                uninstallWheelHandler();
1349:                installWheelHandler();
1350:                updateOverviewAnnotations();
1351:                redraw(); // pick up new highlights
1352:            }
1353:
1354:            /**
1355:             * Uninstalls the mouse wheel handler.
1356:             */
1357:            private void uninstallWheelHandler() {
1358:                fControl.removeListener(SWT.MouseWheel, fMouseHandler);
1359:                fWheelHandlerInstalled = false;
1360:            }
1361:
1362:            /**
1363:             * Installs the mouse wheel handler.
1364:             */
1365:            private void installWheelHandler() {
1366:                if (fFocusRevision != null && !fWheelHandlerInstalled) {
1367:                    fControl.addListener(SWT.MouseWheel, fMouseHandler);
1368:                    fWheelHandlerInstalled = true;
1369:                }
1370:            }
1371:
1372:            /**
1373:             * Handles a mouse wheel event.
1374:             * 
1375:             * @param event the mouse wheel event
1376:             */
1377:            private void handleMouseWheel(Event event) {
1378:                boolean up = event.count > 0;
1379:                int documentHoverLine = fFocusLine;
1380:
1381:                ILineRange nextWidgetRange = null;
1382:                ILineRange last = null;
1383:                List ranges = fFocusRevision.getRegions();
1384:                if (up) {
1385:                    for (Iterator it = ranges.iterator(); it.hasNext();) {
1386:                        RevisionRange range = (RevisionRange) it.next();
1387:                        ILineRange widgetRange = modelLinesToWidgetLines(range);
1388:                        if (contains(range, documentHoverLine)) {
1389:                            nextWidgetRange = last;
1390:                            break;
1391:                        }
1392:                        if (widgetRange != null)
1393:                            last = widgetRange;
1394:                    }
1395:                } else {
1396:                    for (ListIterator it = ranges.listIterator(ranges.size()); it
1397:                            .hasPrevious();) {
1398:                        RevisionRange range = (RevisionRange) it.previous();
1399:                        ILineRange widgetRange = modelLinesToWidgetLines(range);
1400:                        if (contains(range, documentHoverLine)) {
1401:                            nextWidgetRange = last;
1402:                            break;
1403:                        }
1404:                        if (widgetRange != null)
1405:                            last = widgetRange;
1406:                    }
1407:                }
1408:
1409:                if (nextWidgetRange == null)
1410:                    return;
1411:
1412:                int widgetCurrentFocusLine = modelLinesToWidgetLines(
1413:                        new LineRange(documentHoverLine, 1)).getStartLine();
1414:                int widgetNextFocusLine = nextWidgetRange.getStartLine();
1415:                int newTopPixel = fWidget.getTopPixel()
1416:                        + JFaceTextUtil.computeLineHeight(fWidget,
1417:                                widgetCurrentFocusLine, widgetNextFocusLine,
1418:                                widgetNextFocusLine - widgetCurrentFocusLine);
1419:                fWidget.setTopPixel(newTopPixel);
1420:                if (newTopPixel < 0) {
1421:                    Point cursorLocation = fWidget.getDisplay()
1422:                            .getCursorLocation();
1423:                    cursorLocation.y += newTopPixel;
1424:                    fWidget.getDisplay().setCursorLocation(cursorLocation);
1425:                } else {
1426:                    int topPixel = fWidget.getTopPixel();
1427:                    if (topPixel < newTopPixel) {
1428:                        Point cursorLocation = fWidget.getDisplay()
1429:                                .getCursorLocation();
1430:                        cursorLocation.y += newTopPixel - topPixel;
1431:                        fWidget.getDisplay().setCursorLocation(cursorLocation);
1432:                    }
1433:                }
1434:                updateFocusLine(toDocumentLineNumber(fWidget.toControl(fWidget
1435:                        .getDisplay().getCursorLocation()).y));
1436:                immediateUpdate();
1437:            }
1438:
1439:            /**
1440:             * Triggers a redraw in the display thread.
1441:             */
1442:            private final void postRedraw() {
1443:                if (isConnected() && !fControl.isDisposed()) {
1444:                    Display d = fControl.getDisplay();
1445:                    if (d != null) {
1446:                        d.asyncExec(new Runnable() {
1447:                            public void run() {
1448:                                redraw();
1449:                            }
1450:                        });
1451:                    }
1452:                }
1453:            }
1454:
1455:            /**
1456:             * Translates a y coordinate in the pixel coordinates of the column's control to a document line
1457:             * number.
1458:             * 
1459:             * @param y the y coordinate
1460:             * @return the corresponding document line, -1 for no line
1461:             * @see CompositeRuler#toDocumentLineNumber(int)
1462:             */
1463:            private int toDocumentLineNumber(int y) {
1464:                return fParentRuler.toDocumentLineNumber(y);
1465:            }
1466:
1467:            /**
1468:             * Triggers redrawing of the column.
1469:             */
1470:            private void redraw() {
1471:                fColumn.redraw();
1472:            }
1473:
1474:            /**
1475:             * Triggers immediate redrawing of the entire column - use with care.
1476:             */
1477:            private void immediateUpdate() {
1478:                fParentRuler.immediateUpdate();
1479:            }
1480:
1481:            /**
1482:             * Returns the width of the column.
1483:             * 
1484:             * @return the width of the column
1485:             */
1486:            private int getWidth() {
1487:                return fColumn.getWidth();
1488:            }
1489:
1490:            /**
1491:             * Returns the System background color for list widgets.
1492:             * 
1493:             * @return the System background color for list widgets
1494:             */
1495:            private Color getBackground() {
1496:                if (fBackground == null)
1497:                    return fWidget.getDisplay().getSystemColor(
1498:                            SWT.COLOR_LIST_BACKGROUND);
1499:                return fBackground;
1500:            }
1501:
1502:            /**
1503:             * Sets the hover later returned by {@link #getHover()}.
1504:             * 
1505:             * @param hover the hover
1506:             */
1507:            public void setHover(IAnnotationHover hover) {
1508:                // TODO ignore for now - must make revision hover settable from outside
1509:            }
1510:
1511:            /**
1512:             * Returns <code>true</code> if the receiver can provide a hover for a certain document line.
1513:             * 
1514:             * @param activeLine the document line of interest
1515:             * @return <code>true</code> if the receiver can provide a hover
1516:             */
1517:            public boolean hasHover(int activeLine) {
1518:                return fViewer instanceof  ISourceViewer
1519:                        && fHover.getHoverLineRange((ISourceViewer) fViewer,
1520:                                activeLine) != null;
1521:            }
1522:
1523:            /**
1524:             * Returns the revision at a certain document offset, or <code>null</code> for none.
1525:             * 
1526:             * @param offset the document offset
1527:             * @return the revision at offset, or <code>null</code> for none
1528:             */
1529:            Revision getRevision(int offset) {
1530:                IDocument document = fViewer.getDocument();
1531:                int line;
1532:                try {
1533:                    line = document.getLineOfOffset(offset);
1534:                } catch (BadLocationException x) {
1535:                    return null;
1536:                }
1537:                if (line != -1) {
1538:                    RevisionRange range = getRange(line);
1539:                    if (range != null)
1540:                        return range.getRevision();
1541:                }
1542:                return null;
1543:            }
1544:
1545:            /**
1546:             * Returns <code>true</code> if a revision model has been set, <code>false</code> otherwise.
1547:             * 
1548:             * @return <code>true</code> if a revision model has been set, <code>false</code> otherwise
1549:             */
1550:            public boolean hasInformation() {
1551:                return fRevisionInfo != null;
1552:            }
1553:
1554:            /**
1555:             * Returns the width in chars required to display information.
1556:             * 
1557:             * @return the width in chars required to display information
1558:             * @since 3.3
1559:             */
1560:            public int getRequiredWidth() {
1561:                if (fRequiredWidth == -1) {
1562:                    if (hasInformation() && (fShowRevision || fShowAuthor)) {
1563:                        int revisionWidth = 0;
1564:                        int authorWidth = 0;
1565:                        for (Iterator it = fRevisionInfo.getRevisions()
1566:                                .iterator(); it.hasNext();) {
1567:                            Revision revision = (Revision) it.next();
1568:                            revisionWidth = Math.max(revisionWidth, revision
1569:                                    .getId().length());
1570:                            authorWidth = Math.max(authorWidth, revision
1571:                                    .getAuthor().length());
1572:                        }
1573:                        fRevisionIdChars = revisionWidth + 1;
1574:                        if (fShowAuthor && fShowRevision)
1575:                            fRequiredWidth = revisionWidth + authorWidth + 2;
1576:                        else if (fShowAuthor)
1577:                            fRequiredWidth = authorWidth + 1;
1578:                        else
1579:                            fRequiredWidth = revisionWidth + 1;
1580:                    } else {
1581:                        fRequiredWidth = 0;
1582:                    }
1583:                }
1584:                return fRequiredWidth;
1585:            }
1586:
1587:            /**
1588:             * Enables showing the revision id.
1589:             *  
1590:             * @param show <code>true</code> to show the revision, <code>false</code> to hide it
1591:             */
1592:            public void showRevisionId(boolean show) {
1593:                if (fShowRevision != show) {
1594:                    fRequiredWidth = -1;
1595:                    fRevisionIdChars = 0;
1596:                    fShowRevision = show;
1597:                    postRedraw();
1598:                }
1599:            }
1600:
1601:            /**
1602:             * Enables showing the revision author.
1603:             *  
1604:             * @param show <code>true</code> to show the author, <code>false</code> to hide it
1605:             */
1606:            public void showRevisionAuthor(boolean show) {
1607:                if (fShowAuthor != show) {
1608:                    fRequiredWidth = -1;
1609:                    fRevisionIdChars = 0;
1610:                    fShowAuthor = show;
1611:                    postRedraw();
1612:                }
1613:            }
1614:
1615:            /**
1616:             * Adds a revision listener.
1617:             * 
1618:             * @param listener the listener
1619:             * @since 3.3
1620:             */
1621:            public void addRevisionListener(IRevisionListener listener) {
1622:                fRevisionListeners.add(listener);
1623:            }
1624:
1625:            /**
1626:             * Removes a revision listener.
1627:             * 
1628:             * @param listener the listener
1629:             * @since 3.3
1630:             */
1631:            public void removeRevisionListener(IRevisionListener listener) {
1632:                fRevisionListeners.remove(listener);
1633:            }
1634:
1635:            /**
1636:             * Informs the revision listeners about a change.
1637:             * 
1638:             * @since 3.3
1639:             */
1640:            private void informListeners() {
1641:                if (fRevisionInfo == null || fRevisionListeners.isEmpty())
1642:                    return;
1643:
1644:                RevisionEvent event = new RevisionEvent(fRevisionInfo);
1645:                Object[] listeners = fRevisionListeners.getListeners();
1646:                for (int i = 0; i < listeners.length; i++) {
1647:                    IRevisionListener listener = (IRevisionListener) listeners[i];
1648:                    listener.revisionInformationChanged(event);
1649:                }
1650:            }
1651:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.