// 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;
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.
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.<p>
* Note : You probably want to call "resetTable()" if you change this, otherwise data might be inconsistent.
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;
* 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.<p>
+ * 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.<p>
+ *
+ * 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;
}
*******************************************************************************/
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;
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;
* 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
// 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";
// 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;
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.
Font smallFont = null;
int nbEventWidth = -1;
+ int selectedCanvasWidth = -1;
boolean doesTimeTextGroupNeedAdjustment = false;
// Calculate if we need "small screen" fixes
// 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
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);
if ( (dataBackgroundFullRequest == null) && (tmpExperiment != null) ) {
createCanvasAndRequests(tmpExperiment);
}
+
+ // Call a redraw for everything
+ parent.redraw();
}
/**
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
selectedWindowCanvas.redrawAsynchronously();
}
+ /**
+ * Method called when the view is moved.<p>
+ *
+ * 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.<p>
+ *
+ * We will make sure that the size didn't change more than the content size.<p>
+ * 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);
+ }
+ }
+
+ }
}
// 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
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.<p>
+ *
+ * The factor might change if the canvas is resized by a big factor.<p>
+ */
+ 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();
+ }
+ }
+
}