Source Code Cross Referenced for SynchronousXYChart.java in  » IDE-Netbeans » cvsclient » org » netbeans » lib » profiler » ui » charts » 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 » cvsclient » org.netbeans.lib.profiler.ui.charts 
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:         * The Original Software is NetBeans. The Initial Developer of the Original
0026:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0027:         * Microsystems, Inc. All Rights Reserved.
0028:         *
0029:         * If you wish your version of this file to be governed by only the CDDL
0030:         * or only the GPL Version 2, indicate your decision by adding
0031:         * "[Contributor] elects to include this software in this distribution
0032:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0033:         * single choice of license, a recipient has the option to distribute
0034:         * your version of this file under either the CDDL, the GPL Version 2 or
0035:         * to extend the choice of license to its licensees as provided above.
0036:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0037:         * Version 2 license, then the option applies only if the new code is
0038:         * made subject to such option by the copyright holder.
0039:         */
0040:
0041:        package org.netbeans.lib.profiler.ui.charts;
0042:
0043:        import java.awt.*;
0044:        import java.awt.event.*;
0045:        import java.awt.geom.*;
0046:        import java.awt.image.*;
0047:        import java.util.*;
0048:        import javax.accessibility.Accessible;
0049:        import javax.accessibility.AccessibleContext;
0050:        import javax.swing.*;
0051:
0052:        /**
0053:         *
0054:         * @author Tomas Hurka
0055:         * @author Jiri Sedlacek
0056:         */
0057:        public class SynchronousXYChart extends JComponent implements 
0058:                ComponentListener, ChartModelListener, MouseListener,
0059:                MouseMotionListener, AdjustmentListener, Accessible {
0060:            //~ Static fields/initializers -----------------------------------------------------------------------------------------------
0061:
0062:            // -----
0063:            // I18N String constants
0064:            private static final ResourceBundle messages = ResourceBundle
0065:                    .getBundle("org.netbeans.lib.profiler.ui.charts.Bundle"); // NOI18N
0066:            private static final String FIT_TO_WINDOW_STRING = messages
0067:                    .getString("SynchronousXYChart_FitToWindowString"); // NOI18N
0068:            // -----
0069:
0070:            // --- Constants -------------------------------------------------------------
0071:            public static final int TYPE_LINE = 1; // chart draws line segments
0072:            public static final int TYPE_FILL = 2; // chart draws filled areas
0073:            public static final int VALUES_INTERPOLATED = 50; // smooth joining of subsequent values
0074:            public static final int VALUES_DISCRETE = 51; // discrete values => "stairs" effect
0075:            public static final int COPY_ACCEL_GENERIC = 100; // Graphics.copyArea() used, optimal for UNIXes, works well also for Windows (default)
0076:            public static final int COPY_ACCEL_RASTER = 101; // BufferedImage.getRaster().setDataElements() used, seems to have better performance on Windows (HW acceleration)
0077:
0078:            // --- Legend-related variables
0079:            private static final int HORIZONTAL_LEGEND_MARGIN = 5; // margin between component left/right side and clipping area for horizontal axis legend
0080:            private static final double minimumVisibleDataWidthRel = 0.1d;
0081:            private static final long minimumOptimalUnits = 100;
0082:            private static final double maximumZoom = DateTimeAxisUtils
0083:                    .getMaximumScale(minimumOptimalUnits);
0084:
0085:            //~ Instance fields ----------------------------------------------------------------------------------------------------------
0086:
0087:            // --- Variables -------------------------------------------------------------
0088:            private AccessibleContext accessibleContext; // Accessibility support
0089:
0090:            //private VolatileImage offScreenImage;                       // volatile offscreen image buffer (alternative to BufferedImage)
0091:            private BufferedImage offScreenImage; // offscreen image buffer
0092:            private Color evenSelectionSegmentsColor; // color of even segments of selection boundary
0093:            private Color limitYColor = Color.WHITE;
0094:            private Color oddSelectionSegmentColor; // color of odd segments of selection boundary
0095:            private Font horizontalAxisFont;
0096:            private Font horizontalAxisFontSmall;
0097:            private Font verticalAxisFont;
0098:            private Graphics2D offScreenGraphics; // graphics instance of the offscreen image buffer (= offScreenImage.getGraphics())
0099:            private Insets chartInsets; // chart insets
0100:            private Insets insets; // component insets (=getInsets())
0101:            private JScrollBar scrollBar; // JScrollBar attached to the chart
0102:            private Paint backgroundPaint; // paint of the component
0103:            private Paint chartPaint; // paint of the chart area
0104:            private Paint horizontalAxisPaint;
0105:            private Paint horizontalMeshPaint;
0106:            private Paint verticalAxisPaint;
0107:            private Paint verticalMeshPaint;
0108:            private Rectangle horizontalAxisClip = new Rectangle();
0109:            private Rectangle horizontalAxisMarksClip = new Rectangle();
0110:            private Rectangle verticalAxisClip = new Rectangle();
0111:            private Rectangle verticalAxisClip2 = new Rectangle();
0112:            private String verticalAxisValueString;
0113:            private String verticalAxisValueString2;
0114:            private Stroke chartStroke; // stroke used for drawing chart items
0115:            private Stroke evenSelectionSegmentsStroke; // stroke of even segments of selection boundary
0116:            private Stroke horizontalAxisStroke;
0117:            private Stroke horizontalMeshStroke;
0118:            private Stroke oddSelectionSegmentStroke; // stroke of odd segments of selection boundary
0119:            private Stroke verticalAxisStroke;
0120:            private Stroke verticalMeshStroke;
0121:            private SynchronousXYChartModel model; // chart model
0122:
0123:            // --- ChartActionProducer variables
0124:            private Vector chartActionListeners;
0125:            private long[] dataOffsetsY; // first Y-values for each series
0126:            private long[] lastMaxYs; // last Y-value maximums for each series
0127:            private long[] lastMinYs; // last Y-value minimums for each series
0128:            private double[] scaleFactorsY; // vertical scale factors for each series
0129:            private boolean allowSelection; // allow selection in chart?
0130:            private boolean autoTrackingEnd; // should viewing mode be automatically switched to tracking end when scrollbar reaches the right end?
0131:
0132:            // --- Initial appearance (without any data) support
0133:            private boolean customizedEmptyAppearance = false;
0134:            private boolean fitToWindow; // is "fit to window" mode currently active?
0135:            private boolean internalScrollBarChange; // are scrollbar values changed due to chart change?
0136:            private boolean lastLeadingItemIsForBuffer; // index of first visible item contains some offset from optimized algorithm, not valid for non-optimized algorithms
0137:            private boolean lastScaleXValid; // did X-axis scale changed since last iteration (either scaleFactorX changed or component resized)?
0138:            private boolean lastScaleYValid; // did any of the Y-axis scales changed since last iteration (either any scaleFactorsY changed or component resized)
0139:            private boolean lastTrailingItemIndexValid; // is value of lastTrailingItemIndex from the last iteration still valid?
0140:            private boolean lastViewOffsetXValid; // is value of viewOffsetX from the last iteration still valid?
0141:            private boolean mouseInProgress; // is the selection currently being defined by the user? (mouse was pressed and not yet released)
0142:            private boolean offScreenImageInvalid; // does the offscreen image need to be repainted?
0143:            private boolean scaleFactorsNeedUpdate; // do the data scale factors need to be updated?
0144:            private boolean scrollBarValuesDirty; // should scrollbar values be updated to current values?
0145:            private boolean selectionTracksMovement; // should left side of selection track data movement when tracking end?
0146:            private boolean trackingEnd; // is "tracking end" mode currently active?
0147:            private boolean trailingItemVisible; // has the chart to be repainted due to some data-tail change in visible area?
0148:            private boolean useDayInTimeLegend;
0149:            private boolean useSecondaryVerticalAxis;
0150:            private boolean verticalAxisValueAdaptDivider;
0151:            private boolean verticalAxisValueAdaptDivider2;
0152:
0153:            // --- Telemetry Overview workaround
0154:            private double dataWidthAtTrackingEndSwitch; // actual "width" of data when switching from fit to window to tracking end mode and chartWidth is still 0
0155:            private double initialZoom;
0156:            private double scaleFactorX; // horizontal scale factor (== viewScaleX)
0157:            private double scrollBarLongToIntFactor; // scale factor mapping chart (long) data to scrollbar (int) values
0158:            private double viewScaleX; // current scale for X-axis
0159:            private int chartHeight; // height of the chart area
0160:            private int chartWidth; // width of the chart area
0161:
0162:            // --- Copy acceleration support (generic / HW raster)
0163:            private int copyAccel = COPY_ACCEL_GENERIC;
0164:            private int dataType; // data type (interpolated / discrete)
0165:            private int drawHeight; // height of the offscreen image buffer
0166:            private int drawWidth; // width of the offscreen image buffer
0167:            private int lastLeadingItemIndex; // index of first visible item from the last iteration
0168:            private int lastTrailingItemIndex; // index of last visible item used when trailingItemVisible = true
0169:            private int minimumVerticalMarksDistance;
0170:            private int selectionHeight; // selection height
0171:            private int selectionWidth; // selection width
0172:            private int selectionX; // x-coordinate of the selection start
0173:            private int selectionY; // y-coordinate of the selection start
0174:            private int topChartMargin; // extra space between maximum value and top of the chart
0175:            private int type; // chart type (line segments / filled segments)
0176:            private int verticalAxisValueDivider;
0177:            private int verticalAxisValueDivider2;
0178:            private long dataOffsetX; // first X-value
0179:            private long dataViewWidth; // width of all the data in chart(component) coordinate units according to current viewScaleX value
0180:            private long firstValueH;
0181:            private long firstValueV;
0182:            private long lastMaxY; // last global Y-value maximum
0183:            private long lastMinY; // last global Y-value minimum
0184:            private long lastValueH;
0185:            private long lastValueV;
0186:            private long lastViewOffsetX; // value of viewOffsetX from the last iteration
0187:            private long limitYValue = Long.MAX_VALUE;
0188:            private long optimalUnits;
0189:            private long viewOffsetX;
0190:
0191:            //~ Constructors -------------------------------------------------------------------------------------------------------------
0192:
0193:            // --- Constructors ----------------------------------------------------------
0194:
0195:            /** Creates a new instance of SynchronousXYChart */
0196:            public SynchronousXYChart() {
0197:                this (TYPE_FILL);
0198:            }
0199:
0200:            /** Creates a new instance of SynchronousXYChart */
0201:            public SynchronousXYChart(int type) {
0202:                this (type, VALUES_INTERPOLATED);
0203:            }
0204:
0205:            public SynchronousXYChart(int type, int dataType) {
0206:                this (type, dataType, 1.0d);
0207:            }
0208:
0209:            public SynchronousXYChart(int type, int dataType, double initialZoom) {
0210:                this .type = type;
0211:                this .dataType = dataType;
0212:
0213:                allowSelection = false;
0214:
0215:                chartInsets = new Insets(10, 20, 10, 20);
0216:
0217:                topChartMargin = 20;
0218:
0219:                backgroundPaint = UIManager.getColor("Panel.background"); // NOI18N
0220:
0221:                chartPaint = Color.WHITE;
0222:                chartStroke = new BasicStroke(2);
0223:
0224:                mouseInProgress = false;
0225:
0226:                lastViewOffsetXValid = false;
0227:
0228:                lastScaleXValid = false;
0229:                lastScaleYValid = false;
0230:
0231:                lastTrailingItemIndex = 0;
0232:                lastTrailingItemIndexValid = false;
0233:
0234:                changeTrackingEnd(false);
0235:                changeFitToWindow(false);
0236:
0237:                autoTrackingEnd = true;
0238:
0239:                selectionTracksMovement = true;
0240:
0241:                this .initialZoom = initialZoom;
0242:                viewScaleX = initialZoom;
0243:                viewOffsetX = 0;
0244:                //viewScaleX = 10/(double)Integer.MAX_VALUE;
0245:                //changeZoom(0.22898975409836064);
0246:                verticalMeshPaint = new Color(80, 80, 80, 50);
0247:                verticalMeshStroke = new BasicStroke();
0248:                horizontalAxisFont = UIManager.getFont("Panel.font"); // NOI18N
0249:                horizontalAxisFontSmall = horizontalAxisFont
0250:                        .deriveFont((float) (horizontalAxisFont.getSize() - 2));
0251:
0252:                verticalAxisFont = UIManager.getFont("Panel.font"); // NOI18N
0253:
0254:                horizontalAxisPaint = Color.BLACK;
0255:                horizontalAxisStroke = new BasicStroke();
0256:
0257:                verticalAxisPaint = Color.BLACK;
0258:                verticalAxisStroke = new BasicStroke();
0259:
0260:                useSecondaryVerticalAxis = false;
0261:
0262:                verticalAxisValueDivider = 1;
0263:                verticalAxisValueString = ""; // NOI18N
0264:                setVerticalAxisValueAdaptDivider(false);
0265:
0266:                verticalAxisValueDivider2 = 1;
0267:                verticalAxisValueString2 = ""; // NOI18N
0268:                setVerticalAxisValueAdaptDivider2(false);
0269:
0270:                useDayInTimeLegend = false;
0271:
0272:                minimumVerticalMarksDistance = 50;
0273:
0274:                evenSelectionSegmentsColor = Color.WHITE;
0275:                evenSelectionSegmentsStroke = new BasicStroke(1,
0276:                        BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
0277:                        new float[] { 2, 2 }, 0);
0278:
0279:                oddSelectionSegmentColor = Color.BLACK;
0280:                oddSelectionSegmentStroke = new BasicStroke(1,
0281:                        BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0,
0282:                        new float[] { 2, 2 }, 2);
0283:
0284:                addComponentListener(this );
0285:                addMouseListener(this );
0286:                addMouseMotionListener(this );
0287:            }
0288:
0289:            //~ Methods ------------------------------------------------------------------------------------------------------------------
0290:
0291:            // --- Accessibility support -------------------------------------------------
0292:            public void setAccessibleContext(AccessibleContext accessibleContext) {
0293:                this .accessibleContext = accessibleContext;
0294:            }
0295:
0296:            public AccessibleContext getAccessibleContext() {
0297:                return accessibleContext;
0298:            }
0299:
0300:            public void setAutoTrackingEnd(boolean autoTrackingEnd) {
0301:                this .autoTrackingEnd = autoTrackingEnd;
0302:            }
0303:
0304:            public boolean getAutoTrackingEnd() {
0305:                return autoTrackingEnd;
0306:            }
0307:
0308:            // --- Colors % Stroke customization -----------------------------------------
0309:            public void setBackgroundPaint(Paint backgroundPaint) {
0310:                if (((this .backgroundPaint == null) && (backgroundPaint != null))
0311:                        || (!this .backgroundPaint.equals(backgroundPaint))) {
0312:                    this .backgroundPaint = backgroundPaint;
0313:                    doRepaint(false);
0314:                }
0315:            }
0316:
0317:            public Paint getBackgroundPaint() {
0318:                return backgroundPaint;
0319:            }
0320:
0321:            public void setChartPaint(Paint chartPaint) {
0322:                if (((this .chartPaint == null) && (chartPaint != null))
0323:                        || (!this .chartPaint.equals(chartPaint))) {
0324:                    this .chartPaint = chartPaint;
0325:                    doRepaint(false);
0326:                }
0327:            }
0328:
0329:            public Paint getChartPaint() {
0330:                return backgroundPaint;
0331:            }
0332:
0333:            public void setChartStroke(Stroke chartStroke) {
0334:                if (((this .chartStroke == null) && (chartStroke != null))
0335:                        || (!this .chartStroke.equals(chartStroke))) {
0336:                    this .chartStroke = chartStroke;
0337:                    doRepaint(false);
0338:                }
0339:            }
0340:
0341:            public Stroke getChartStroke() {
0342:                return chartStroke;
0343:            }
0344:
0345:            // --- Copy acceleration support (generic / HW raster) -----------------------
0346:            public void setCopyAcceleration(int copyAccel) {
0347:                this .copyAccel = copyAccel;
0348:            }
0349:
0350:            public int getCopyAcceleration() {
0351:                return copyAccel;
0352:            }
0353:
0354:            // conversion of data Y interval from data coordinates to chart coordinates
0355:            public long getDataToViewHeight(long height, int seriesIndex) {
0356:                return (long) Math.ceil(height * scaleFactorsY[seriesIndex]);
0357:            }
0358:
0359:            // conversion of data X interval from data coordinates to chart coordinates
0360:            public long getDataToViewWidth(long width) {
0361:                return (long) Math.ceil(width * scaleFactorX);
0362:            }
0363:
0364:            // conversion of X value from data coordinates to chart coordinates
0365:            public long getDataToViewX(long xValue) {
0366:                return (long) Math
0367:                        .ceil((((xValue - dataOffsetX) * scaleFactorX) + chartInsets.left)
0368:                                - viewOffsetX); //
0369:            }
0370:
0371:            // conversion of Y value from data coordinates to chart coordinates
0372:            public long getDataToViewY(long yValue, int seriesIndex) {
0373:                return chartHeight
0374:                        - (long) Math
0375:                                .ceil(((yValue - dataOffsetsY[seriesIndex]) * scaleFactorsY[seriesIndex])
0376:                                        - chartInsets.top);
0377:            }
0378:
0379:            public void setFitToWindow(boolean fitToWindow) {
0380:                if (fitToWindow) {
0381:                    setFitToWindow();
0382:                } else {
0383:                    resetFitToWindow();
0384:                }
0385:            }
0386:
0387:            public void setFitToWindow() {
0388:                if (!fitToWindow) {
0389:                    changeFitToWindow(true);
0390:                    changeTrackingEnd(false);
0391:                    lastViewOffsetXValid = false;
0392:                    lastScaleXValid = false;
0393:                    offScreenImageInvalid = true;
0394:                    doRepaint(true);
0395:                }
0396:            }
0397:
0398:            public boolean isFitToWindow() {
0399:                return fitToWindow;
0400:            }
0401:
0402:            public void setHorizontalAxisFont(Font horizontalAxisFont) {
0403:                if ((horizontalAxisFont != null)
0404:                        && !this .horizontalAxisFont.equals(horizontalAxisFont)) {
0405:                    this .horizontalAxisFont = horizontalAxisFont;
0406:                    horizontalAxisFontSmall = horizontalAxisFont
0407:                            .deriveFont((float) (horizontalAxisFont.getSize() - 2));
0408:
0409:                    // TODO: repaint
0410:                }
0411:            }
0412:
0413:            public Font getHorizontalAxisFont() {
0414:                return horizontalAxisFont;
0415:            }
0416:
0417:            public void setHorizontalMeshPaint(Paint horizontalMeshPaint) {
0418:                if ((horizontalMeshPaint != null)
0419:                        && !this .horizontalMeshPaint
0420:                                .equals(horizontalMeshPaint)) {
0421:                    this .horizontalMeshPaint = horizontalMeshPaint;
0422:
0423:                    // TODO: repaint
0424:                }
0425:            }
0426:
0427:            public Paint getHorizontalMeshPaint() {
0428:                return horizontalMeshPaint;
0429:            }
0430:
0431:            public void setHorizontalMeshStroke(Stroke horizontalMeshStroke) {
0432:                if ((horizontalMeshStroke != null)
0433:                        && !this .horizontalMeshStroke
0434:                                .equals(horizontalMeshStroke)) {
0435:                    this .horizontalMeshStroke = horizontalMeshStroke;
0436:
0437:                    // TODO: repaint
0438:                }
0439:            }
0440:
0441:            public Stroke getHorizontalMeshStroke() {
0442:                return horizontalMeshStroke;
0443:            }
0444:
0445:            // scrollbar block increment mapped to (int)
0446:            public int getIntBlockIncrement() {
0447:                return (int) ((chartWidth - 20) * scrollBarLongToIntFactor);
0448:            }
0449:
0450:            // current chart width mapped to (int)
0451:            public int getIntExtent() {
0452:                return (int) (getRealExtent() * scrollBarLongToIntFactor);
0453:            }
0454:
0455:            // data view width mapped to (int)
0456:            public int getIntMaximum() {
0457:                return (int) (getRealMaximum() * scrollBarLongToIntFactor);
0458:            }
0459:
0460:            // minimum (starting) position mapped to (int)
0461:            public int getIntMinimum() {
0462:                return 0;
0463:            }
0464:
0465:            // current view offset mapped to (int)
0466:            public int getIntPosition() {
0467:                return (int) (getRealPosition() * scrollBarLongToIntFactor);
0468:            }
0469:
0470:            // scrollbar unit increment mapped to (int)
0471:            public int getIntUnitIncrement() {
0472:                return (int) (20 * scrollBarLongToIntFactor);
0473:            }
0474:
0475:            public int getLeadingItemIndexForPosition(int x) {
0476:                if ((model == null) || (model.getItemCount() < 2)) {
0477:                    return -1;
0478:                }
0479:
0480:                long timeAtPosition = (long) getXValueAtPosition(x);
0481:
0482:                int itemIndex = lastLeadingItemIndex;
0483:
0484:                if (model.getXValue(itemIndex) == timeAtPosition) {
0485:                    return itemIndex;
0486:                }
0487:
0488:                if (model.getXValue(itemIndex) < timeAtPosition) {
0489:                    // searching forward (most probably)
0490:                    for (int i = itemIndex; i < (model.getItemCount() - 1); i++) {
0491:                        if (model.getXValue(i + 1) > timeAtPosition) {
0492:                            return i;
0493:                        }
0494:                    }
0495:
0496:                    return -1;
0497:                } else {
0498:                    // searching backward
0499:                    for (int i = itemIndex; i >= 0; i--) {
0500:                        if (model.getXValue(i) < timeAtPosition) {
0501:                            return i;
0502:                        }
0503:                    }
0504:
0505:                    return -1;
0506:                }
0507:            }
0508:
0509:            public double getMaximumZoom() {
0510:                return maximumZoom;
0511:            }
0512:
0513:            public boolean isMaximumZoom() {
0514:                return viewScaleX >= maximumZoom;
0515:            }
0516:
0517:            public void setMinimumVerticalMarksDistance(
0518:                    int minimumVerticalMarksDistance) {
0519:                this .minimumVerticalMarksDistance = minimumVerticalMarksDistance;
0520:            }
0521:
0522:            public int getMinimumVerticalMarksDistance() {
0523:                return minimumVerticalMarksDistance;
0524:            }
0525:
0526:            public double getMinimumZoom() {
0527:                if (model == null) {
0528:                    return 0;
0529:                }
0530:
0531:                return Math.min(initialZoom,
0532:                        (double) (chartWidth * minimumVisibleDataWidthRel)
0533:                                / (double) (model.getMaxXValue() - model
0534:                                        .getMinXValue()));
0535:            }
0536:
0537:            // --- Scaling support -------------------------------------------------------
0538:            public boolean isMinimumZoom() {
0539:                return viewScaleX <= getMinimumZoom();
0540:            }
0541:
0542:            // --- SynchronousXYChartModel stuff -----------------------------------------
0543:            public void setModel(SynchronousXYChartModel model) {
0544:                // automatically unregister itself as a ChartModelListener from current model
0545:                if (this .model != null) {
0546:                    this .model.removeChartModelListener(this );
0547:                }
0548:
0549:                // automatically register itself as a ChartModelListener for new model
0550:                if (model != null) {
0551:                    model.addChartModelListener(this );
0552:                }
0553:
0554:                this .model = model;
0555:
0556:                lastMinYs = new long[model.getSeriesCount()];
0557:                lastMaxYs = new long[model.getSeriesCount()];
0558:
0559:                dataOffsetsY = new long[model.getSeriesCount()];
0560:                scaleFactorsY = new double[model.getSeriesCount()];
0561:
0562:                offScreenImageInvalid = true;
0563:                doRepaint(true);
0564:            }
0565:
0566:            public SynchronousXYChartModel getModel() {
0567:                return model;
0568:            }
0569:
0570:            // --- ToolTip support -------------------------------------------------------
0571:            public boolean isOverChart(Point point) {
0572:                return isOverChart(point.x, point.y);
0573:            }
0574:
0575:            public boolean isOverChart(int x, int y) {
0576:                Insets componentInsets = getInsets();
0577:
0578:                return ((x >= (componentInsets.left + chartInsets.left))
0579:                        && (x <= (componentInsets.left + chartInsets.left + chartWidth))
0580:                        && (y >= (componentInsets.top + chartInsets.top)) && (y <= (componentInsets.top
0581:                        + chartInsets.top + chartHeight)));
0582:            }
0583:
0584:            // current chart width in chart coordinates
0585:            public long getRealExtent() {
0586:                return chartWidth;
0587:            }
0588:
0589:            // data view width in chart coordinates
0590:            public long getRealMaximum() {
0591:                return dataViewWidth;
0592:            }
0593:
0594:            // --- Scrolling support -----------------------------------------------------
0595:
0596:            // minimum (starting) position in chart coordinates
0597:            public long getRealMinimum() {
0598:                return 0;
0599:            }
0600:
0601:            // current view offset in chart coordinates
0602:            public long getRealPosition() {
0603:                return viewOffsetX;
0604:            }
0605:
0606:            public void setScale(double viewScaleX) {
0607:                if (!fitToWindow) {
0608:                    if (isMinimumZoom() && (viewScaleX < getMinimumZoom())) {
0609:                        return;
0610:                    }
0611:
0612:                    if (isMaximumZoom() && (viewScaleX > getMaximumZoom())) {
0613:                        return;
0614:                    }
0615:                }
0616:
0617:                if (this .viewScaleX != viewScaleX) {
0618:                    double dataX = getViewToDataApproxX(chartInsets.left);
0619:                    changeZoom(viewScaleX);
0620:                    changeFitToWindow(false);
0621:                    changePan(Math.min((long) (((model.getMaxXValue() - model
0622:                            .getMinXValue()) * this .viewScaleX) - chartWidth),
0623:                            (long) ((dataX - dataOffsetX) * this .viewScaleX)));
0624:                    lastViewOffsetXValid = false;
0625:                    lastScaleXValid = false;
0626:                    offScreenImageInvalid = true;
0627:                    doRepaint(true);
0628:                } else {
0629:                    repaint();
0630:                }
0631:            }
0632:
0633:            public double getScale() {
0634:                return viewScaleX;
0635:            }
0636:
0637:            public void setScaleAndOffsetX(double viewScaleX, long viewOffsetX) {
0638:                if ((this .viewScaleX != viewScaleX)
0639:                        || (this .viewOffsetX != viewOffsetX)) {
0640:                    changeZoom(viewScaleX);
0641:                    changePan(viewOffsetX);
0642:                    changeTrackingEnd(false);
0643:                    changeFitToWindow(false);
0644:                    lastViewOffsetXValid = false;
0645:                    lastScaleXValid = false;
0646:                    offScreenImageInvalid = true;
0647:                    doRepaint(true);
0648:                }
0649:            }
0650:
0651:            public boolean isSelectionAllowed() {
0652:                return allowSelection;
0653:            }
0654:
0655:            public void setSelectionTracksMovement(
0656:                    boolean selectionTracksMovement) {
0657:                this .selectionTracksMovement = selectionTracksMovement;
0658:            }
0659:
0660:            public boolean getSelectionTracksMovement() {
0661:                return selectionTracksMovement;
0662:            }
0663:
0664:            public String getTimeAtPosition(int x) {
0665:                if ((model == null) || (model.getItemCount() < 2)) {
0666:                    return null;
0667:                }
0668:
0669:                if (!useDayInTimeLegend) {
0670:                    return DateTimeAxisUtils.getMillisValue(
0671:                            (long) getXValueAtPosition(x), false);
0672:                }
0673:
0674:                return DateTimeAxisUtils
0675:                        .getMillisValueFull((long) getXValueAtPosition(x));
0676:            }
0677:
0678:            // --- Legend customization --------------------------------------------------
0679:            public void setTopChartMargin(int topChartMargin) {
0680:                if (this .topChartMargin != topChartMargin) {
0681:                    this .topChartMargin = topChartMargin;
0682:
0683:                    // TODO: repaint
0684:                }
0685:            }
0686:
0687:            public int getTopChartMargin() {
0688:                return topChartMargin;
0689:            }
0690:
0691:            public void setTrackingEnd(double viewScaleX) {
0692:                if (this .viewScaleX != viewScaleX) {
0693:                    changeZoom(viewScaleX);
0694:                    lastScaleXValid = false;
0695:                }
0696:
0697:                setTrackingEnd();
0698:            }
0699:
0700:            public void setTrackingEnd(boolean trackingEnd) {
0701:                if (trackingEnd) {
0702:                    setTrackingEnd();
0703:                } else {
0704:                    resetTrackingEnd();
0705:                }
0706:            }
0707:
0708:            public void setTrackingEnd() {
0709:                if (!trackingEnd) {
0710:                    changeTrackingEnd(!trailingItemVisible || fitToWindow);
0711:                    lastViewOffsetXValid = false;
0712:
0713:                    if (fitToWindow && (viewScaleX == 0)) {
0714:                        dataWidthAtTrackingEndSwitch = (double) (model
0715:                                .getMaxXValue() - dataOffsetX); // workaround for Telemetry Overview initialization
0716:                    }
0717:
0718:                    changeFitToWindow(false);
0719:                    offScreenImageInvalid = true;
0720:                    doRepaint(true);
0721:                }
0722:            }
0723:
0724:            public boolean isTrackingEnd() {
0725:                return trackingEnd;
0726:            }
0727:
0728:            public void setUseSecondaryVerticalAxis(
0729:                    boolean useSecondaryVerticalAxis) {
0730:                this .useSecondaryVerticalAxis = useSecondaryVerticalAxis;
0731:                updateOffScreenImageSize();
0732:            }
0733:
0734:            public boolean getUseSecondaryVerticalAxis() {
0735:                return useSecondaryVerticalAxis;
0736:            }
0737:
0738:            public void setVerticalAxisFont(Font verticalAxisFont) {
0739:                if ((verticalAxisFont != null)
0740:                        && !this .verticalAxisFont.equals(verticalAxisFont)) {
0741:                    this .verticalAxisFont = verticalAxisFont;
0742:
0743:                    //verticalAxisFontSmall = verticalAxisFont.deriveFont((float)(verticalAxisFont.getSize() - 2));
0744:                    // TODO: repaint
0745:                }
0746:            }
0747:
0748:            public Font getVerticalAxisFont() {
0749:                return verticalAxisFont;
0750:            }
0751:
0752:            public void setVerticalAxisValueDivider(int verticalAxisValueDivider) {
0753:                this .verticalAxisValueDivider = verticalAxisValueDivider;
0754:
0755:                // TODO: check chart margins, repaint
0756:            }
0757:
0758:            public int getVerticalAxisValueDivider() {
0759:                return verticalAxisValueDivider;
0760:            }
0761:
0762:            public void setVerticalAxisValueDivider2(
0763:                    int verticalAxisValueDivider2) {
0764:                this .verticalAxisValueDivider2 = verticalAxisValueDivider2;
0765:
0766:                // TODO: check chart margins, repaint
0767:            }
0768:
0769:            public int getVerticalAxisValueDivider2() {
0770:                return verticalAxisValueDivider2;
0771:            }
0772:
0773:            public void setVerticalAxisValueString(
0774:                    String verticalAxisValueString) {
0775:                if (verticalAxisValueString == null) {
0776:                    this .verticalAxisValueString = ""; // NOI18N
0777:                } else {
0778:                    this .verticalAxisValueString = verticalAxisValueString;
0779:                }
0780:
0781:                // TODO: check chart margins, repaint
0782:            }
0783:
0784:            public String getVerticalAxisValueString() {
0785:                return verticalAxisValueString;
0786:            }
0787:
0788:            public void setVerticalAxisValueString2(
0789:                    String verticalAxisValueString2) {
0790:                if (verticalAxisValueString2 == null) {
0791:                    this .verticalAxisValueString2 = ""; // NOI18N
0792:                } else {
0793:                    this .verticalAxisValueString2 = verticalAxisValueString2;
0794:                }
0795:
0796:                // TODO: check chart margins, repaint
0797:            }
0798:
0799:            public String getVerticalAxisValueString2() {
0800:                return verticalAxisValueString2;
0801:            }
0802:
0803:            public void setVerticalMeshPaint(Paint verticalMeshPaint) {
0804:                if ((verticalMeshPaint != null)
0805:                        && !this .verticalMeshPaint.equals(verticalMeshPaint)) {
0806:                    this .verticalMeshPaint = verticalMeshPaint;
0807:
0808:                    // TODO: repaint
0809:                }
0810:            }
0811:
0812:            public Paint getVerticalMeshPaint() {
0813:                return verticalMeshPaint;
0814:            }
0815:
0816:            public void setVerticalMeshStroke(Stroke verticalMeshStroke) {
0817:                if ((verticalMeshStroke != null)
0818:                        && !this .verticalMeshStroke.equals(verticalMeshStroke)) {
0819:                    this .verticalMeshStroke = verticalMeshStroke;
0820:
0821:                    // TODO: repaint
0822:                }
0823:            }
0824:
0825:            public Stroke getVerticalMeshStroke() {
0826:                return verticalMeshStroke;
0827:            }
0828:
0829:            public void setViewOffsetX(long viewOffsetX) {
0830:                if (this .viewOffsetX != viewOffsetX) {
0831:                    changePan(viewOffsetX);
0832:                    changeTrackingEnd(false);
0833:                    changeFitToWindow(false);
0834:                    lastViewOffsetXValid = false;
0835:                    offScreenImageInvalid = true;
0836:                    doRepaint(true);
0837:                }
0838:            }
0839:
0840:            public long getViewOffsetX() {
0841:                return viewOffsetX;
0842:            }
0843:
0844:            // conversion of data Y interval from chart coordinates to data coordinates
0845:            public double getViewToDataApproxHeight(long height, int seriesIndex) {
0846:                // TODO, currently not supported
0847:                return 0;
0848:            }
0849:
0850:            // conversion of data X interval from chart coordinates to data coordinates
0851:            public double getViewToDataApproxWidth(long width) {
0852:                return width / scaleFactorX;
0853:            }
0854:
0855:            // conversion of X value from chart coordinates to data coordinates
0856:            public double getViewToDataApproxX(long xValue) {
0857:                return ((xValue - chartInsets.left + viewOffsetX) / scaleFactorX)
0858:                        + dataOffsetX;
0859:            }
0860:
0861:            // conversion of Y value from chart coordinates to data coordinates
0862:            public double getViewToDataApproxY(long yValue, int seriesIndex) {
0863:                // TODO, currently not supported
0864:                return 0;
0865:            }
0866:
0867:            public boolean isWithinData(int x) {
0868:                if ((model == null) || (model.getItemCount() < 2)) {
0869:                    return false;
0870:                }
0871:
0872:                int leadingItemIndex = getLeadingItemIndexForPosition(x);
0873:
0874:                if (leadingItemIndex == -1) {
0875:                    return false;
0876:                }
0877:
0878:                return ((leadingItemIndex >= 0) && (leadingItemIndex < model
0879:                        .getItemCount()));
0880:            }
0881:
0882:            public long getYValueAtPosition(int x, int seriesIndex) {
0883:                if ((model == null) || (model.getItemCount() < 2)) {
0884:                    return -1;
0885:                }
0886:
0887:                double positionTime = getXValueAtPosition(x);
0888:
0889:                int leadingItemIndex = getLeadingItemIndexForPosition(x);
0890:                int trailingItemIndex = leadingItemIndex + 1;
0891:
0892:                if (trailingItemIndex == model.getItemCount()) {
0893:                    return -1;
0894:                }
0895:
0896:                long leadingItemValue = model.getYValue(leadingItemIndex,
0897:                        seriesIndex);
0898:
0899:                if (dataType == VALUES_DISCRETE) {
0900:                    return leadingItemValue;
0901:                }
0902:
0903:                long trailingItemValue = model.getYValue(trailingItemIndex,
0904:                        seriesIndex);
0905:
0906:                long leadingItemTime = model.getXValue(leadingItemIndex);
0907:                long trailingItemTime = model.getXValue(trailingItemIndex);
0908:
0909:                double interpolationFactor = (double) (positionTime - leadingItemTime)
0910:                        / (double) (trailingItemTime - leadingItemTime);
0911:
0912:                return (long) ((trailingItemValue - leadingItemValue) * interpolationFactor)
0913:                        + leadingItemValue;
0914:            }
0915:
0916:            // --- ChartActionProducer stuff ---------------------------------------------
0917:
0918:            /**
0919:             * Adds new chartActionListener.
0920:             * @param chartActionListener chartActionListener to add
0921:             */
0922:            public synchronized void addChartActionListener(
0923:                    ChartActionListener chartActionListener) {
0924:                if (chartActionListeners == null) {
0925:                    chartActionListeners = new Vector();
0926:                }
0927:
0928:                if (!chartActionListeners.contains(chartActionListener)) {
0929:                    chartActionListeners.add(chartActionListener);
0930:                }
0931:            }
0932:
0933:            public void adjustmentValueChanged(AdjustmentEvent e) {
0934:                if (internalScrollBarChange) {
0935:                    internalScrollBarChange = false;
0936:                } else {
0937:                    if (autoTrackingEnd
0938:                            && ((e.getValue() + scrollBar.getModel()
0939:                                    .getExtent()) == scrollBar.getMaximum())) {
0940:                        setTrackingEnd();
0941:                    } else {
0942:                        setViewOffsetX((long) (e.getValue() / scrollBarLongToIntFactor));
0943:
0944:                        if (!scrollBar.getValueIsAdjusting()
0945:                                && scrollBarValuesDirty) {
0946:                            scrollBarValuesDirty = false;
0947:                            updateScrollBarValues();
0948:                        }
0949:                    }
0950:                }
0951:            }
0952:
0953:            // --- Selection stuff -------------------------------------------------------
0954:            public void allowSelection() {
0955:                allowSelection = true;
0956:            }
0957:
0958:            // --- JScrollBar support stuff ----------------------------------------------
0959:            public void associateJScrollBar(JScrollBar scrollBar) {
0960:                deassociateJScrollBar();
0961:
0962:                if (scrollBar != null) {
0963:                    this .scrollBar = scrollBar;
0964:                    this .scrollBar.addAdjustmentListener(this );
0965:                    updateScrollBarValues();
0966:                }
0967:            }
0968:
0969:            // Used for public chart update & listener implementation
0970:            public void chartDataChanged() {
0971:                limitYValue = model.getLimitYValue();
0972:                limitYColor = model.getLimitYColor();
0973:
0974:                updateScaleFactors();
0975:
0976:                fireChartDataChanged();
0977:
0978:                if (isShowing()) {
0979:                    if (trackingEnd && (scrollBar != null)
0980:                            && scrollBar.getValueIsAdjusting()) {
0981:                        return;
0982:                    }
0983:
0984:                    checkChartMargins();
0985:
0986:                    if (trackingEnd || fitToWindow || trailingItemVisible
0987:                            || !lastScaleYValid) {
0988:                        offScreenImageInvalid = true;
0989:                        doRepaint(false);
0990:                    }
0991:                } else {
0992:                    lastViewOffsetXValid = false;
0993:                    lastScaleXValid = false;
0994:                    lastScaleYValid = false;
0995:                    lastTrailingItemIndexValid = false;
0996:                    lastLeadingItemIsForBuffer = false;
0997:                    lastLeadingItemIndex = 0;
0998:                    trailingItemVisible = true;
0999:                    scrollBarValuesDirty = true;
1000:                    offScreenImageInvalid = true;
1001:
1002:                    if (trackingEnd) {
1003:                        viewOffsetX = -chartWidth + dataViewWidth;
1004:                    }
1005:
1006:                    if (!fitToWindow && (model.getItemCount() > 1)) {
1007:                        updateTrailingItemVisible();
1008:                    }
1009:                }
1010:            }
1011:
1012:            // --- ComponentListener implementation --------------------------------------
1013:            public void componentHidden(ComponentEvent e) {
1014:            }
1015:
1016:            public void componentMoved(ComponentEvent e) {
1017:            }
1018:
1019:            public void componentResized(ComponentEvent e) {
1020:                updateOffScreenImageSize();
1021:            }
1022:
1023:            public void componentShown(ComponentEvent e) {
1024:            }
1025:
1026:            public boolean containsValidData() {
1027:                return ((model != null) && (model.getItemCount() > 1));
1028:            }
1029:
1030:            public void deassociateJScrollBar() {
1031:                if (scrollBar != null) {
1032:                    scrollBar.removeAdjustmentListener(this );
1033:                }
1034:            }
1035:
1036:            public void denySelection() {
1037:                allowSelection = false;
1038:            }
1039:
1040:            // sets zoom & offset according to provided coordinates
1041:            public void fitToViewRectangle(int viewX, int viewY, int viewWidth,
1042:                    int viewHeight) {
1043:                double dataX = getViewToDataApproxX(viewX - getInsets().left);
1044:                double dataWidth = getViewToDataApproxWidth(viewWidth + 1);
1045:
1046:                double newScaleX = chartWidth / dataWidth;
1047:
1048:                if (isMaximumZoom() && (newScaleX > maximumZoom)) {
1049:                    repaint();
1050:
1051:                    return;
1052:                }
1053:
1054:                changeZoom(newScaleX);
1055:                changeFitToWindow(false);
1056:                changePan((long) ((dataX - dataOffsetX) * viewScaleX));
1057:                trackingEnd = false;
1058:                lastViewOffsetXValid = false;
1059:                lastScaleXValid = false;
1060:                offScreenImageInvalid = true;
1061:                doRepaint(true);
1062:            }
1063:
1064:            public boolean hasValidDataForPosition(Point point) {
1065:                return hasValidDataForPosition(point.x, point.y);
1066:            }
1067:
1068:            public boolean hasValidDataForPosition(int x, int y) {
1069:                return isOverChart(x, y) && isWithinData(x);
1070:            }
1071:
1072:            // --- Main (Tester Frame) ---------------------------------------------------
1073:            public static void main(String[] args) {
1074:                SynchronousXYChart xyChart = new SynchronousXYChart(
1075:                        SynchronousXYChart.TYPE_FILL);
1076:
1077:                xyChart.setBorder(BorderFactory.createEmptyBorder(15, 20, 35,
1078:                        20));
1079:                //xyChart.setBackgroundColor(Color.WHITE);
1080:                //xyChart.setFitToWindow();
1081:                //xyChart.setTrackingEnd();
1082:                xyChart.setPreferredSize(new Dimension(600, 400));
1083:
1084:                DynamicSynchronousXYChartModel xyChartModel = new DynamicSynchronousXYChartModel();
1085:                xyChartModel.setupModel(new String[] { "Item 1", "Item 2",
1086:                        "Item 3" }, // NOI18N
1087:                        new Color[] { Color.RED, Color.GREEN, Color.BLUE });
1088:
1089:                JFrame frame = new JFrame("SynchronousXYChart Tester"); // NOI18N
1090:                frame.getContentPane().setLayout(new BorderLayout());
1091:                frame.getContentPane().add(xyChart, BorderLayout.CENTER);
1092:
1093:                JScrollBar scrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
1094:                xyChart.associateJScrollBar(scrollBar);
1095:
1096:                frame.getContentPane().add(scrollBar, BorderLayout.SOUTH);
1097:                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
1098:                frame.pack();
1099:                frame.setVisible(true);
1100:
1101:                xyChartModel.addItemValues(00, new long[] { 30, 20, 10 });
1102:                xyChartModel.addItemValues(05, new long[] { 45, 38, 20 });
1103:                //for (int i = 0; i < 3000000; i++) Math.sin(i);
1104:                xyChartModel.addItemValues(20, new long[] { 62, 61, 30 });
1105:                //for (int i = 0; i < 3000000; i++) Math.sin(i);
1106:                xyChartModel.addItemValues(50, new long[] { 90, 80, 48 });
1107:
1108:                xyChart.setModel(xyChartModel);
1109:            }
1110:
1111:            public void setVerticalAxisValueAdaptDivider(
1112:                    boolean verticalAxisValueAdaptDivider) {
1113:                this .verticalAxisValueAdaptDivider = verticalAxisValueAdaptDivider;
1114:            }
1115:
1116:            public boolean isVerticalAxisValueAdaptDivider() {
1117:                return verticalAxisValueAdaptDivider;
1118:            }
1119:
1120:            public void setVerticalAxisValueAdaptDivider2(
1121:                    boolean verticalAxisValueAdaptDivider2) {
1122:                this .verticalAxisValueAdaptDivider2 = verticalAxisValueAdaptDivider2;
1123:            }
1124:
1125:            public boolean isVerticalAxisValueAdaptDivider2() {
1126:                return verticalAxisValueAdaptDivider2;
1127:            }
1128:
1129:            // --- MouseListener & MouseMotionListener implementation --------------------
1130:            public void mouseClicked(MouseEvent e) {
1131:            }
1132:
1133:            public void mouseDragged(MouseEvent e) {
1134:                if (allowSelection && mouseInProgress) {
1135:                    int mouseX = Math.min((insets.left + drawWidth)
1136:                            - chartInsets.right - 1, e.getX());
1137:
1138:                    if (trailingItemVisible) {
1139:                        mouseX = Math.min(insets.left
1140:                                + (int) getDataToViewX(model.getXValue(model
1141:                                        .getItemCount() - 1)), mouseX);
1142:                    }
1143:
1144:                    int mouseY = Math.min((insets.top + drawHeight)
1145:                            - chartInsets.bottom, e.getY());
1146:
1147:                    selectionWidth = mouseX - selectionX;
1148:                    selectionHeight = mouseY - selectionY - 1;
1149:
1150:                    doRepaint(false);
1151:                }
1152:            }
1153:
1154:            public void mouseEntered(MouseEvent e) {
1155:            }
1156:
1157:            public void mouseExited(MouseEvent e) {
1158:            }
1159:
1160:            public void mouseMoved(MouseEvent e) {
1161:            }
1162:
1163:            public void mousePressed(MouseEvent e) {
1164:                if ((model == null) || (model.getItemCount() < 2)) {
1165:                    return;
1166:                }
1167:
1168:                if (allowSelection && (e.getButton() == MouseEvent.BUTTON1)) {
1169:                    int leftOffset = insets.left + chartInsets.left;
1170:                    int topOffset = insets.top + chartInsets.top;
1171:
1172:                    //int mouseX = e.getX();
1173:                    int mouseX = Math.max(leftOffset, e.getX()); // easier selection on the left side
1174:                    int mouseY = e.getY();
1175:
1176:                    // is X coordinate inside chart area?
1177:                    //boolean mouseXInChart = (mouseX >= leftOffset && mouseX < leftOffset + chartWidth);
1178:                    boolean mouseXInChart = (mouseX < (leftOffset + chartWidth));
1179:
1180:                    // is X coordinate on the left of last item?
1181:                    if (trailingItemVisible) {
1182:                        mouseXInChart = (mouseXInChart && (mouseX <= (insets.left + (int) getDataToViewX(model
1183:                                .getXValue(model.getItemCount() - 1)))));
1184:                    }
1185:
1186:                    // is X coordinate on the right of first item?
1187:                    // NOTE: should never happen for Profiler implementation
1188:                    /*if (trackingEnd) {
1189:                       long firstItemX = insets.left + (int)getDataToViewX(model.getXValue(0));
1190:                       if (firstItemX > 0) mouseXInChart = (mouseXInChart && mouseX >= firstItemX);
1191:                       }*/
1192:
1193:                    // is Y coordinate inside chart area?
1194:                    boolean mouseYInChart = ((mouseY >= topOffset) && (mouseY < (topOffset + chartHeight)));
1195:
1196:                    if (mouseXInChart && mouseYInChart) {
1197:                        selectionX = mouseX;
1198:                        selectionWidth = 0;
1199:
1200:                        selectionY = mouseY;
1201:                        selectionHeight = mouseY - selectionY - 1;
1202:
1203:                        mouseInProgress = true;
1204:                        doRepaint(false);
1205:                    }
1206:                }
1207:            }
1208:
1209:            public void mouseReleased(MouseEvent e) {
1210:                if (allowSelection && mouseInProgress) {
1211:                    mouseInProgress = false;
1212:                    performSelectionDone();
1213:                }
1214:            }
1215:
1216:            public void paintComponent(Graphics g) {
1217:                // super.paintComponent
1218:                super .paintComponent(g);
1219:
1220:                //Graphics2D g2 = (Graphics2D) g.create();
1221:
1222:                // check if ChartModel is assigned
1223:                if (model == null) {
1224:                    return;
1225:                }
1226:
1227:                // check if offScreenImage has been created
1228:                if (offScreenImage == null) {
1229:                    updateOffScreenImageSize();
1230:                }
1231:
1232:                // paint component to the offScreenImage
1233:                if (offScreenImageInvalid) {
1234:                    //long startTime = System.currentTimeMillis();
1235:                    //for (int i = 0; i < 200; i++)
1236:                    drawChart(offScreenGraphics);
1237:
1238:                    //long endTime = System.currentTimeMillis();
1239:                    //System.out.println((endTime - startTime));
1240:                    //drawChartAxes(offScreenGraphics);
1241:                }
1242:
1243:                // paint offScreenImage to the output Graphics
1244:                g.drawImage(offScreenImage, insets.left, insets.top, this );
1245:
1246:                // paint current selection area
1247:                if (allowSelection && mouseInProgress) {
1248:                    drawSelection((Graphics2D) g);
1249:                }
1250:            }
1251:
1252:            /**
1253:             * Removes chartActionListener.
1254:             * @param chartActionListener chartActionListener to remove
1255:             */
1256:            public synchronized void removeChartActionListener(
1257:                    ChartActionListener chartActionListener) {
1258:                if (chartActionListeners != null) {
1259:                    chartActionListeners.remove(chartActionListener);
1260:                }
1261:            }
1262:
1263:            public void resetChart() {
1264:                changeTrackingEnd(false);
1265:                changeFitToWindow(false);
1266:                changePan(0);
1267:                changeZoom(initialZoom);
1268:                mouseInProgress = false;
1269:                lastViewOffsetXValid = false;
1270:                lastScaleXValid = false;
1271:                lastScaleYValid = false;
1272:                lastLeadingItemIsForBuffer = false;
1273:                lastLeadingItemIndex = 0;
1274:                trailingItemVisible = true;
1275:                scrollBarValuesDirty = true;
1276:                offScreenImageInvalid = true;
1277:                lastTrailingItemIndexValid = false;
1278:                lastTrailingItemIndex = 0;
1279:                useDayInTimeLegend = false;
1280:                doRepaint(true);
1281:            }
1282:
1283:            public void resetFitToWindow() {
1284:                if (fitToWindow) {
1285:                    changeFitToWindow(false);
1286:                    offScreenImageInvalid = true;
1287:                    doRepaint(true);
1288:                }
1289:            }
1290:
1291:            public void resetTrackingEnd() {
1292:                if (trackingEnd) {
1293:                    changeTrackingEnd(false);
1294:                    offScreenImageInvalid = true;
1295:                    doRepaint(true);
1296:                }
1297:            }
1298:
1299:            // --- Initial appearance (without any data) support -------------------------
1300:            public void setupInitialAppearance(long firstValueH,
1301:                    long lastValueH, long firstValueV, long lastValueV) {
1302:                customizedEmptyAppearance = true;
1303:                this .firstValueH = firstValueH;
1304:                this .lastValueH = lastValueH;
1305:                this .firstValueV = firstValueV;
1306:                this .lastValueV = lastValueV;
1307:            }
1308:
1309:            public void update(Graphics g) {
1310:            }
1311:
1312:            /**
1313:             * Notifies all listeners about chart zoom.
1314:             */
1315:            protected void fireChartDataChanged() {
1316:                if (chartActionListeners == null) {
1317:                    return;
1318:                }
1319:
1320:                Vector toNotify;
1321:
1322:                synchronized (this ) {
1323:                    toNotify = ((Vector) chartActionListeners.clone());
1324:                }
1325:
1326:                Iterator iterator = toNotify.iterator();
1327:
1328:                while (iterator.hasNext()) {
1329:                    ((ChartActionListener) iterator.next()).chartDataChanged();
1330:                }
1331:            }
1332:
1333:            /**
1334:             * Notifies all listeners about fitToWindow change.
1335:             */
1336:            protected void fireChartFitToWindowChanged() {
1337:                if (chartActionListeners == null) {
1338:                    return;
1339:                }
1340:
1341:                Vector toNotify;
1342:
1343:                synchronized (this ) {
1344:                    toNotify = ((Vector) chartActionListeners.clone());
1345:                }
1346:
1347:                Iterator iterator = toNotify.iterator();
1348:
1349:                while (iterator.hasNext()) {
1350:                    ((ChartActionListener) iterator.next())
1351:                            .chartFitToWindowChanged();
1352:                }
1353:            }
1354:
1355:            /**
1356:             * Notifies all listeners about chart pan.
1357:             */
1358:            protected void fireChartPanned() {
1359:                if (chartActionListeners == null) {
1360:                    return;
1361:                }
1362:
1363:                Vector toNotify;
1364:
1365:                synchronized (this ) {
1366:                    toNotify = ((Vector) chartActionListeners.clone());
1367:                }
1368:
1369:                Iterator iterator = toNotify.iterator();
1370:
1371:                while (iterator.hasNext()) {
1372:                    ((ChartActionListener) iterator.next()).chartPanned();
1373:                }
1374:            }
1375:
1376:            /**
1377:             * Notifies all listeners about trackingEnd change.
1378:             */
1379:            protected void fireChartTrackingEndChanged() {
1380:                if (chartActionListeners == null) {
1381:                    return;
1382:                }
1383:
1384:                Vector toNotify;
1385:
1386:                synchronized (this ) {
1387:                    toNotify = ((Vector) chartActionListeners.clone());
1388:                }
1389:
1390:                Iterator iterator = toNotify.iterator();
1391:
1392:                while (iterator.hasNext()) {
1393:                    ((ChartActionListener) iterator.next())
1394:                            .chartTrackingEndChanged();
1395:                }
1396:            }
1397:
1398:            /**
1399:             * Notifies all listeners about chart zoom.
1400:             */
1401:            protected void fireChartZoomed() {
1402:                if (chartActionListeners == null) {
1403:                    return;
1404:                }
1405:
1406:                Vector toNotify;
1407:
1408:                synchronized (this ) {
1409:                    toNotify = ((Vector) chartActionListeners.clone());
1410:                }
1411:
1412:                Iterator iterator = toNotify.iterator();
1413:
1414:                while (iterator.hasNext()) {
1415:                    ((ChartActionListener) iterator.next()).chartZoomed();
1416:                }
1417:            }
1418:
1419:            private int getBottomHorizontalAxisLegendHeight() {
1420:                return horizontalAxisFont.getSize()
1421:                        + (verticalAxisFont.getSize() / 2) + 10;
1422:            }
1423:
1424:            // --- viewOffset to itemIndex conversion routines ---------------------------
1425:
1426:            // returns index of first visible item according to provided offsetX
1427:            private int getLeadingItemIndex(long offsetX) {
1428:                for (int leadingItemIndex = 0; leadingItemIndex < (model
1429:                        .getItemCount() - 1); leadingItemIndex++) {
1430:                    if ((long) Math
1431:                            .ceil((model.getXValue(leadingItemIndex + 1) - dataOffsetX)
1432:                                    * viewScaleX) > offsetX) {
1433:                        return leadingItemIndex;
1434:                    }
1435:                }
1436:
1437:                return model.getItemCount() - 1;
1438:            }
1439:
1440:            // returns index of first visible item according to provided offsetX, searching starts from lastLeadingItemIndex
1441:            private int getLeadingItemIndex(long offsetX,
1442:                    int lastLeadingItemIndex) {
1443:                if (offsetX > lastViewOffsetX) {
1444:                    // lastViewOffsetX was smaller, searching in forward direction
1445:                    for (int leadingItemIndex = lastLeadingItemIndex; leadingItemIndex < (model
1446:                            .getItemCount() - 1); leadingItemIndex++) {
1447:                        if ((long) Math.ceil((model
1448:                                .getXValue(leadingItemIndex + 1) - dataOffsetX)
1449:                                * viewScaleX) > offsetX) {
1450:                            return leadingItemIndex;
1451:                        }
1452:                    }
1453:
1454:                    return model.getItemCount() - 1;
1455:                } else {
1456:                    // lastViewOffsetX was bigger, searching in backward direction
1457:                    for (int leadingItemIndex = lastLeadingItemIndex; leadingItemIndex >= 0; leadingItemIndex--) {
1458:                        if ((long) Math
1459:                                .ceil((model.getXValue(leadingItemIndex) - dataOffsetX)
1460:                                        * viewScaleX) < offsetX) {
1461:                            return leadingItemIndex;
1462:                        }
1463:                    }
1464:
1465:                    return 0;
1466:                }
1467:            }
1468:
1469:            // --- chart legend painting stuff -------------------------------------------
1470:            private int getLeftVerticalAxisLegendWidth() {
1471:                if (isVerticalAxisValueAdaptDivider()) {
1472:                    return offScreenGraphics
1473:                            .getFontMetrics(verticalAxisFont)
1474:                            .stringWidth(
1475:                                    "2000M"
1476:                                            + ((getVerticalAxisValueString() != null) ? getVerticalAxisValueString()
1477:                                                    : "")); // NOI18N
1478:                }
1479:
1480:                return offScreenGraphics.getFontMetrics(verticalAxisFont)
1481:                        .stringWidth(
1482:                                getVerticalAxisMarkString(model
1483:                                        .getMaxDisplayYValue(0))) - 10;
1484:            }
1485:
1486:            private int getRightVerticalAxisLegendWidth() {
1487:                if (useSecondaryVerticalAxis) {
1488:                    if (isVerticalAxisValueAdaptDivider2()) {
1489:                        return offScreenGraphics
1490:                                .getFontMetrics(verticalAxisFont)
1491:                                .stringWidth(
1492:                                        "2000M"
1493:                                                + ((getVerticalAxisValueString2() != null) ? getVerticalAxisValueString2()
1494:                                                        : "")); // NOI18N
1495:                    } else {
1496:                        return offScreenGraphics.getFontMetrics(
1497:                                verticalAxisFont).stringWidth(
1498:                                getVerticalAxisMarkString2(model
1499:                                        .getMaxDisplayYValue(1))) + 7;
1500:                    }
1501:                }
1502:
1503:                return offScreenGraphics.getFontMetrics(verticalAxisFont)
1504:                        .stringWidth("100") - 10; // NOI18N
1505:            }
1506:
1507:            private String getVerticalAxisMarkString(long mark) {
1508:                return mark + verticalAxisValueString;
1509:            }
1510:
1511:            private String getVerticalAxisMarkString2(long mark) {
1512:                return mark + verticalAxisValueString2;
1513:            }
1514:
1515:            private double getXValueAtPosition(int x) {
1516:                return getViewToDataApproxX(x - getInsets().left);
1517:            }
1518:
1519:            private void changeFitToWindow(boolean newValue) {
1520:                if (fitToWindow != newValue) {
1521:                    fitToWindow = newValue;
1522:                    SwingUtilities.invokeLater(new Runnable() {
1523:                        public void run() {
1524:                            fireChartFitToWindowChanged();
1525:                        }
1526:                    });
1527:                }
1528:            }
1529:
1530:            // --- Panning support -------------------------------------------------------
1531:            private void changePan(long newValue) {
1532:                newValue = Math.max(newValue, 0);
1533:
1534:                if (viewOffsetX != newValue) {
1535:                    viewOffsetX = newValue;
1536:                    SwingUtilities.invokeLater(new Runnable() {
1537:                        public void run() {
1538:                            fireChartPanned();
1539:                        }
1540:                    });
1541:                }
1542:            }
1543:
1544:            private void changeTrackingEnd(boolean newValue) {
1545:                if (trackingEnd != newValue) {
1546:                    trackingEnd = newValue;
1547:                    SwingUtilities.invokeLater(new Runnable() {
1548:                        public void run() {
1549:                            fireChartTrackingEndChanged();
1550:                        }
1551:                    });
1552:                }
1553:            }
1554:
1555:            private void changeZoom(double newValue) {
1556:                if (!fitToWindow) {
1557:                    newValue = Math.max(newValue, getMinimumZoom());
1558:                    newValue = Math.min(newValue, getMaximumZoom());
1559:                }
1560:
1561:                if (viewScaleX != newValue) {
1562:                    viewScaleX = newValue;
1563:                    SwingUtilities.invokeLater(new Runnable() {
1564:                        public void run() {
1565:                            fireChartZoomed();
1566:                        }
1567:                    });
1568:                }
1569:            }
1570:
1571:            private boolean checkBottomChartMargin() {
1572:                int bottomHorizontalAxisLegendHeight = getBottomHorizontalAxisLegendHeight();
1573:
1574:                if (chartInsets.bottom < bottomHorizontalAxisLegendHeight) {
1575:                    chartInsets.bottom = bottomHorizontalAxisLegendHeight;
1576:
1577:                    return true;
1578:                }
1579:
1580:                return false;
1581:            }
1582:
1583:            private void checkChartMargins() {
1584:                if (checkLeftChartMargin() || checkRightChartMargin()) {
1585:                    updateOffScreenImageSize();
1586:                }
1587:            }
1588:
1589:            private boolean checkLeftChartMargin() {
1590:                int leftVerticalAxisLegendWidth = getLeftVerticalAxisLegendWidth();
1591:
1592:                if (chartInsets.left < leftVerticalAxisLegendWidth) {
1593:                    chartInsets.left = leftVerticalAxisLegendWidth;
1594:
1595:                    return true;
1596:                }
1597:
1598:                return false;
1599:            }
1600:
1601:            private boolean checkRightChartMargin() {
1602:                int rightVerticalAxisLegendWidth = getRightVerticalAxisLegendWidth();
1603:
1604:                if (chartInsets.right < rightVerticalAxisLegendWidth) {
1605:                    chartInsets.right = rightVerticalAxisLegendWidth;
1606:
1607:                    return true;
1608:                }
1609:
1610:                return false;
1611:            }
1612:
1613:            // --- component painting stuff ----------------------------------------------
1614:            private void doRepaint(boolean rescale) {
1615:                scaleFactorsNeedUpdate = rescale;
1616:                repaint();
1617:            }
1618:
1619:            // --- general chart painting stuff ------------------------------------------
1620:            private void drawChart(Graphics2D g2) {
1621:                // not enough data to draw the chart
1622:                if (model.getItemCount() < 2 /*model.getItemCount() < 1*/) {
1623:                    if (customizedEmptyAppearance) {
1624:                        drawChartLegendEmpty(g2);
1625:                    }
1626:
1627:                    return;
1628:                }
1629:
1630:                // update scale factors and status flags
1631:                if (scaleFactorsNeedUpdate) {
1632:                    updateScaleFactors();
1633:                }
1634:
1635:                if (fitToWindow) {
1636:                    // draws chart that fits the window (not optimized algorithm)
1637:                    drawChartFitToWindow(g2);
1638:                } else {
1639:                    // draws scaled chart (optimized algorithm, according to useBufferCopy flag)
1640:                    drawChartFromCurrentViewOffsetX(g2);
1641:                }
1642:
1643:                // new offscreen buffer is now valid
1644:                offScreenImageInvalid = false;
1645:            }
1646:
1647:            // --- fit to window painting stuff ------------------------------------------
1648:
1649:            // draws chart scaled to fit the component size
1650:            private void drawChartFitToWindow(Graphics2D g2) {
1651:                // save current clip
1652:                Shape clip = g2.getClip();
1653:
1654:                // set current clip to whole graph area
1655:                g2.setClip(chartInsets.left, chartInsets.top, chartWidth,
1656:                        chartHeight);
1657:
1658:                int thresholdY = Integer.MAX_VALUE;
1659:
1660:                if (limitYValue != Long.MAX_VALUE) {
1661:                    thresholdY = (int) getDataToViewY(limitYValue, 0);
1662:                }
1663:
1664:                if ((thresholdY != Integer.MAX_VALUE)
1665:                        && (thresholdY >= chartInsets.top)) {
1666:                    // fill the threshold area
1667:                    g2.setPaint(limitYColor);
1668:                    g2.fillRect(chartInsets.left, chartInsets.top, chartWidth,
1669:                            thresholdY - chartInsets.top + 1);
1670:                    // clear rest of the graph area
1671:                    g2.setPaint(chartPaint);
1672:                    g2.fillRect(chartInsets.left, thresholdY + 1, chartWidth,
1673:                            chartHeight - thresholdY + chartInsets.top);
1674:                } else {
1675:                    // whole graph area needs to be cleared using user-defined background color
1676:                    g2.setPaint(chartPaint);
1677:                    g2.fillRect(chartInsets.left, chartInsets.top, chartWidth,
1678:                            chartHeight);
1679:                }
1680:
1681:                // --- Support for displaying garbage collections
1682:                if (model instanceof  AsyncMarksProvider) {
1683:                    AsyncMarksProvider amp = (AsyncMarksProvider) model;
1684:                    Color markColor = amp.getMarkColor();
1685:                    int marksCount = amp.getMarksCount();
1686:
1687:                    int lastXEnd = Integer.MIN_VALUE;
1688:
1689:                    for (int i = 0; i < marksCount; i++) {
1690:                        long markBegin = amp.getMarkStart(i);
1691:                        long markEnd = amp.getMarkEnd(i);
1692:
1693:                        int xBegin = (int) getDataToViewX(markBegin);
1694:                        int xEnd = Math.max((int) getDataToViewX(markEnd),
1695:                                xBegin + 1);
1696:
1697:                        if (xEnd > lastXEnd) {
1698:                            xBegin = Math.max(xBegin, lastXEnd);
1699:
1700:                            int xWidth = xEnd - xBegin;
1701:
1702:                            if (xWidth > 0) {
1703:                                g2.setColor(markColor);
1704:                                g2.fillRect(xBegin, chartInsets.top, xEnd
1705:                                        - xBegin, chartHeight);
1706:                            }
1707:
1708:                            lastXEnd = xEnd;
1709:                        }
1710:                    }
1711:                }
1712:
1713:                // ---
1714:                long startX;
1715:                long endX;
1716:
1717:                // for every item draw series area
1718:                for (int itemIndex = 1; itemIndex < model.getItemCount(); itemIndex++) {
1719:                    // use last x-coordinate
1720:                    startX = getDataToViewX(model.getXValue(itemIndex - 1));
1721:                    endX = getDataToViewX(model.getXValue(itemIndex));
1722:
1723:                    for (int seriesIndex = 0; seriesIndex < model
1724:                            .getSeriesCount(); seriesIndex++) {
1725:                        drawSeriesItem(g2, model.getSeriesColor(seriesIndex),
1726:                                startX, getDataToViewY(model.getYValue(
1727:                                        itemIndex - 1, seriesIndex),
1728:                                        seriesIndex), endX,
1729:                                getDataToViewY(model.getYValue(itemIndex,
1730:                                        seriesIndex), seriesIndex));
1731:                    }
1732:                }
1733:
1734:                drawChartLegend(g2, g2.getClip(), 0, chartWidth - 1);
1735:
1736:                // restore original clip
1737:                g2.setClip(clip);
1738:            }
1739:
1740:            // --- scaled chart painting stuff -------------------------------------------
1741:
1742:            // draws scaled chart from current viewOffsetX
1743:            private void drawChartFromCurrentViewOffsetX(Graphics2D g2) {
1744:                drawChartFromOffset(g2, viewOffsetX);
1745:            }
1746:
1747:            // draws scaled chart from provided viewOffsetX
1748:            private void drawChartFromOffset(Graphics2D g2, long offsetX) {
1749:                // save current clip
1750:                Shape clip = g2.getClip();
1751:
1752:                // new leading item index to compute
1753:                int leadingItemIndex;
1754:
1755:                if (lastViewOffsetXValid) { // viewOffsetX didn't changed since last iteration
1756:
1757:                    if (trailingItemVisible || !lastScaleYValid) {
1758:                        if (lastLeadingItemIsForBuffer) {
1759:                            // if bufferCopy optimization is used, lastLeadingItemIndex is not valid, current leadingItemIndex must be found from the beginning
1760:                            leadingItemIndex = getLeadingItemIndex(offsetX);
1761:                        } else {
1762:                            if (lastScaleYValid && lastTrailingItemIndexValid) {
1763:                                // lastLeadingItemIndex (index of last item from last iteration) is used as leadingItemIndex
1764:                                leadingItemIndex = lastTrailingItemIndex;
1765:                            } else {
1766:                                leadingItemIndex = getLeadingItemIndex(offsetX,
1767:                                        lastLeadingItemIndex);
1768:                            }
1769:                        }
1770:
1771:                        drawChartFromOffset(g2, leadingItemIndex,
1772:                                (int) getDataToViewX(model
1773:                                        .getXValue(leadingItemIndex))
1774:                                        - chartInsets.left, chartWidth); // some data-tail changed in visible area since last iteration
1775:                        //System.err.println(System.currentTimeMillis() + " Drawing from last offsetX from " + ((int)getDataToViewX(model.getXValue(leadingItemIndex)) - chartInsets.left) + " to " + chartWidth);
1776:
1777:                        if (lastLeadingItemIsForBuffer) {
1778:                            lastLeadingItemIndex = leadingItemIndex;
1779:                        }
1780:
1781:                        lastLeadingItemIsForBuffer = false;
1782:                        lastTrailingItemIndex = model.getItemCount() - 1;
1783:                        lastTrailingItemIndexValid = true;
1784:
1785:                        if (trackingEnd) {
1786:                            lastViewOffsetXValid = false;
1787:                        }
1788:                    } else {
1789:                        lastTrailingItemIndexValid = false;
1790:                    }
1791:                } else {
1792:                    lastTrailingItemIndexValid = false;
1793:
1794:                    if (lastScaleXValid) { // X-scale didn't change sice last iteration but viewOffsetX did, chart area needs to be repainted ("moved" left / right)
1795:
1796:                        if (lastScaleYValid) { // scaleFactorsY didn't change, not-changed-areas can be just "moved" left / right
1797:
1798:                            if (viewOffsetX > lastViewOffsetX) { // right-move
1799:                                // area that needs to be redrawn
1800:
1801:                                int dirtyWidth = (int) (viewOffsetX - lastViewOffsetX);
1802:                                int copyWidth = (int) (trailingItemVisible ? getDataToViewX(model
1803:                                        .getMaxXValue())
1804:                                        : (chartWidth - dirtyWidth));
1805:
1806:                                if ((copyWidth > 0) && (dirtyWidth > 0)
1807:                                        && (chartHeight > 0)) {
1808:                                    // copy not-changed-areas to the left
1809:                                    if (copyAccel == COPY_ACCEL_RASTER) {
1810:                                        // BufferedImage.getRaster().setDataElements() used, seems to have better performance on Windows (HW acceleration)
1811:                                        int rasterWidth = offScreenImage
1812:                                                .getRaster().getWidth();
1813:                                        int rasterHeight = offScreenImage
1814:                                                .getRaster().getHeight();
1815:                                        int startX = chartInsets.left
1816:                                                + dirtyWidth;
1817:                                        int startY = chartInsets.top;
1818:
1819:                                        if ((startX >= 0)
1820:                                                && ((startX + copyWidth) <= rasterWidth)
1821:                                                && (startY >= 0)
1822:                                                && ((startY + chartHeight) <= rasterHeight)
1823:                                                && (chartInsets.left >= 0)
1824:                                                && ((chartInsets.left + copyWidth) <= rasterWidth)
1825:                                                && (chartInsets.top >= 0)
1826:                                                && ((chartInsets.top + chartHeight) <= rasterHeight)) {
1827:                                            Raster raster = offScreenImage
1828:                                                    .getRaster()
1829:                                                    .createWritableChild(
1830:                                                            startX, startY,
1831:                                                            copyWidth,
1832:                                                            chartHeight, 0, 0,
1833:                                                            null);
1834:                                            offScreenImage.getRaster()
1835:                                                    .setDataElements(
1836:                                                            chartInsets.left,
1837:                                                            chartInsets.top,
1838:                                                            raster);
1839:                                        }
1840:                                    } else {
1841:                                        // Graphics.copyArea() used, optimal for UNIXes, works well also for Windows (default)
1842:                                        g2.copyArea(chartInsets.left
1843:                                                + dirtyWidth, chartInsets.top,
1844:                                                copyWidth, chartHeight,
1845:                                                -dirtyWidth, 0);
1846:                                    }
1847:                                }
1848:
1849:                                // update selection boundary start according to chart movement when tracking end
1850:                                if (selectionTracksMovement && mouseInProgress
1851:                                        && trackingEnd) {
1852:                                    int delta = Math.min(selectionX
1853:                                            - chartInsets.left
1854:                                            - getInsets().left, dirtyWidth);
1855:                                    selectionX -= delta;
1856:                                    selectionWidth += delta;
1857:                                }
1858:
1859:                                // update dirty area on the right side
1860:                                if (lastLeadingItemIsForBuffer) {
1861:                                    leadingItemIndex = getLeadingItemIndex(
1862:                                            offsetX + copyWidth,
1863:                                            lastLeadingItemIndex);
1864:                                } else {
1865:                                    leadingItemIndex = getLeadingItemIndex(offsetX);
1866:                                }
1867:
1868:                                drawChartFromOffset(g2, leadingItemIndex,
1869:                                        copyWidth, chartWidth);
1870:                                //System.err.println(System.currentTimeMillis() + " Drawing on right from " + copyWidth + " to " + chartWidth);
1871:                                lastLeadingItemIsForBuffer = true;
1872:                                lastLeadingItemIndex = leadingItemIndex;
1873:                            } else { // left-move
1874:
1875:                                // area that needs to be redrawn
1876:                                int dirtyWidth = (int) (lastViewOffsetX - viewOffsetX);
1877:                                int copyWidth = chartWidth - dirtyWidth;
1878:
1879:                                if ((copyWidth > 0) && (dirtyWidth > 0)
1880:                                        && (chartHeight > 0)) {
1881:                                    // copy not-changed-areas to the right
1882:                                    if (copyAccel == COPY_ACCEL_RASTER) {
1883:                                        // BufferedImage.getRaster().setDataElements() used, seems to have better performance on Windows (HW acceleration)
1884:                                        int rasterWidth = offScreenImage
1885:                                                .getRaster().getWidth();
1886:                                        int rasterHeight = offScreenImage
1887:                                                .getRaster().getHeight();
1888:                                        int startX = chartInsets.left
1889:                                                + dirtyWidth;
1890:                                        int startY = chartInsets.top;
1891:
1892:                                        if ((startX >= 0)
1893:                                                && ((startX + copyWidth) <= rasterWidth)
1894:                                                && (startY >= 0)
1895:                                                && ((startY + chartHeight) <= rasterHeight)
1896:                                                && (chartInsets.left >= 0)
1897:                                                && ((chartInsets.left + copyWidth) <= rasterWidth)
1898:                                                && (chartInsets.top >= 0)
1899:                                                && ((chartInsets.top + chartHeight) <= rasterHeight)) {
1900:                                            Raster raster = offScreenImage
1901:                                                    .getRaster()
1902:                                                    .createWritableChild(
1903:                                                            chartInsets.left,
1904:                                                            chartInsets.top,
1905:                                                            copyWidth,
1906:                                                            chartHeight, 0, 0,
1907:                                                            null);
1908:                                            offScreenImage.getRaster()
1909:                                                    .setDataElements(startX,
1910:                                                            startY, raster);
1911:                                        }
1912:                                    } else {
1913:                                        // Graphics.copyArea() used, optimal for UNIXes, works well also for Windows (default)
1914:                                        g2.copyArea(chartInsets.left,
1915:                                                chartInsets.top, copyWidth,
1916:                                                chartHeight, dirtyWidth, 0);
1917:                                    }
1918:                                }
1919:
1920:                                // update dirty area on the left side
1921:                                if (lastLeadingItemIsForBuffer) {
1922:                                    leadingItemIndex = getLeadingItemIndex(
1923:                                            offsetX, lastLeadingItemIndex);
1924:                                } else {
1925:                                    leadingItemIndex = getLeadingItemIndex(offsetX);
1926:                                }
1927:
1928:                                drawChartFromOffset(g2, leadingItemIndex, 0,
1929:                                        dirtyWidth);
1930:                                //System.err.println(System.currentTimeMillis() + " Drawing on left from " + 0 + " to " + dirtyWidth);
1931:                                lastLeadingItemIsForBuffer = true;
1932:                                lastLeadingItemIndex = leadingItemIndex;
1933:                            }
1934:                        } else {
1935:                            if (lastLeadingItemIsForBuffer) {
1936:                                // if bufferCopy optimization is used, lastLeadingItemIndex is not valid, current leadingItemIndex must be found from the beginning
1937:                                leadingItemIndex = getLeadingItemIndex(offsetX);
1938:                            } else {
1939:                                // lastLeadingItemIndex is valid, it can be used to find current leadingItemIndex
1940:                                leadingItemIndex = getLeadingItemIndex(offsetX,
1941:                                        lastLeadingItemIndex);
1942:                            }
1943:
1944:                            drawChartFromOffset(g2, leadingItemIndex, 0,
1945:                                    chartWidth);
1946:                            //System.err.println(System.currentTimeMillis() + " Drawing from scratch (y changed) from " + 0 + " to " + chartWidth);
1947:                            lastLeadingItemIsForBuffer = false;
1948:                            lastLeadingItemIndex = leadingItemIndex;
1949:                        }
1950:                    } else { // whole chart area needs to be repainted from the beginning without any optimizations
1951:                        leadingItemIndex = getLeadingItemIndex(offsetX);
1952:                        drawChartFromOffset(g2, leadingItemIndex, 0, chartWidth);
1953:                        //System.err.println(System.currentTimeMillis() + " Drawing from scratch (x changed) from " + 0 + " to " + chartWidth);
1954:                        lastLeadingItemIsForBuffer = false;
1955:                        lastLeadingItemIndex = leadingItemIndex;
1956:                    }
1957:                }
1958:
1959:                lastViewOffsetX = offsetX; // update lastViewOffsetX value
1960:
1961:                lastViewOffsetXValid = true; // lastViewOffsetXValid is now valid
1962:                lastScaleXValid = true; // lastScaleXValid is now valid
1963:                lastScaleYValid = true; // lastScaleYValid is now valid
1964:
1965:                updateTrailingItemVisible();
1966:
1967:                // restore original clip
1968:                g2.setClip(clip);
1969:            }
1970:
1971:            // draws scaled beginning from the provided item index, with defined horizontal clip bounds
1972:            private void drawChartFromOffset(Graphics2D g2,
1973:                    int leadingItemIndex, int startClipX, int endClipX) {
1974:                startClipX = Math.max(0, startClipX);
1975:                endClipX = Math.min(chartWidth, endClipX);
1976:
1977:                // set clip valid for current drawing
1978:                g2.setClip(chartInsets.left + startClipX, chartInsets.top,
1979:                        endClipX - startClipX, chartHeight);
1980:
1981:                int thresholdY = Integer.MAX_VALUE;
1982:
1983:                if (limitYValue != Long.MAX_VALUE) {
1984:                    thresholdY = (int) getDataToViewY(limitYValue, 0);
1985:                }
1986:
1987:                if ((thresholdY != Integer.MAX_VALUE)
1988:                        && (thresholdY >= chartInsets.top)) {
1989:                    // fill the threshold area
1990:                    g2.setPaint(limitYColor);
1991:                    g2.fillRect(chartInsets.left + startClipX, chartInsets.top,
1992:                            endClipX - startClipX, thresholdY - chartInsets.top
1993:                                    + 1);
1994:                    // clear rest of the graph area
1995:                    g2.setPaint(chartPaint);
1996:                    g2.fillRect(chartInsets.left + startClipX, thresholdY + 1,
1997:                            endClipX - startClipX, chartHeight - thresholdY
1998:                                    + chartInsets.top);
1999:                } else {
2000:                    // whole graph area needs to be cleared using user-defined background color
2001:                    g2.setPaint(chartPaint);
2002:                    g2.fillRect(chartInsets.left + startClipX, chartInsets.top,
2003:                            endClipX - startClipX, chartHeight);
2004:                }
2005:
2006:                // --- Support for displaying garbage collections
2007:                if (model instanceof  AsyncMarksProvider) {
2008:                    AsyncMarksProvider amp = (AsyncMarksProvider) model;
2009:                    Color markColor = amp.getMarkColor();
2010:                    int marksCount = amp.getMarksCount();
2011:
2012:                    int lastXEnd = Integer.MIN_VALUE;
2013:
2014:                    long firstMarkTime = model.getXValue(leadingItemIndex);
2015:
2016:                    for (int i = 0; i < marksCount; i++) {
2017:                        long markEnd = amp.getMarkEnd(i);
2018:
2019:                        if (markEnd >= firstMarkTime) {
2020:                            long markBegin = amp.getMarkStart(i);
2021:                            int xBegin = (int) getDataToViewX(markBegin);
2022:
2023:                            if (xBegin > (chartInsets.left + endClipX + 1)) {
2024:                                break;
2025:                            }
2026:
2027:                            int xEnd = Math.max((int) getDataToViewX(markEnd),
2028:                                    xBegin + 1);
2029:
2030:                            if (xEnd > lastXEnd) {
2031:                                xBegin = Math.max(xBegin, lastXEnd);
2032:
2033:                                int xWidth = xEnd - xBegin;
2034:
2035:                                if (xWidth > 0) {
2036:                                    g2.setColor(markColor);
2037:                                    g2.fillRect(xBegin, chartInsets.top, xEnd
2038:                                            - xBegin, chartHeight);
2039:                                }
2040:
2041:                                lastXEnd = xEnd;
2042:                            }
2043:                        }
2044:                    }
2045:                }
2046:
2047:                // ---
2048:
2049:                // previous item must be included due to line joining & vertical lines
2050:                leadingItemIndex = ((type == TYPE_LINE) ? Math.max(0,
2051:                        leadingItemIndex - 1) : leadingItemIndex);
2052:
2053:                // first and second x-coordinate
2054:                long startX;
2055:                long endX;
2056:
2057:                // for every item draw series area
2058:                for (int itemIndex = leadingItemIndex + 1; itemIndex < model
2059:                        .getItemCount(); itemIndex++) {
2060:                    // use last x-coordinate
2061:                    startX = getDataToViewX(model.getXValue(itemIndex - 1));
2062:                    endX = getDataToViewX(model.getXValue(itemIndex));
2063:
2064:                    for (int seriesIndex = 0; seriesIndex < model
2065:                            .getSeriesCount(); seriesIndex++) {
2066:                        drawSeriesItem(g2, model.getSeriesColor(seriesIndex),
2067:                                startX, getDataToViewY(model.getYValue(
2068:                                        itemIndex - 1, seriesIndex),
2069:                                        seriesIndex), endX,
2070:                                getDataToViewY(model.getYValue(itemIndex,
2071:                                        seriesIndex), seriesIndex));
2072:                    }
2073:
2074:                    // if the last drawn item ends out of visible area, there won't be any changes/redraw needed in next iteration until viewOffset or viewScale changes
2075:                    if (endX > (chartInsets.left + endClipX + 1)) {
2076:                        break;
2077:                    }
2078:                }
2079:
2080:                // draw chart legend
2081:                drawChartLegend(g2, g2.getClip(), startClipX, endClipX);
2082:            }
2083:
2084:            private void drawChartLegend(Graphics2D g2, Shape chartClip,
2085:                    int startClipX, int endClipX) {
2086:                // use for absolute time:
2087:                long firstValueH = (long) getViewToDataApproxWidth(viewOffsetX)
2088:                        + model.getMinXValue();
2089:                long lastValueH = (long) getViewToDataApproxWidth(viewOffsetX
2090:                        + chartWidth)
2091:                        + model.getMinXValue();
2092:                // use for relative time:
2093:                //long firstValueH = (long)Math.floor(getViewToDataApproxWidth(viewOffsetX));
2094:                //long lastValueH = (long)Math.floor(getViewToDataApproxWidth(viewOffsetX + chartWidth));
2095:                drawHorizontalChartLegend(g2, chartClip, startClipX, endClipX,
2096:                        firstValueH, lastValueH);
2097:
2098:                double firstValueV = model.getMinDisplayYValue(0)
2099:                        / (double) verticalAxisValueDivider;
2100:                double lastValueV = model.getMaxDisplayYValue(0)
2101:                        / (double) verticalAxisValueDivider;
2102:                drawVerticalChartLegend(g2, chartClip, startClipX, endClipX,
2103:                        firstValueV, lastValueV);
2104:            }
2105:
2106:            private void drawChartLegendEmpty(Graphics2D g2) {
2107:                // save current clip
2108:                Shape clip = g2.getClip();
2109:
2110:                // set current clip to whole graph area
2111:                g2.setClip(chartInsets.left, chartInsets.top, chartWidth,
2112:                        chartHeight);
2113:
2114:                Shape newClip = g2.getClip();
2115:
2116:                // whole graph area needs to be cleared using user-defined background color
2117:                g2.setPaint(chartPaint);
2118:                g2.fillRect(chartInsets.left, chartInsets.top, chartWidth,
2119:                        chartHeight);
2120:
2121:                drawHorizontalChartLegend(g2, newClip, 0, chartWidth - 1,
2122:                        firstValueH, lastValueH);
2123:                drawVerticalChartLegend(g2, newClip, 0, chartWidth - 1,
2124:                        firstValueV, lastValueV);
2125:
2126:                // restore original clip
2127:                g2.setClip(clip);
2128:            }
2129:
2130:            // draws one series item using filled segment
2131:            private void drawFillSeriesItem(Graphics2D g2, Color color, int x1,
2132:                    int y1, int x2, int y2) {
2133:                g2.setColor(color);
2134:                g2.setStroke(chartStroke);
2135:
2136:                if (dataType == VALUES_INTERPOLATED) {
2137:                    Polygon polygon = new Polygon(new int[] { x1, x1, x2, x2 },
2138:                            new int[] { y1, chartHeight + chartInsets.top,
2139:                                    chartHeight + chartInsets.top, y2 }, 4);
2140:                    g2.fill(polygon);
2141:                } else if (dataType == VALUES_DISCRETE) {
2142:                    Polygon polygon = new Polygon(new int[] { x1, x1, x2, x2 },
2143:                            new int[] { y1, chartHeight + chartInsets.top,
2144:                                    chartHeight + chartInsets.top, y1 }, 4);
2145:                    g2.fill(polygon);
2146:                }
2147:            }
2148:
2149:            private void drawHorizontalAxisLegendSegment(Graphics2D g2,
2150:                    long currentMark, int x) {
2151:                g2.setClip(horizontalAxisMarksClip);
2152:                g2.setPaint(horizontalAxisPaint);
2153:                g2.drawLine(x, chartInsets.top + chartHeight + 1, x,
2154:                        chartInsets.top + chartHeight + 4);
2155:
2156:                g2.setClip(horizontalAxisClip);
2157:                paintHorizontalTimeMarkString(g2, currentMark, x);
2158:            }
2159:
2160:            private void drawHorizontalChartLegend(Graphics2D g2,
2161:                    Shape chartClip, int startClipX, int endClipX,
2162:                    long firstValue, long lastValue) {
2163:                // set horizontal axis marks clip
2164:                g2.setClip(horizontalAxisMarksClip);
2165:
2166:                // clear horizontal axis marks area
2167:                g2.setPaint(backgroundPaint);
2168:                g2.fillRect(horizontalAxisMarksClip.x,
2169:                        horizontalAxisMarksClip.y,
2170:                        horizontalAxisMarksClip.width,
2171:                        horizontalAxisMarksClip.height);
2172:
2173:                // set horizontal axis clip
2174:                g2.setClip(horizontalAxisClip);
2175:
2176:                // clear horizontal axis area
2177:                g2.setPaint(backgroundPaint);
2178:                g2.fillRect(horizontalAxisClip.x, horizontalAxisClip.y,
2179:                        horizontalAxisClip.width, horizontalAxisClip.height);
2180:
2181:                if ((lastValue - firstValue) > 0) {
2182:                    double factor = (double) chartWidth
2183:                            / (double) (lastValue - firstValue);
2184:                    optimalUnits = DateTimeAxisUtils
2185:                            .getOptimalUnits(viewScaleX);
2186:
2187:                    long firstMark = Math.max((firstValue / optimalUnits)
2188:                            * optimalUnits, 0);
2189:                    long currentMark = firstMark;
2190:
2191:                    while (currentMark <= lastValue) {
2192:                        if (currentMark >= firstValue) {
2193:                            long currentMarkRel = currentMark - firstValue;
2194:                            int markPosition = (int) Math.floor(currentMarkRel
2195:                                    * factor)
2196:                                    + chartInsets.left;
2197:
2198:                            drawVerticalMeshSegment(g2, chartClip,
2199:                                    markPosition, chartInsets.top,
2200:                                    markPosition, chartInsets.top + chartHeight);
2201:
2202:                            drawHorizontalAxisLegendSegment(g2, currentMark,
2203:                                    markPosition);
2204:                        }
2205:
2206:                        currentMark += optimalUnits;
2207:                    }
2208:                }
2209:            }
2210:
2211:            private void drawHorizontalMeshSegment(Graphics2D g2,
2212:                    Shape chartClip, int x1, int y1, int x2, int y2) {
2213:                g2.setClip(chartClip);
2214:                g2.setPaint(verticalMeshPaint);
2215:                g2.setStroke(verticalMeshStroke);
2216:                g2.drawLine(x1, y1, x2, y2);
2217:            }
2218:
2219:            // draws one series item using line segment
2220:            private void drawLineSeriesItem(Graphics2D g2, Color color, int x1,
2221:                    int y1, int x2, int y2) {
2222:                g2.setColor(color);
2223:                g2.setStroke(chartStroke);
2224:
2225:                if (dataType == VALUES_INTERPOLATED) {
2226:                    g2.drawLine(x1, y1, x2, y2);
2227:                } else if (dataType == VALUES_DISCRETE) {
2228:                    g2.drawLine(x1, y1, x2, y1);
2229:                    g2.drawLine(x2, y1, x2, y2);
2230:                }
2231:            }
2232:
2233:            // --- selection painting stuff ----------------------------------------------
2234:            private void drawSelection(Graphics2D g2) {
2235:                int selectionTop = insets.top + chartInsets.top;
2236:                int selectionBottom = (selectionTop + chartHeight) - 1;
2237:
2238:                if (selectionWidth < 0) {
2239:                    if (fitToWindow) {
2240:                        return;
2241:                    }
2242:
2243:                    Shape clip = g2.getClip();
2244:                    Insets componentInsets = getInsets();
2245:                    g2.setClip(componentInsets.left, componentInsets.top,
2246:                            drawWidth, drawHeight);
2247:
2248:                    g2.setFont(getFont());
2249:
2250:                    g2.setColor(evenSelectionSegmentsColor);
2251:                    g2.setStroke(evenSelectionSegmentsStroke);
2252:                    g2.drawLine(selectionX, selectionTop, selectionX,
2253:                            selectionBottom);
2254:
2255:                    g2.setColor(oddSelectionSegmentColor);
2256:                    g2.setStroke(oddSelectionSegmentStroke);
2257:                    g2.drawLine(selectionX, selectionTop, selectionX,
2258:                            selectionBottom);
2259:
2260:                    g2.setColor(Color.WHITE);
2261:                    g2.drawString(FIT_TO_WINDOW_STRING, selectionX
2262:                            + selectionWidth + 1,
2263:                            (selectionY + selectionHeight + 1) - 5);
2264:                    g2.setColor(Color.BLACK);
2265:                    g2.drawString(FIT_TO_WINDOW_STRING, selectionX
2266:                            + selectionWidth,
2267:                            (selectionY + selectionHeight) - 5);
2268:
2269:                    g2.setClip(clip);
2270:
2271:                    return;
2272:                }
2273:
2274:                g2.setColor(evenSelectionSegmentsColor);
2275:                g2.setStroke(evenSelectionSegmentsStroke);
2276:                g2.drawLine(selectionX, selectionTop, selectionX
2277:                        + selectionWidth, selectionTop);
2278:                g2.drawLine(selectionX + selectionWidth, selectionTop,
2279:                        selectionX + selectionWidth, selectionBottom);
2280:                g2.drawLine(selectionX, selectionBottom, selectionX
2281:                        + selectionWidth, selectionBottom);
2282:                g2.drawLine(selectionX, selectionTop, selectionX,
2283:                        selectionBottom);
2284:
2285:                g2.setColor(oddSelectionSegmentColor);
2286:                g2.setStroke(oddSelectionSegmentStroke);
2287:                g2.drawLine(selectionX, selectionTop, selectionX
2288:                        + selectionWidth, selectionTop);
2289:                g2.drawLine(selectionX + selectionWidth, selectionTop,
2290:                        selectionX + selectionWidth, selectionBottom);
2291:                g2.drawLine(selectionX, selectionBottom, selectionX
2292:                        + selectionWidth, selectionBottom);
2293:                g2.drawLine(selectionX, selectionTop, selectionX,
2294:                        selectionBottom);
2295:            }
2296:
2297:            // --- chart primitives painting stuff ---------------------------------------
2298:
2299:            // draws one series item according to chart type
2300:            private void drawSeriesItem(Graphics2D g2, Color color, long x1,
2301:                    long y1, long x2, long y2) {
2302:                // workaround for Java Bug [ID:4755500] - calling Math.round(NaN) can break subsequent calls to Math.round()
2303:                // which happens when some value for drawing exceeds 1000000, resulting in sun.dc.pr.PRException: endPath: bad path
2304:                if ((Math.abs(x1) > 1000000) || (Math.abs(x2) > 1000000)) {
2305:                    x1 = Math.max(-1000000, x1);
2306:                    x1 = Math.min(1000000, x1);
2307:                    x2 = Math.max(-1000000, x2);
2308:                    x2 = Math.min(1000000, x2);
2309:                }
2310:
2311:                if (type == TYPE_FILL) {
2312:                    drawFillSeriesItem(g2, color, (int) x1, (int) y1, (int) x2,
2313:                            (int) y2);
2314:                } else if (type == TYPE_LINE) {
2315:                    drawLineSeriesItem(g2, color, (int) x1, (int) y1, (int) x2,
2316:                            (int) y2);
2317:                }
2318:            }
2319:
2320:            private void drawVerticalAxisLegendSegment(Graphics2D g2,
2321:                    long currentMark, long optimalUnits, int y) {
2322:                if ("%".equals(verticalAxisValueString) && (currentMark > 100)) {
2323:                    return; // NOI18N // Ugly workaround not to display relative values over 100%
2324:                }
2325:
2326:                g2.setClip(verticalAxisClip);
2327:                g2.setPaint(verticalAxisPaint);
2328:                g2.drawLine(chartInsets.left - 4, y, chartInsets.left, y);
2329:
2330:                paintVerticalTimeMarkString(g2, currentMark, optimalUnits, y);
2331:            }
2332:
2333:            private void drawVerticalAxisLegendSegment2(Graphics2D g2,
2334:                    long currentMark, long optimalUnits, int y) {
2335:                if ("%".equals(verticalAxisValueString2) && (currentMark > 100)) {
2336:                    return; // NOI18N // Ugly workaround not to display relative values over 100%
2337:                }
2338:
2339:                g2.setClip(verticalAxisClip2);
2340:                g2.setPaint(verticalAxisPaint);
2341:                g2.drawLine(chartInsets.left + chartWidth, y, chartInsets.left
2342:                        + chartWidth + 3, y);
2343:
2344:                paintVerticalTimeMarkString2(g2, currentMark, optimalUnits, y);
2345:            }
2346:
2347:            private void drawVerticalChartLegend(Graphics2D g2,
2348:                    Shape chartClip, int startClipX, int endClipX,
2349:                    double firstValue, double lastValue) {
2350:                if (!lastScaleYValid) {
2351:                    // set horizontal axis clip
2352:                    g2.setClip(verticalAxisClip);
2353:
2354:                    // clear horizontal axis area
2355:                    g2.setPaint(backgroundPaint);
2356:                    g2.fillRect(verticalAxisClip.x, verticalAxisClip.y,
2357:                            verticalAxisClip.width, verticalAxisClip.height);
2358:                }
2359:
2360:                long div = verticalAxisValueDivider;
2361:                String tmp = verticalAxisValueString;
2362:
2363:                if (verticalAxisValueAdaptDivider) {
2364:                    if ((model.getMaxDisplayYValue(0) > 2000000000L)
2365:                            && (div < (1024 * 1024 * 1024L))) {
2366:                        div = 1024 * 1024 * 1024L;
2367:                        verticalAxisValueString = "G" + verticalAxisValueString; // NOI18N
2368:                    } else if ((model.getMaxDisplayYValue(0) > 2000000L)
2369:                            && (div < (1024 * 1024L))) {
2370:                        div = 1024 * 1024L;
2371:                        verticalAxisValueString = "M" + verticalAxisValueString; // NOI18N
2372:                    } else if ((model.getMaxDisplayYValue(0) > 2000)
2373:                            && (div < 1024)) {
2374:                        div = 1024;
2375:                        verticalAxisValueString = "K" + verticalAxisValueString; // NOI18N
2376:                    }
2377:
2378:                    firstValue = firstValue / (double) div;
2379:                    lastValue = lastValue / (double) div;
2380:                }
2381:
2382:                if ((lastValue - firstValue) > 0) {
2383:                    double factor = (double) (chartHeight - topChartMargin)
2384:                            / (lastValue - firstValue);
2385:                    optimalUnits = DecimalAxisUtils.getOptimalUnits(factor,
2386:                            minimumVerticalMarksDistance);
2387:
2388:                    if (optimalUnits > 0) {
2389:                        long firstMark = Math.max((long) (Math.ceil(firstValue
2390:                                / optimalUnits) * optimalUnits), 0);
2391:                        long currentMark = firstMark;
2392:
2393:                        double currentMarkRel = currentMark - firstValue;
2394:                        int markPosition = (chartInsets.top + chartHeight)
2395:                                - (int) (currentMarkRel * factor);
2396:
2397:                        while (markPosition >= chartInsets.top) {
2398:                            if (markPosition <= (chartInsets.top + chartHeight)) {
2399:                                drawHorizontalMeshSegment(g2, chartClip,
2400:                                        startClipX + chartInsets.left,
2401:                                        markPosition, endClipX
2402:                                                + chartInsets.left,
2403:                                        markPosition);
2404:
2405:                                if (!lastScaleYValid) {
2406:                                    drawVerticalAxisLegendSegment(g2,
2407:                                            currentMark, optimalUnits,
2408:                                            markPosition);
2409:                                }
2410:                            }
2411:
2412:                            currentMark += optimalUnits;
2413:                            currentMarkRel = currentMark - firstValue;
2414:                            markPosition = (chartInsets.top + chartHeight)
2415:                                    - (int) (currentMarkRel * factor);
2416:                        }
2417:                    }
2418:                }
2419:
2420:                verticalAxisValueString = tmp;
2421:
2422:                if (useSecondaryVerticalAxis && !lastScaleYValid) {
2423:                    // set horizontal axis clip
2424:                    g2.setClip(verticalAxisClip2);
2425:
2426:                    // clear horizontal axis area
2427:                    g2.setPaint(backgroundPaint);
2428:                    g2.fillRect(verticalAxisClip2.x, verticalAxisClip2.y,
2429:                            verticalAxisClip2.width, verticalAxisClip2.height);
2430:
2431:                    div = verticalAxisValueDivider2;
2432:                    tmp = verticalAxisValueString2;
2433:
2434:                    if (isVerticalAxisValueAdaptDivider2()) {
2435:                        if ((model.getMaxDisplayYValue(1) > 2000000000L)
2436:                                && (div < (1024 * 1024 * 1024L))) {
2437:                            div = 1024 * 1024 * 1024L;
2438:                            verticalAxisValueString2 = "G"
2439:                                    + verticalAxisValueString2; // NOI18N
2440:                        } else if ((model.getMaxDisplayYValue(1) > 2000000L)
2441:                                && (div < (1024 * 1024L))) {
2442:                            div = 1024 * 1024L;
2443:                            verticalAxisValueString2 = "M"
2444:                                    + verticalAxisValueString2; // NOI18N
2445:                        } else if ((model.getMaxDisplayYValue(1) > 2000)
2446:                                && (div < 1024)) {
2447:                            div = 1024;
2448:                            verticalAxisValueString2 = "K"
2449:                                    + verticalAxisValueString2; // NOI18N
2450:                        }
2451:                    }
2452:
2453:                    firstValue = model.getMinDisplayYValue(1) / (double) div;
2454:                    lastValue = model.getMaxDisplayYValue(1) / (double) div;
2455:
2456:                    if ((lastValue - firstValue) > 0) {
2457:                        double factor = (double) (chartHeight - topChartMargin)
2458:                                / (lastValue - firstValue);
2459:                        optimalUnits = DecimalAxisUtils.getOptimalUnits(factor,
2460:                                minimumVerticalMarksDistance);
2461:
2462:                        if (optimalUnits > 0) {
2463:                            long firstMark = Math
2464:                                    .max((long) (Math.ceil(firstValue
2465:                                            / optimalUnits) * optimalUnits), 0);
2466:                            long currentMark = firstMark;
2467:
2468:                            double currentMarkRel = currentMark - firstValue;
2469:                            int markPosition = (chartInsets.top + chartHeight)
2470:                                    - (int) (currentMarkRel * factor);
2471:
2472:                            while (markPosition >= chartInsets.top) {
2473:                                if (markPosition <= (chartInsets.top + chartHeight)) {
2474:                                    drawVerticalAxisLegendSegment2(g2,
2475:                                            currentMark, optimalUnits,
2476:                                            markPosition);
2477:                                }
2478:
2479:                                currentMark += optimalUnits;
2480:                                currentMarkRel = currentMark - firstValue;
2481:                                markPosition = (chartInsets.top + chartHeight)
2482:                                        - (int) (currentMarkRel * factor);
2483:                            }
2484:                        }
2485:                    }
2486:
2487:                    verticalAxisValueString2 = tmp;
2488:                }
2489:            }
2490:
2491:            private void drawVerticalMeshSegment(Graphics2D g2,
2492:                    Shape chartClip, int x1, int y1, int x2, int y2) {
2493:                g2.setClip(chartClip);
2494:                //g2.setClip(chartInsets.left, chartInsets.top, chartWidth, chartHeight);
2495:                g2.setPaint(verticalMeshPaint);
2496:                g2.setStroke(verticalMeshStroke);
2497:                g2.drawLine(x1, y1, x2, y2);
2498:            }
2499:
2500:            // sets zoom & offset according to current selection
2501:            private void fitToSelection() {
2502:                fitToViewRectangle(selectionX, selectionY, selectionWidth,
2503:                        selectionHeight);
2504:            }
2505:
2506:            // applies fit to window scale
2507:            private void fitToWindow() {
2508:                dataViewWidth = chartWidth;
2509:                changeZoom((double) (chartWidth)
2510:                        / (double) (model.getMaxXValue() - dataOffsetX));
2511:                lastScaleXValid = false;
2512:                changePan(0);
2513:                lastViewOffsetXValid = false;
2514:                offScreenImageInvalid = true;
2515:                doRepaint(true);
2516:            }
2517:
2518:            private void paintHorizontalTimeMarkString(Graphics2D g2,
2519:                    long currentMark, int x) {
2520:                int y = chartInsets.top + chartHeight
2521:                        + horizontalAxisFont.getSize()
2522:                        + (verticalAxisFont.getSize() / 2);
2523:
2524:                int markStringMillisMargin = 0; // space between mark's string without milliseconds and mark's milliseconds string
2525:                int markStringMillisReduce = 2; // markStringNoMillis.height - markStringMillisReduce = markStringMillis.height
2526:
2527:                String markStringNoMillis = DateTimeAxisUtils
2528:                        .getTimeMarkNoMillisString(currentMark, optimalUnits,
2529:                                useDayInTimeLegend);
2530:                int wMarkStringNoMillis = g2.getFontMetrics(horizontalAxisFont)
2531:                        .stringWidth(markStringNoMillis); // width of the mark's string without milliseconds
2532:                String markStringMillis = DateTimeAxisUtils
2533:                        .getTimeMarkMillisString(currentMark, optimalUnits);
2534:
2535:                if (!markStringMillis.equals("")) {
2536:                    markStringMillis = "." + markStringMillis; // NOI18N
2537:                }
2538:
2539:                int wMarkStringMillis = g2.getFontMetrics(
2540:                        horizontalAxisFontSmall).stringWidth(markStringMillis); // width of the mark's milliseconds string
2541:
2542:                int xMarkStringNoMillis = x
2543:                        - ((wMarkStringNoMillis + wMarkStringMillis) / 2) + 1; // x-position of the mark's string without milliseconds
2544:                int xMarkStringMillis = xMarkStringNoMillis
2545:                        + wMarkStringNoMillis + markStringMillisMargin; // x-position of the mark's milliseconds string
2546:
2547:                g2.setFont(horizontalAxisFont);
2548:                g2.drawString(markStringNoMillis, xMarkStringNoMillis, y);
2549:
2550:                g2.setFont(horizontalAxisFontSmall);
2551:                g2.drawString(markStringMillis, xMarkStringMillis, y
2552:                        - markStringMillisReduce + 1);
2553:            }
2554:
2555:            private void paintVerticalTimeMarkString(Graphics2D g2,
2556:                    long currentMark, long optimalUnits, int y) {
2557:                String currentMarkString = getVerticalAxisMarkString(currentMark);
2558:                int currentMarkWidth = g2.getFontMetrics(verticalAxisFont)
2559:                        .stringWidth(currentMarkString);
2560:                int currentMarkX = chartInsets.left - currentMarkWidth - 6;
2561:                int currentMarkY = (y + (verticalAxisFont.getSize() / 2)) - 2;
2562:
2563:                if (useSecondaryVerticalAxis) {
2564:                    g2.setPaint(model.getSeriesColor(0));
2565:                }
2566:
2567:                g2.setFont(verticalAxisFont);
2568:                g2.drawString(currentMarkString, currentMarkX, currentMarkY);
2569:            }
2570:
2571:            private void paintVerticalTimeMarkString2(Graphics2D g2,
2572:                    long currentMark, long optimalUnits, int y) {
2573:                if (currentMark > model.getMaxDisplayYValue(1)) {
2574:                    return;
2575:                }
2576:
2577:                String currentMarkString = getVerticalAxisMarkString2(currentMark);
2578:                int currentMarkX = chartInsets.left + chartWidth + 6;
2579:                int currentMarkY = (y + (verticalAxisFont.getSize() / 2)) - 2;
2580:
2581:                g2.setPaint(model.getSeriesColor(1));
2582:                g2.setFont(verticalAxisFont);
2583:                g2.drawString(currentMarkString, currentMarkX, currentMarkY);
2584:            }
2585:
2586:            // --- Selection interaction stuff -------------------------------------------
2587:            private void performSelectionDone() {
2588:                if (selectionWidth > 0) { // valid zoom-in selection
2589:                    fitToSelection();
2590:                } else if (selectionWidth < 0) { // valid zoom-out selection
2591:                    fitToWindow();
2592:                } else {
2593:                    // no selection, hide selection boundary
2594:                    doRepaint(false);
2595:                }
2596:            }
2597:
2598:            // --- offscreen image buffer resize routines  -------------------------------
2599:            private void updateOffScreenImageSize() {
2600:                // component insets
2601:                insets = getInsets();
2602:
2603:                // area of component
2604:                drawWidth = getWidth() - insets.left - insets.right - 1;
2605:                drawHeight = getHeight() - insets.top - insets.bottom - 1;
2606:
2607:                if ((drawWidth > 0) && (drawHeight > 0)) {
2608:                    // offscreen image buffer
2609:                    //offScreenImage = createVolatileImage(drawWidth + 1, drawHeight + 1); // for volatile offscreen image buffer
2610:                    offScreenImage = (BufferedImage) createImage(drawWidth + 1,
2611:                            drawHeight + 1);
2612:                    offScreenGraphics = offScreenImage.createGraphics();
2613:
2614:                    // chart insets
2615:                    chartInsets.top = 20;
2616:
2617:                    if (useSecondaryVerticalAxis) {
2618:                        checkRightChartMargin();
2619:                    } else {
2620:                        chartInsets.right = 0; // NOI18N
2621:                    }
2622:
2623:                    chartInsets.bottom = horizontalAxisFont.getSize()
2624:                            + (verticalAxisFont.getSize() / 2) + 8;
2625:                    chartInsets.left = offScreenGraphics.getFontMetrics(
2626:                            verticalAxisFont).stringWidth("MMMMM"); // NOI18N
2627:
2628:                    // area of graph inside component
2629:                    chartWidth = drawWidth - chartInsets.left
2630:                            - chartInsets.right;
2631:                    chartHeight = drawHeight - chartInsets.top
2632:                            - chartInsets.bottom;
2633:
2634:                    // clear component area using user-defined background color
2635:                    Area chartArea = new Area(new Rectangle(chartInsets.left,
2636:                            chartInsets.top, chartWidth, chartHeight));
2637:
2638:                    Area componentArea = new Area(new Rectangle(0, 0,
2639:                            drawWidth + 1, drawHeight + 1));
2640:                    componentArea.subtract(chartArea);
2641:
2642:                    offScreenGraphics.setPaint(backgroundPaint);
2643:                    offScreenGraphics.fill(componentArea);
2644:
2645:                    offScreenGraphics.setPaint(chartPaint);
2646:                    offScreenGraphics.fill(chartArea);
2647:
2648:                    // vertical and horizontal axis baseline
2649:                    offScreenGraphics.setPaint(horizontalAxisPaint);
2650:                    offScreenGraphics.setStroke(horizontalAxisStroke);
2651:                    offScreenGraphics.drawLine(chartInsets.left,
2652:                            chartInsets.top + chartHeight,
2653:                            (chartInsets.left + chartWidth) - 1,
2654:                            chartInsets.top + chartHeight);
2655:
2656:                    offScreenGraphics.setPaint(verticalAxisPaint);
2657:                    offScreenGraphics.setStroke(verticalAxisStroke);
2658:                    offScreenGraphics.drawLine(chartInsets.left - 1,
2659:                            chartInsets.top, chartInsets.left - 1,
2660:                            chartInsets.top + chartHeight);
2661:
2662:                    if (useSecondaryVerticalAxis) {
2663:                        offScreenGraphics.drawLine(chartInsets.left
2664:                                + chartWidth, chartInsets.top, chartInsets.left
2665:                                + chartWidth, chartInsets.top + chartHeight);
2666:                    }
2667:
2668:                    // use antialiasing
2669:                    offScreenGraphics.setRenderingHint(
2670:                            RenderingHints.KEY_ANTIALIASING,
2671:                            RenderingHints.VALUE_ANTIALIAS_ON);
2672:                    offScreenGraphics.setRenderingHint(
2673:                            RenderingHints.KEY_RENDERING,
2674:                            RenderingHints.VALUE_RENDER_SPEED);
2675:
2676:                    // new offscreen image needs to be painted in the nearest paint()
2677:                    offScreenImageInvalid = true;
2678:
2679:                    // data on Y-axis needs to be rescaled
2680:                    lastScaleYValid = false;
2681:
2682:                    // data at the end of graph may changed (component resized => more/less data could appear)
2683:                    trailingItemVisible = true;
2684:
2685:                    // data need to be repainted from beginning of visible area
2686:                    lastViewOffsetXValid = false;
2687:                    lastScaleXValid = false;
2688:
2689:                    horizontalAxisMarksClip.setRect(chartInsets.left - 1,
2690:                            chartInsets.top + chartHeight + 1, chartWidth + 1,
2691:                            3);
2692:                    horizontalAxisClip.setRect(HORIZONTAL_LEGEND_MARGIN,
2693:                            chartInsets.top + chartHeight
2694:                                    + (verticalAxisFont.getSize() / 2) + 1,
2695:                            drawWidth - (2 * HORIZONTAL_LEGEND_MARGIN),
2696:                            horizontalAxisFont.getSize() + 2);
2697:                    verticalAxisClip.setRect(0, chartInsets.top
2698:                            - (verticalAxisFont.getSize() / 2),
2699:                            chartInsets.left - 1, chartHeight
2700:                                    + verticalAxisFont.getSize());
2701:
2702:                    if (useSecondaryVerticalAxis) {
2703:                        verticalAxisClip2.setRect(chartInsets.left + chartWidth
2704:                                + 1, chartInsets.top
2705:                                - (verticalAxisFont.getSize() / 2),
2706:                                chartInsets.right - 1, chartHeight
2707:                                        + verticalAxisFont.getSize());
2708:                    }
2709:
2710:                    if (model != null) {
2711:                        updateScaleFactors();
2712:                    }
2713:
2714:                    doRepaint(false);
2715:                }
2716:            }
2717:
2718:            // --- data coordinates <-> chart (component) coordinates conversion routines
2719:            private void updateScaleFactors() {
2720:                if (!useDayInTimeLegend) {
2721:                    Calendar firstTimestampCalendar = Calendar.getInstance();
2722:                    firstTimestampCalendar.setTime(new Date(model
2723:                            .getMinXValue()));
2724:
2725:                    Calendar lastTimestampCalendar = Calendar.getInstance();
2726:                    lastTimestampCalendar
2727:                            .setTime(new Date(model.getMaxXValue()));
2728:
2729:                    if ((firstTimestampCalendar.get(Calendar.DAY_OF_WEEK) != lastTimestampCalendar
2730:                            .get(Calendar.DAY_OF_WEEK))
2731:                            || (firstTimestampCalendar.get(Calendar.MONTH) != lastTimestampCalendar
2732:                                    .get(Calendar.MONTH))
2733:                            || (firstTimestampCalendar.get(Calendar.YEAR) != lastTimestampCalendar
2734:                                    .get(Calendar.YEAR))) {
2735:                        useDayInTimeLegend = true;
2736:                    }
2737:                }
2738:
2739:                long dataLimitX;
2740:
2741:                if (customizedEmptyAppearance && (model.getItemCount() < 2)) {
2742:                    dataLimitX = lastValueH;
2743:                    dataOffsetX = firstValueH;
2744:                } else {
2745:                    dataLimitX = model.getMaxXValue();
2746:                    dataOffsetX = model.getMinXValue();
2747:                }
2748:
2749:                if (fitToWindow) {
2750:                    dataViewWidth = chartWidth;
2751:                    scaleFactorX = (double) (chartWidth)
2752:                            / (double) (dataLimitX - dataOffsetX);
2753:                    lastScaleXValid = false;
2754:                    lastLeadingItemIndex = 0;
2755:                    trailingItemVisible = false;
2756:                    changePan(0);
2757:                    lastViewOffsetXValid = false;
2758:                } else {
2759:                    if ((viewScaleX == 0) && (chartWidth > 0)) {
2760:                        viewScaleX = (double) (chartWidth)
2761:                                / dataWidthAtTrackingEndSwitch; // workaround for Telemetry Overview initialization
2762:                    }
2763:
2764:                    dataViewWidth = (long) ((dataLimitX - dataOffsetX) * viewScaleX);
2765:                    scaleFactorX = viewScaleX;
2766:
2767:                    if (trackingEnd && !trailingItemVisible) {
2768:                        changePan(-chartWidth + dataViewWidth);
2769:                        lastViewOffsetXValid = false;
2770:                    }
2771:                }
2772:
2773:                if (fitToWindow) {
2774:                    changeZoom(scaleFactorX);
2775:                } else {
2776:                    viewScaleX = scaleFactorX;
2777:                }
2778:
2779:                boolean yScaleChanged = false;
2780:
2781:                for (int seriesIndex = 0; seriesIndex < model.getSeriesCount(); seriesIndex++) {
2782:                    long maxYValue = model.getMaxDisplayYValue(seriesIndex);
2783:                    long minYValue = model.getMinDisplayYValue(seriesIndex);
2784:
2785:                    if (lastMaxYs[seriesIndex] != maxYValue) {
2786:                        yScaleChanged = true;
2787:                    }
2788:
2789:                    if (lastMinYs[seriesIndex] != minYValue) {
2790:                        yScaleChanged = true;
2791:                    }
2792:
2793:                    lastMaxYs[seriesIndex] = maxYValue;
2794:                    lastMinYs[seriesIndex] = minYValue;
2795:
2796:                    dataOffsetsY[seriesIndex] = minYValue;
2797:                    scaleFactorsY[seriesIndex] = (double) (chartHeight - topChartMargin)
2798:                            / (double) (maxYValue - dataOffsetsY[seriesIndex]);
2799:                }
2800:
2801:                lastScaleYValid = lastScaleYValid && !yScaleChanged;
2802:
2803:                updateScrollBarValues();
2804:
2805:                scaleFactorsNeedUpdate = false;
2806:            }
2807:
2808:            private void updateScrollBarValues() {
2809:                if (scrollBar != null) {
2810:                    scrollBarLongToIntFactor = ((dataViewWidth > Integer.MAX_VALUE) ? (Integer.MAX_VALUE / (double) dataViewWidth)
2811:                            : 1);
2812:
2813:                    int value = getIntPosition();
2814:                    int extent = getIntExtent();
2815:                    int minimum = getIntMinimum();
2816:                    int maximum = getIntMaximum();
2817:
2818:                    if (dataViewWidth <= chartWidth) {
2819:                        if (scrollBar.isEnabled()) {
2820:                            scrollBar.setEnabled(false);
2821:                        }
2822:                    } else {
2823:                        if (!scrollBar.isEnabled()) {
2824:                            scrollBar.setEnabled(true);
2825:                        }
2826:
2827:                        if (!scrollBar.getValueIsAdjusting()) {
2828:                            internalScrollBarChange = true;
2829:                            scrollBar.setUnitIncrement(getIntUnitIncrement());
2830:                            scrollBar.setBlockIncrement(getIntBlockIncrement());
2831:                            scrollBar
2832:                                    .setValues(value, extent, minimum, maximum);
2833:                        } else {
2834:                            scrollBarValuesDirty = true;
2835:                        }
2836:                    }
2837:                }
2838:            }
2839:
2840:            private void updateTrailingItemVisible() {
2841:                if (trackingEnd) {
2842:                    trailingItemVisible = false;
2843:                }
2844:
2845:                long preTrailingItemX = getDataToViewX(model.getXValue(model
2846:                        .getItemCount() - 2));
2847:                boolean preTrailingItemVisible = ((preTrailingItemX >= 0) && (preTrailingItemX <= ((chartWidth + chartInsets.left) - 1)));
2848:
2849:                long trailingItemX = getDataToViewX(model.getXValue(model
2850:                        .getItemCount() - 1));
2851:                trailingItemVisible = ((trailingItemX >= 0) && (trailingItemX <= ((chartWidth + chartInsets.left) - 1)));
2852:
2853:                if (preTrailingItemVisible && !trailingItemVisible) {
2854:                    changeTrackingEnd(true);
2855:                }
2856:            }
2857:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.