From 7ef9ae3f484399f1f808b84748a4a678c2f7d18d Mon Sep 17 00:00:00 2001 From: William Bourque Date: Fri, 23 Apr 2010 19:05:36 +0000 Subject: [PATCH] Very stable and almost final version ofthe histogram view. Fix resize, silly SWT bug, added good behavior on small screen, correct difference to average algorithm. --- .../HistogramCanvasControlListener.java | 2 +- .../ui/views/histogram/HistogramContent.java | 44 +++++-- .../ui/views/histogram/HistogramRequest.java | 31 +++-- .../ui/views/histogram/HistogramView.java | 108 ++++++++++++++---- .../histogram/ParentHistogramCanvas.java | 10 ++ .../ParentHistogramCanvasPaintListener.java | 31 +++++ 6 files changed, 175 insertions(+), 51 deletions(-) diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramCanvasControlListener.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramCanvasControlListener.java index cda50f53e1..6df72388cb 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramCanvasControlListener.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramCanvasControlListener.java @@ -39,7 +39,7 @@ public class HistogramCanvasControlListener implements ControlListener { * * We need to tell the content that the canvas size changed and to recenter the windows * - * @param event The controle event generated by the resize. + * @param event The control event generated by the resize. */ public void controlResized(ControlEvent event) { diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramContent.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramContent.java index 281406ddc4..6ddad8303f 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramContent.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramContent.java @@ -36,6 +36,8 @@ public class HistogramContent { // This value is used to calculate at which point we should "cut" bar that are too tall. // Default value is large enought so that no bar should be cut protected Double maxDifferenceToAverage = HistogramConstant.DEFAULT_DIFFERENCE_TO_AVERAGE; + // This is a factor we might apply on the max difference to average, as example if we concatenate interval together + protected Double maxDifferenceFactor = 1.0; // By default we will only consider element up to this position protected Integer readyUpToPosition = 0; @@ -75,9 +77,7 @@ public class HistogramContent { canvasWindowSize = newCanvasSize; barsWidth = newBarWidth; maxHeight = newMaxHeight; - - // Max difference is a bit special, we need to call set - setMaxDifferenceToAverage(newDiffToAverage); + maxDifferenceToAverage = newDiffToAverage; // Create a new element table from the above value // The table will not get initialized until resetTable() is called. @@ -480,9 +480,6 @@ public class HistogramContent { return endTime; } - // *** TODO *** - // Implement a way to "compress" a table if the endtime change. - // That way, the end time could change without having to reset the table data. /** * Setter for the end time of the content.

* Note : You probably want to call "resetTable()" if you change this, otherwise data might be inconsistent. @@ -530,9 +527,9 @@ public class HistogramContent { public void recalculateHeightFactor() { // Recalculate the new HeightFactor for the element; // the highest bar will get "maxHeight" and other bar a fraction of it. - // If a maxDifferenceToAverage exist, this is considered here - if ( heighestEventCount > (maxDifferenceToAverage * averageNumberOfEvents) ) { - heightFactor = (double)maxHeight/( maxDifferenceToAverage * (double)averageNumberOfEvents); + double diffToConsider = (maxDifferenceToAverage * maxDifferenceFactor * (double)barsWidth); + if ( heighestEventCount > (int)(diffToConsider * (double)averageNumberOfEvents) ) { + heightFactor = (double)maxHeight/( diffToConsider * (double)averageNumberOfEvents); } else { heightFactor = (double)maxHeight/(double)heighestEventCount; @@ -668,13 +665,38 @@ public class HistogramContent { * This determine at which point a bar too tall is "cut". Set a very large value (like 1000.0) to ignore. * * Note : this is used in some drawing calculation so make sure this number make sense. - * Note : the given number is multiplied by the bar width, as we have bigger bar (so more events as average to consider) * Note : you might want to call recalculateEventHeight() if you change this. * * @param newDiffToAverage The new maximum difference to the average to use. */ public void setMaxDifferenceToAverage(Double newDiffToAverage) { - maxDifferenceToAverage = (newDiffToAverage*barsWidth); + maxDifferenceToAverage = newDiffToAverage; + } + + + /** + * Getter for a factor applied to the max difference to the average height a bar can have.

+ * This is muliplied to maxDifferenceToAverage. Set to value 1.0 to ignore. + * + * Note : this is useful if you concatenate some intervals to gether but want the average to be consistent + * + * @return maximum difference to the average we currently use. + */ + public Double getMaxDifferenceToAverageFactor() { + return maxDifferenceFactor; + } + + /** + * Setter for a factor applied to the max difference to the average height a bar can have.

+ * + * Note : this is used in some drawing calculation so make sure this number make sense. + * Note : you might want to call recalculateEventHeight() if you change this. + * Note : setting to 0 will cause bar to have a zero size... use 1.0 to desactivate + * + * @param newFactor The new factor to use. + */ + public void setMaxDifferenceToAverageFactor(Double newFactor) { + maxDifferenceFactor = newFactor; } diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramRequest.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramRequest.java index 1e959d5925..db8db7cc79 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramRequest.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramRequest.java @@ -34,8 +34,6 @@ public class HistogramRequest extends TmfEventRequest { protected Integer lastDrawPosition = 0; - protected Boolean requestCompleted = false; - protected HistogramCanvas parentCanvas = null; /** @@ -87,7 +85,8 @@ public class HistogramRequest extends TmfEventRequest { // However, the request with number of events will loop until it reach its number or EOF // We have to filter out ourself the extra useless events! // - if ( (evt[0] != null) && (requestCompleted == false) ) { + if (evt[0] != null) { + LttngEvent tmpEvent = (LttngEvent)evt[0]; // This check is linked to the evil fix mentionned above @@ -143,19 +142,20 @@ public class HistogramRequest extends TmfEventRequest { redrawAsyncronously(); } } - else { - //System.out.println("Requested Timerange is : " + histogramContent.getStartTime() + " / " + histogramContent.getEndTime()); - //System.out.println("Time is : " + tmpEvent.getTimestamp().getValue()); - // *** FIXME *** - // *** EVIL FIX *** - // Because of the other evil bug (see above), we have to ignore extra useless events we will get - // However, we might be far away from the end so we better start a redraw now - if (tmpEvent.getTimestamp().getValue() >= histogramContent.getEndTime()) { - redrawAsyncronously(); - requestCompleted = true; - } - } } + // We got a null event! This mean we reach the end of the request. + // Save the last interval we had, so we won't miss the very last events at the end. + else { + // Save the last events + histogramContent.getElementByIndex(lastInterval).intervalNbEvents = nbEventsInInterval; + // We reached the end of the request, so assume we fill up the content as well + histogramContent.setReadyUpToPosition(histogramContent.getNbElement()); + + // If the interval wasn't null, count this as a "non empty" interval + if (nbEventsInInterval > 0) { + nbIntervalNotEmpty++; + } + } } /** @@ -191,7 +191,6 @@ public class HistogramRequest extends TmfEventRequest { @Override public void handleCancel() { redrawAsyncronously(); - requestCompleted = true; } /** diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramView.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramView.java index 5c27943421..afb414a05d 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramView.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/HistogramView.java @@ -11,7 +11,6 @@ *******************************************************************************/ package org.eclipse.linuxtools.lttng.ui.views.histogram; - import org.eclipse.linuxtools.lttng.event.LttngEvent; import org.eclipse.linuxtools.lttng.event.LttngTimestamp; import org.eclipse.linuxtools.tmf.event.TmfTimeRange; @@ -23,6 +22,8 @@ import org.eclipse.linuxtools.tmf.signal.TmfSignalHandler; import org.eclipse.linuxtools.tmf.signal.TmfTimeSynchSignal; import org.eclipse.linuxtools.tmf.ui.views.TmfView; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.layout.GridData; @@ -39,7 +40,7 @@ import org.eclipse.swt.widgets.Text; * This view is composed of 2 canvas, one for the whole experiment and one for the selectionned window in the experiment. * It also contain a certain number of controls to print or change informations about the experiment. */ -public class HistogramView extends TmfView { +public class HistogramView extends TmfView implements ControlListener { // *** TODO *** // Here is what's left to do in this view @@ -49,7 +50,6 @@ public class HistogramView extends TmfView { // updated from different threads; we need to carefully decide when/where to redraw them. // This is a real problem since there is a lot of thread going on in this view. // All basic control should be subclassed to offer "Asynchronous" functions. - // 2- Find a way to totally fix the SWT bug in createPartControl() public static final String ID = "org.eclipse.linuxtools.lttng.ui.views.histogram"; @@ -59,10 +59,11 @@ public class HistogramView extends TmfView { // Size of the "fulll trace" canvas private static final int FULL_TRACE_CANVAS_HEIGHT = 25; private static final int FULL_TRACE_BAR_WIDTH = 1; - private static final double FULL_TRACE_DIFFERENCE_TO_AVERAGE = 2.0; + private static final double FULL_TRACE_DIFFERENCE_TO_AVERAGE = 1.5; // Size of the "Selected Window" canvas private static final int SELECTED_WINDOW_CANVAS_WIDTH = 300; + private static final int SMALL_SELECTED_WINDOW_CANVAS_WIDTH = 200; private static final int SELECTED_WINDOW_CANVAS_HEIGHT = 60; private static final int SELECTED_WINDOW_BAR_WIDTH = 1; private static final double SELECTED_WINDOW_DIFFERENCE_TO_AVERAGE = 10.0; @@ -101,9 +102,6 @@ public class HistogramView extends TmfView { private Long selectedWindowTimerange = 0L; private Long currentEventTime = 0L; - // This need to be a class variable to enable "small screen" ajustement - private Integer selectedCanvasWidth = 0; - // *** All the UI control below // // NOTE : All textboxes will be READ_ONLY. @@ -147,6 +145,7 @@ public class HistogramView extends TmfView { Font smallFont = null; int nbEventWidth = -1; + int selectedCanvasWidth = -1; boolean doesTimeTextGroupNeedAdjustment = false; // Calculate if we need "small screen" fixes @@ -154,22 +153,13 @@ public class HistogramView extends TmfView { // A lot smaller font for timstampe smallFont = new Font(font.getDevice(), tmpFontData.getName(), tmpFontData.getHeight() - VERY_SMALL_FONT_MODIFIER, tmpFontData.getStyle()); + // Smaller selection window canvas + selectedCanvasWidth = SMALL_SELECTED_WINDOW_CANVAS_WIDTH; // Smaller event number text field nbEventWidth = NB_EVENTS_FIXED_WIDTH/2; // Tell the text group to ajust doesTimeTextGroupNeedAdjustment = true; - - // *** BUG *** - // This might NOT be respected because of the way SWT draw its control, as startTime and endTime - // might take more space than the control. - // However, SWT is too DUMB to have a way to fix a Text size so there is no way I can know the size - // it will take. The only ways to avoid this bug is to : - // 1- Put a size to the canvas that we know to be bigger than the Texts. This is dumb as we try to SAVE space - // 2- Avoid to use "SWT.FILL" on the Canvas. On that case, we WILL have a gap between the canvas and the next text field. - // - // Neither solution is good but 2 is slightly better. Way to go SWT! - selectedCanvasWidth = SELECTED_WINDOW_CANVAS_WIDTH - (SELECTED_WINDOW_CANVAS_WIDTH/3); } else { // Slightly smaller font for timestamp @@ -295,9 +285,49 @@ public class HistogramView extends TmfView { txtWindowStopTime.setText(""); txtWindowStopTime.setLayoutData(gridDataWindowStop); - - - // *** Everything related to the spinner is below + GridData gridDataSpacer = new GridData(SWT.FILL, SWT.TOP, true, true, 1, 1); + gridDataSpacer.minimumWidth = nbEventWidth; + // *** HACK *** + // To align properly AND to make sure the canvas size is fixed, we NEED to make sure all "section" of the + // gridlayout are taken (and if possible of a fixed size). + // However, SWT is VERY VERY DUMB and won't consider griddata that contain no control. + // Since there will be missing a section, the SelectedWindowCanvas + NbEventsText will take 3 spaces, but + // startTimeText + stopTimeText will take only 2 (as if empty the other griddata of 1 will get ignored). + // StopTime will then take over the missing space; I want to align "stopTime" right on the end of canvas, so + // the added space to stop time would make it being aligned improperly + // So I NEED the empty griddata to be considered! + // Visually : + // |---------------|---------------|-----------| + // |SelectionCanvas SelectionCanvas|NbEventText| + // |SelectionCanvas SelectionCanvas|NbEventText| + // |---------------|---------------|-----------| + // |StartTime | StopTime| ??? | + // |---------------|---------------|-----------| + // + // So since SWT will only consider griddata with control, + // I need to create a totally useless control in the ??? section. + // That's ugly, useless and it generally a bad practice. + // + // *** SUB-HACK *** + // Other interesting fact about SWT : the way it draws (Fill/Expand control in grid) will change if + // the control is a Text of a Label. + // A Label here will be "pushed" by startTime/stopTime Text and won't fill the full space as NbEventText. + // A Text here will NOT be "pushed" and would give a nice visual output. + // (NB : No, I am NOT kidding, try it for yourself!) + // + // Soooooo I guess I will use a Text here. Way to go SWT! + // Downside is that disabled textbox has a slightly different color (even if you change it) so if I want + // to make the text "invisible", I have to keep it editable, so it can be selected. + // + // Label uselessControlToByPassSWTStupidBug = new Label(layoutSelectionWindow, SWT.BORDER); // WON'T align correctly!!! + Text uselessControlToByPassSWTStupidBug = new Text(layoutSelectionWindow, SWT.READ_ONLY); // WILL align correctly!!! + uselessControlToByPassSWTStupidBug.setEditable(false); + uselessControlToByPassSWTStupidBug.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND)); + uselessControlToByPassSWTStupidBug.setLayoutData(gridDataSpacer); + + + + // *** Everything related to the time text group is below GridData gridDataCurrentEvent = new GridData(SWT.CENTER, SWT.CENTER, true, true, 1, 2); ntgCurrentEventTime = new TimeTextGroup(this, layoutTimesSpinner, SWT.BORDER, SWT.BORDER, EVENT_CURRENT_TIME_LABEL_TEXT, HistogramConstant.formatNanoSecondsTime( 0L ), doesTimeTextGroupNeedAdjustment); ntgCurrentEventTime.setLayoutData(gridDataCurrentEvent); @@ -351,6 +381,9 @@ public class HistogramView extends TmfView { if ( (dataBackgroundFullRequest == null) && (tmpExperiment != null) ) { createCanvasAndRequests(tmpExperiment); } + + // Call a redraw for everything + parent.redraw(); } /** @@ -437,8 +470,7 @@ public class HistogramView extends TmfView { fullExperimentCanvas.getHistogramContent().resetTable(newExperiment.getStartTime().getValue(), newExperiment.getEndTime().getValue()); // Create the content for the selected window. - selectedCanvasWidth = selectedWindowCanvas.getSize().x; - selectedWindowCanvas.createNewHistogramContent(selectedCanvasWidth ,SELECTED_WINDOW_BAR_WIDTH, SELECTED_WINDOW_CANVAS_HEIGHT, SELECTED_WINDOW_DIFFERENCE_TO_AVERAGE); + selectedWindowCanvas.createNewHistogramContent(selectedWindowCanvas.getSize().x ,SELECTED_WINDOW_BAR_WIDTH, SELECTED_WINDOW_CANVAS_HEIGHT, SELECTED_WINDOW_DIFFERENCE_TO_AVERAGE); selectedWindowCanvas.getHistogramContent().resetTable(fullExperimentCanvas.getCurrentWindow().getTimestampLeft(), fullExperimentCanvas.getCurrentWindow().getTimestampRight()); // Make sure the UI object are sane @@ -773,4 +805,34 @@ public class HistogramView extends TmfView { selectedWindowCanvas.redrawAsynchronously(); } + /** + * Method called when the view is moved.

+ * + * Just redraw everything... + * + * @param event The controle event generated by the move. + */ + public void controlMoved(ControlEvent event) { + parent.redraw(); + } + + /** + * Method called when the view is resized.

+ * + * We will make sure that the size didn't change more than the content size.

+ * Otherwise we need to perform a new request for the full experiment because we are missing data). + * + * @param event The control event generated by the resize. + */ + public void controlResized(ControlEvent event) { + + // Ouch! The screen enlarged (screen resolution changed?) so far that we miss content to fill the space. + // Perform a new full request... this is quite heavy. + if ( parent.getDisplay().getBounds().width > fullExperimentCanvas.getHistogramContent().getNbElement() ) { + if ( lastUsedExperiment != null ) { + performAllTraceEventsRequest(lastUsedExperiment); + } + } + + } } diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvas.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvas.java index ef287e73a1..4344c1c7f0 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvas.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvas.java @@ -67,6 +67,16 @@ public class ParentHistogramCanvas extends HistogramCanvas { // Create the content histogramContent = new HistogramContent( contentSize, canvasSize, widthPerBar, barsHeight, maxBarsDifferenceToAverage); + + // We need to ajust the "maxDifferenceToAverageFactor" as the bars we draw might be slitghly larger than the value asked + // Each "interval" are concatenated when draw so the worst case should be : + // contentSize / (closest power of 2 to canvasMaxSize) + // Ex : if canvasSize is 1500 -> (2048 / 1024) == 2 so maxDiff should be twice larger + // + // Note : this is not perfect, if the screen is resized after we calculate this, the resulting output can be quite ugly + // For this reason, this will be recalculated in the paintListener as well. + double maxBarsDiffFactor = ((double)contentSize / Math.pow(2, exp-1)); + histogramContent.setMaxDifferenceToAverageFactor(maxBarsDiffFactor); } /* diff --git a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvasPaintListener.java b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvasPaintListener.java index e63eac45bf..1ea696401d 100644 --- a/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvasPaintListener.java +++ b/org.eclipse.linuxtools.lttng.ui/src/org/eclipse/linuxtools/lttng/ui/views/histogram/ParentHistogramCanvasPaintListener.java @@ -57,6 +57,9 @@ public class ParentHistogramCanvasPaintListener extends HistogramCanvasPaintList // Calculate the closest power of 2 just smaller than the canvas size int closestPowerToCanvas = (int)Math.pow(2, Math.floor( Math.log( canvasSize ) / Math.log(2.0) )); + // Make sure the canvas didn't change size, it which case we need to recalculate our heights + recalculateHeightIfCanvasSizeChanged(); + // Calculate the factor of difference between canvas and the power double factor = (double)canvasSize / (double)closestPowerToCanvas; // Calculate how many interval will need to be concatenated into one pixel @@ -108,4 +111,32 @@ public class ParentHistogramCanvasPaintListener extends HistogramCanvasPaintList Rectangle rect = new Rectangle(widthFilled, 0, event.width, event.height); event.gc.fillRectangle(rect); } + + /* + * The function will make sure that the "max difference average" factor is still the same as before; + * if not, the heigth of the events will be recalculated.

+ * + * The factor might change if the canvas is resized by a big factor.

+ */ + protected void recalculateHeightIfCanvasSizeChanged() { + HistogramContent tmpContent = parentCanvas.getHistogramContent(); + // We need to ajust the "maxDifferenceToAverageFactor" as the bars we draw might be slitghly larger than the value asked + // Each "interval" are concatenated when draw so the worst case should be : + // contentSize / (closest power of 2 to canvasMaxSize) + // Ex : if canvasSize is 1500 -> (2048 / 1024) == 2 so maxDiff should be twice larger + // + // His is set in the create content of the canvas, but we need to recalculate it + // here because the window might have been resized! + int exp = (int)Math.floor( Math.log( (double)tmpContent.getCanvasWindowSize() ) / Math.log(2.0) ); + int contentSize = (int)Math.pow(2, exp); + Double maxBarsDiffFactor = ((double)tmpContent.getNbElement() / (double)contentSize ); + + if ( maxBarsDiffFactor != tmpContent.getMaxDifferenceToAverageFactor() ) { + // The factor changed! That's unfortunate because it will take a while to recalculate. + tmpContent.setMaxDifferenceToAverageFactor(maxBarsDiffFactor); + tmpContent.recalculateHeightFactor(); + tmpContent.recalculateEventHeight(); + } + } + } -- 2.34.1