2010-11-09 Francois Chouinard <fchouinard@gmail.com> Contribution for Bug315307
[deliverable/tracecompass.git] / org.eclipse.linuxtools.lttng.ui / src / org / eclipse / linuxtools / lttng / ui / views / histogram / ParentHistogramCanvasPaintListener.java
1 /*******************************************************************************
2 * Copyright (c) 2009 Ericsson
3 *
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
8 *
9 * Contributors:
10 * William Bourque - Initial API and implementation
11 *
12 * Modifications:
13 * 2010-07-16 Yuriy Vashchuk - Base class simplification. Redraw bug correction.
14 * Double Buffering implementation.
15 *******************************************************************************/
16
17 package org.eclipse.linuxtools.lttng.ui.views.histogram;
18
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.events.PaintEvent;
21
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.graphics.GC;
24 import org.eclipse.swt.graphics.Image;
25
26 /**
27 * <b><u>HistogramCanvasPaintListener</u></b>
28 * <p>
29 * Implementation of a PaintListener for the specific need of the ParentHistogramCanvas.
30 * <p>
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>
33 *
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>
38 */
39 public class ParentHistogramCanvasPaintListener extends HistogramCanvasPaintListener
40 {
41 private static ParentHistogramCanvas parentCanvas = null;
42
43 /**
44 * ParentHistogramCanvasPaintListener constructor
45 *
46 * @param newCanvas Related canvas
47 */
48 public ParentHistogramCanvasPaintListener(ParentHistogramCanvas newCanvas) {
49 parentCanvas = newCanvas;
50 }
51
52 // *** VERIFY ***
53 // Is it good to put this synchronized?
54 //
55 /**
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>
59 *
60 * Note : This draw function is somewhat heavier than the default one.
61 *
62 * @param event The generated paint event when redraw is called.
63 */
64 @Override
65 public synchronized void drawHistogram(GC imageGC, Image image) {
66 final HistogramContent tmpContent = parentCanvas.getHistogramContent();
67 final int tmpBarWidth = tmpContent.getBarsWidth();
68
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) ));
71
72 // First clear the whole canvas to have a clean section where to draw
73 clearDrawingSection(imageGC, image, parentCanvas);
74
75 // Make sure the canvas didn't change size, it which case we need to recalculate our heights
76 recalculateHeightIfCanvasSizeChanged();
77
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;
82
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
86 int posInPower = 0;
87 int widthFilled = 0;
88
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) );
91
92 // Read from 0 up to the currently ready position
93 // We advance by "intervalDifference" as the bars migth not represent 1 interval only
94 int itemWidth = 0;
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;
102
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;
110 }
111 }
112
113 // *** NOTE ***
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(
118 widthFilled,
119 (thisElementHeight > image.getBounds().height ? 0 : image.getBounds().height - thisElementHeight),
120 itemWidth,
121 thisElementHeight
122 );
123
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
128 posInPower++;
129 }
130 }
131
132 /*
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>
135 *
136 * The factor might change if the canvas is resized by a big factor.<p>
137 */
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
144 //
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 );
150
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();
158 }
159 }
160
161 /**
162 * Function called when the canvas need to redraw.<p>
163 *
164 * @param event The generated paint event when redraw is called.
165 */
166 private final String DATA_KEY = "double-buffer-image"; //$NON-NLS-1$
167 @Override
168 public void paintControl(PaintEvent event) {
169
170 if (parentCanvas.getSize().x > 0 && parentCanvas.getSize().y > 0) {
171 Image image = (Image) parentCanvas.getData(DATA_KEY);
172
173 // Creates new image only absolutely necessary.
174 if (image == null
175 || image.getBounds().width != parentCanvas.getClientArea().width
176 || image.getBounds().height != parentCanvas.getClientArea().height) {
177
178 image = new Image(
179 event.display,
180 parentCanvas.getClientArea().width,
181 parentCanvas.getClientArea().height
182 );
183
184 parentCanvas.setData(DATA_KEY, image);
185 // isFinished = false;
186 }
187
188 // Initializes the graphics context of the image.
189 GC imageGC = new GC(image);
190
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) ) {
194
195 // Call the function that draw the bars
196 // if (!isFinished) {
197 drawHistogram(imageGC, image);
198 // isFinished = HistogramCanvas.getHistogramView().getDataBackgroundFullRequest().isCompleted();
199 // }
200
201 Image img = new Image (image.getDevice(), image, SWT.IMAGE_COPY);
202 GC imgGC = new GC(img);
203
204 // If we have a selected window set to visible, call the function to draw it
205 if ( (parentCanvas.getCurrentWindow() != null) && (parentCanvas.getCurrentWindow().getSelectedWindowVisible()) ) {
206 drawSelectedWindow(
207 imgGC,
208 img
209 );
210 }
211
212 // Draws the buffer image onto the canvas.
213 event.gc.drawImage(img, 0, 0);
214
215 imgGC.dispose();
216 img.dispose();
217 }
218
219 imageGC.dispose();
220 }
221
222 }
223
224 /**
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"
228 *
229 * @param imageGC GC content.
230 * @param image Image content.
231 */
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();
237
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;
242
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) {
248 imageGC.drawLine(
249 positionCenter - crosshairHalfWidth,
250 imageHalfHeight - 20,
251 positionCenter - crosshairHalfWidth,
252 imageHalfHeight + 20
253 );
254 }
255 if(positionRight - positionLeft > 40) {
256 imageGC.drawLine(
257 positionCenter - 20,
258 imageHalfHeight - crosshairHalfWidth,
259 positionCenter + 20,
260 imageHalfHeight - crosshairHalfWidth
261 );
262 }
263
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,
269 lineHalfWidth,
270 (positionRight - positionLeft) - lineHalfWidth,
271 imageHeight - HistogramConstant.SELECTION_LINE_WIDTH,
272 15,
273 15
274 );
275
276 // Show the selection
277 imageGC.setBackground( new Color (imageGC.getDevice(), 255, 128, 0) );
278 imageGC.setAlpha(25);
279 imageGC.fillRoundRectangle(
280 positionLeft + 1,
281 lineHalfWidth + 1,
282 (positionRight - positionLeft) - lineHalfWidth - 1,
283 imageHeight - lineHalfWidth - 1,
284 15,
285 15
286 );
287 }
288
289 }
This page took 0.038001 seconds and 5 git commands to generate.