1 /*******************************************************************************
2 * Copyright (c) 2009 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * William Bourque - Initial API and implementation
13 * 2010-07-16 Yuriy Vashchuk - Base class simplification. Redraw bug correction.
14 * Double Buffering implementation.
15 *******************************************************************************/
17 package org
.eclipse
.linuxtools
.lttng
.ui
.views
.histogram
;
19 import org
.eclipse
.swt
.SWT
;
20 import org
.eclipse
.swt
.events
.PaintEvent
;
22 import org
.eclipse
.swt
.graphics
.Color
;
23 import org
.eclipse
.swt
.graphics
.GC
;
24 import org
.eclipse
.swt
.graphics
.Image
;
27 * <b><u>HistogramCanvasPaintListener</u></b>
29 * Implementation of a PaintListener for the specific need of the ParentHistogramCanvas.
31 * The difference with the default one is that this one take a content that is the power of 2 higher
32 * than the display size.<p>
34 * When it is time to draw, it takes the closest power of 2 smaller than the canvas size; it is then easy to
35 * concatenate the interval as they are both power of 2.<p>
36 * The difference between the power of 2 and the not-power-of-2 canvas size is then filled by drawing bar that are
37 * slightly larger every (power/canvasSize) interval.<p>
39 public class ParentHistogramCanvasPaintListener
extends HistogramCanvasPaintListener
41 private static ParentHistogramCanvas parentCanvas
= null;
44 * ParentHistogramCanvasPaintListener constructor
46 * @param newCanvas Related canvas
48 public ParentHistogramCanvasPaintListener(ParentHistogramCanvas newCanvas
) {
49 parentCanvas
= newCanvas
;
53 // Is it good to put this synchronized?
56 * Draw the histogram bars in the canvas.<p>
57 * This drawing function expect the content to be the power of 2 higher than the canvas size.
58 * The bars size will be slightly dynamic to fill the gap between the power and the canvas size.<p>
60 * Note : This draw function is somewhat heavier than the default one.
62 * @param event The generated paint event when redraw is called.
65 public synchronized void drawHistogram(GC imageGC
, Image image
) {
66 final HistogramContent tmpContent
= parentCanvas
.getHistogramContent();
67 final int tmpBarWidth
= tmpContent
.getBarsWidth();
69 // Calculate the closest power of 2 just smaller than the canvas size
70 final int closestPowerToCanvas
= (int)Math
.pow(2, Math
.floor( Math
.log( image
.getBounds().width
) / Math
.log(2.0) ));
72 // First clear the whole canvas to have a clean section where to draw
73 clearDrawingSection(imageGC
, image
, parentCanvas
);
75 // Make sure the canvas didn't change size, it which case we need to recalculate our heights
76 recalculateHeightIfCanvasSizeChanged();
78 // Calculate the factor of difference between canvas and the power
79 final double factor
= (double)image
.getBounds().width
/ (double)closestPowerToCanvas
;
80 // Calculate how many interval will need to be concatenated into one pixel
81 final int intervalDifference
= (tmpContent
.getNbElement() / closestPowerToCanvas
)*tmpBarWidth
;
83 // This keep a link between the position in "power" and the pixel we draw
84 // I.e. correlation between position in the power ("fake" pixels) and the position in the canvas ("real" pixels)
85 // So if pos == 30 and factor == 1.5, we know that the pixel that draw this pos is (30 * 1.5) == 45
89 // This will be the color for all the bars that wil be draw below.
90 imageGC
.setBackground( new Color( imageGC
.getDevice(), 74, 112, 139) );
92 // Read from 0 up to the currently ready position
93 // We advance by "intervalDifference" as the bars migth not represent 1 interval only
95 int thisElementHeight
= 0;
96 for( int contentPos
=0; contentPos
< tmpContent
.getReadyUpToPosition(); contentPos
+= intervalDifference
) {
97 // Width of the current item.
98 // Vary because of the difference between the power of 2 and the canvas size
99 // Ex: if power == 1024 and canvas == 1500, a bars every (1024/1500) will have a size of 2 instead of 1.
100 itemWidth
= (int)( Math
.ceil((double)(posInPower
+1)*factor
) - Math
.ceil((double)posInPower
*factor
) );
101 itemWidth
= itemWidth
* tmpBarWidth
;
103 // Concatenate all the element in the interval
104 // Ex : if power == 1024 and content == 2048, every (2048/1024)*bars_width will be concatenated
105 thisElementHeight
= 0;
106 for ( int concatPos
=0; concatPos
<intervalDifference
; concatPos
++) {
107 // Make sure we don't cross the last element available.
108 if ( contentPos
+ concatPos
< tmpContent
.getReadyUpToPosition() ) {
109 thisElementHeight
+= tmpContent
.getElementByIndex(contentPos
+ concatPos
).intervalHeight
;
114 // Y Position in a canvas is REVERSED, so "0" is on top of the screen and "MAX" is on bottom.
115 // Not very instinctive, isn't it?
116 // Draw our rectangle
117 imageGC
.fillRectangle(
119 (thisElementHeight
> image
.getBounds().height ?
0 : image
.getBounds().height
- thisElementHeight
),
124 // Keep in a variable how much width we filld so far
125 widthFilled
+= itemWidth
;
126 // Keep a correlation between fake_pixel -> real_pixel,
127 // this is used to calculate the width of each element
133 * The function will make sure that the "max difference average" factor is still the same as before;
134 * if not, the heigth of the events will be recalculated.<p>
136 * The factor might change if the canvas is resized by a big factor.<p>
138 protected void recalculateHeightIfCanvasSizeChanged() {
139 final HistogramContent tmpContent
= parentCanvas
.getHistogramContent();
140 // We need to ajust the "maxDifferenceToAverageFactor" as the bars we draw might be slitghly larger than the value asked
141 // Each "interval" are concatenated when draw so the worst case should be :
142 // contentSize / (closest power of 2 to canvasMaxSize)
143 // Ex : if canvasSize is 1500 -> (2048 / 1024) == 2 so maxDiff should be twice larger
145 // His is set in the create content of the canvas, but we need to recalculate it
146 // here because the window might have been resized!
147 final int exp
= (int)Math
.floor( Math
.log( (double)tmpContent
.getCanvasWindowSize() ) / Math
.log(2.0) );
148 final int contentSize
= (int)Math
.pow(2, exp
);
149 final double maxBarsDiffFactor
= ((double)tmpContent
.getNbElement() / (double)contentSize
);
151 // Floating point comparaison :
152 // We consider it is different if the difference is greater than 10^-3
153 if ( Math
.abs(maxBarsDiffFactor
- tmpContent
.getMaxDifferenceToAverageFactor()) > 0.001 ) {
154 // The factor changed! That's unfortunate because it will take a while to recalculate.
155 tmpContent
.setMaxDifferenceToAverageFactor(maxBarsDiffFactor
);
156 tmpContent
.recalculateHeightFactor();
157 tmpContent
.recalculateEventHeight();
162 * Function called when the canvas need to redraw.<p>
164 * @param event The generated paint event when redraw is called.
166 private final String DATA_KEY
= "double-buffer-image"; //$NON-NLS-1$
168 public void paintControl(PaintEvent event
) {
170 if (parentCanvas
.getSize().x
> 0 && parentCanvas
.getSize().y
> 0) {
171 Image image
= (Image
) parentCanvas
.getData(DATA_KEY
);
173 // Creates new image only absolutely necessary.
175 || image
.getBounds().width
!= parentCanvas
.getClientArea().width
176 || image
.getBounds().height
!= parentCanvas
.getClientArea().height
) {
180 parentCanvas
.getClientArea().width
,
181 parentCanvas
.getClientArea().height
184 parentCanvas
.setData(DATA_KEY
, image
);
185 // isFinished = false;
188 // Initializes the graphics context of the image.
189 GC imageGC
= new GC(image
);
191 // If the content is null or has rady to draw we quit the function here
192 if ( (parentCanvas
.getHistogramContent() != null)
193 && (parentCanvas
.getHistogramContent().getReadyUpToPosition() != 0) ) {
195 // Call the function that draw the bars
196 // if (!isFinished) {
197 drawHistogram(imageGC
, image
);
198 // isFinished = HistogramCanvas.getHistogramView().getDataBackgroundFullRequest().isCompleted();
201 Image img
= new Image (image
.getDevice(), image
, SWT
.IMAGE_COPY
);
202 GC imgGC
= new GC(img
);
204 // If we have a selected window set to visible, call the function to draw it
205 if ( (parentCanvas
.getCurrentWindow() != null) && (parentCanvas
.getCurrentWindow().getSelectedWindowVisible()) ) {
212 // Draws the buffer image onto the canvas.
213 event
.gc
.drawImage(img
, 0, 0);
225 * Draw the selection window in the canvas.<p>
226 * This draw a square around the selected section with a crosshair in the middle.
227 * The square cannot be smaller than "MINIMUM_WINDOW_WIDTH"
229 * @param imageGC GC content.
230 * @param image Image content.
232 public void drawSelectedWindow(GC imageGC
, Image image
) {
233 // Get the window position... this would fail if the window is not initialized yet
234 final int positionCenter
= parentCanvas
.getCurrentWindow().getWindowXPositionCenter();
235 final int positionLeft
= parentCanvas
.getCurrentWindow().getWindowXPositionLeft();
236 final int positionRight
= parentCanvas
.getCurrentWindow().getWindowXPositionRight();
238 final int imageHeight
= image
.getBounds().height
;
239 final int imageHalfHeight
= image
.getBounds().height
/ 2;
240 final int crosshairHalfWidth
= HistogramConstant
.SELECTION_CROSSHAIR_WIDTH
/ 2;
241 final int lineHalfWidth
= HistogramConstant
.SELECTION_LINE_WIDTH
/ 2;
243 // Draw the crosshair
244 imageGC
.setForeground( new Color (imageGC
.getDevice(), 255, 128, 0) );
245 imageGC
.setLineWidth(HistogramConstant
.SELECTION_CROSSHAIR_WIDTH
);
246 imageGC
.setLineStyle(SWT
.LINE_SOLID
);
247 if(imageHeight
> 40) {
249 positionCenter
- crosshairHalfWidth
,
250 imageHalfHeight
- 20,
251 positionCenter
- crosshairHalfWidth
,
255 if(positionRight
- positionLeft
> 40) {
258 imageHalfHeight
- crosshairHalfWidth
,
260 imageHalfHeight
- crosshairHalfWidth
264 // Draw the selection window square
265 imageGC
.setLineWidth(HistogramConstant
.SELECTION_LINE_WIDTH
);
266 imageGC
.setLineStyle(SWT
.LINE_SOLID
);
267 imageGC
.drawRoundRectangle(
268 positionLeft
+ lineHalfWidth
,
270 (positionRight
- positionLeft
) - lineHalfWidth
,
271 imageHeight
- HistogramConstant
.SELECTION_LINE_WIDTH
,
276 // Show the selection
277 imageGC
.setBackground( new Color (imageGC
.getDevice(), 255, 128, 0) );
278 imageGC
.setAlpha(25);
279 imageGC
.fillRoundRectangle(
282 (positionRight
- positionLeft
) - lineHalfWidth
- 1,
283 imageHeight
- lineHalfWidth
- 1,