Import views plugins
authorAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 27 Jun 2017 19:09:04 +0000 (15:09 -0400)
committerAlexandre Montplaisir <alexmonthy@efficios.com>
Tue, 27 Jun 2017 19:09:04 +0000 (15:09 -0400)
From LTTng-Scope commit c5f55ff6c3525a528c60fd6f4589ba47abe7c406.

Change-Id: I783e5b54d132cb605b1da3f65dbe8c8db5fe12cd
Signed-off-by: Alexandre Montplaisir <alexmonthy@efficios.com>
193 files changed:
tmf/org.lttng.scope.tmf2.views.core/.classpath [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.project [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.runtime.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.ui.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.api.tools.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/META-INF/MANIFEST.MF [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/about.html [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/build.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/plugin.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/ChangeListenerHandler.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/MathUtils.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/NestingBoolean.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/TimeRange.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/Activator.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/config/ConfigOption.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/SignalBridge.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/ViewGroupContext.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/TimeGraphModelControl.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProviderFactory.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProviderManager.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/ITimeGraphModelArrowProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/TimeGraphModelArrowProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/ITimeGraphDrawnEventProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProviderManager.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/ITimeGraphModelStateProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/TimeGraphModelStateProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelArrowProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelStateProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemTimeGraphTreeElement.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/ColorDefinition.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/FlatUIColors.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/LineThickness.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/StateDefinition.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/TimeGraphEvent.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrow.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowRender.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowSeries.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEvent.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventRender.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventSeries.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/BasicTimeGraphStateInterval.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/MultiStateInterval.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateInterval.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateRender.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeElement.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeRender.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/TimeGraphModelView.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/RenderToJson.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/TimeGraphJsonOutput.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.classpath [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.project [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.runtime.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.ui.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.api.tools.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.prefs [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/META-INF/MANIFEST.MF [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/about.html [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/build.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/config.gif [new file with mode: 0755]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/help.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/legend.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_back.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_fwd.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_back.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_fwd.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_back.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_fwd.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_back.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_fwd.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_full.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_in.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_out.gif [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/plugin.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/plugin.xml [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/Activator.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Arrow.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/CountingGridPane.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxColorFactory.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxImageFactory.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxUtils.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Logo.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ArrowExample.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example2.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleCanvas.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag2.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/FadeTransitionEx.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ParallelTransitionExample.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/SwtToobar2.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/TestSwtToolbar.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ZoomExample.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/application.css [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/DummyTrace.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/LttngScopeApplication.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider1.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider2.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelStateProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp2.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/DebugOptions.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/ITimelineWidget.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineManager.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineView.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LatestTaskExecutor.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LoadingOverlay.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/PeriodicRedrawTask.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/StateRectangle.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidget.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidgetTreeArea.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/VerticalPosition.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphArrowLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphBackgroundLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphDrawnEventLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphSelectionLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphStateLayer.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ArrowSeriesMenuButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/FilterModeMenuButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/SortingModeMenuButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ViewerToolBar.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomInButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomOutButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToFullRangeButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToSelectionButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsDialog.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/CreateEventSeriesDialog.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/EventSeriesMenuButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/PredicateDrawnEventProvider.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigButton.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigDialog.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/Messages.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavUtils.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationButtons.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationMode.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowArrows.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowBookmarks.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowEvents.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowStateChanges.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/messages.properties [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/package-info.java [new file with mode: 0644]
tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/package-info.java [new file with mode: 0644]

diff --git a/tmf/org.lttng.scope.tmf2.views.core/.classpath b/tmf/org.lttng.scope.tmf2.views.core/.classpath
new file mode 100644 (file)
index 0000000..0be7b35
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+               <attributes>
+                       <attribute name="annotationpath" value="/org.lttng.scope.common.core/annotations"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+               <attributes>
+                       <attribute name="annotationpath" value="/org.lttng.scope.common.core/annotations"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.project b/tmf/org.lttng.scope.tmf2.views.core/.project
new file mode 100644 (file)
index 0000000..971601e
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.lttng.scope.tmf2.views.core</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+       </natures>
+</projectDescription>
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.resources.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..99f26c0
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.runtime.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.core.runtime.prefs
new file mode 100644 (file)
index 0000000..5a0ad22
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.core.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..bf5f566
--- /dev/null
@@ -0,0 +1,436 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=f
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=info
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=error
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=error
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=error
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=error
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=error
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=error
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=enabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=info
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=error
+org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=error
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=error
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=error
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=error
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=error
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
+org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=error
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=250
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.ui.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644 (file)
index 0000000..2591ac9
--- /dev/null
@@ -0,0 +1,65 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_tmf-style
+formatter_settings_version=12
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.overrideannotation=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.api.tools.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644 (file)
index 0000000..ce92bf3
--- /dev/null
@@ -0,0 +1,102 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Ignore
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Warning
+INVALID_JAVADOC_TAG=Warning
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Warning
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Ignore
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Warning
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.prefs b/tmf/org.lttng.scope.tmf2.views.core/.settings/org.eclipse.pde.prefs
new file mode 100644 (file)
index 0000000..01d624d
--- /dev/null
@@ -0,0 +1,32 @@
+compilers.f.unresolved-features=1
+compilers.f.unresolved-plugins=1
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.build.bin.includes=0
+compilers.p.build.encodings=2
+compilers.p.build.java.compiler=2
+compilers.p.build.java.compliance=1
+compilers.p.build.missing.output=2
+compilers.p.build.output.library=1
+compilers.p.build.source.library=0
+compilers.p.build.src.includes=0
+compilers.p.deprecated=1
+compilers.p.discouraged-class=1
+compilers.p.internal=1
+compilers.p.missing-packages=1
+compilers.p.missing-version-export-package=2
+compilers.p.missing-version-import-package=2
+compilers.p.missing-version-require-bundle=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=1
+compilers.p.unknown-attribute=1
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-identifier=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.s.create-docs=false
+compilers.s.doc-folder=doc
+compilers.s.open-tags=1
+eclipse.preferences.version=1
diff --git a/tmf/org.lttng.scope.tmf2.views.core/META-INF/MANIFEST.MF b/tmf/org.lttng.scope.tmf2.views.core/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..ae32157
--- /dev/null
@@ -0,0 +1,38 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-Vendor: %Bundle-Vendor
+Bundle-Version: 0.2.0.qualifier
+Bundle-Localization: plugin
+Bundle-SymbolicName: org.lttng.scope.tmf2.views.core;singleton:=true
+Bundle-Activator: org.lttng.scope.tmf2.views.core.activator.internal.Activator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Eclipse-ExtensibleAPI: true
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
+ org.lttng.scope.common.core,
+ ca.polymtl.dorsal.libdelorean,
+ org.eclipse.tracecompass.tmf.core
+Export-Package: org.lttng.scope.tmf2.views.core,
+ org.lttng.scope.tmf2.views.core.activator.internal;x-internal:=true,
+ org.lttng.scope.tmf2.views.core.config,
+ org.lttng.scope.tmf2.views.core.context,
+ org.lttng.scope.tmf2.views.core.timegraph.control,
+ org.lttng.scope.tmf2.views.core.timegraph.model.provider,
+ org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows,
+ org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents,
+ org.lttng.scope.tmf2.views.core.timegraph.model.provider.states,
+ org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem,
+ org.lttng.scope.tmf2.views.core.timegraph.model.render,
+ org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows,
+ org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents,
+ org.lttng.scope.tmf2.views.core.timegraph.model.render.states,
+ org.lttng.scope.tmf2.views.core.timegraph.model.render.tree,
+ org.lttng.scope.tmf2.views.core.timegraph.view,
+ org.lttng.scope.tmf2.views.core.timegraph.view.json
+Import-Package: com.google.common.annotations;version="15.0.0",
+ com.google.common.base,
+ com.google.common.collect,
+ com.google.gson,
+ org.json
diff --git a/tmf/org.lttng.scope.tmf2.views.core/about.html b/tmf/org.lttng.scope.tmf2.views.core/about.html
new file mode 100644 (file)
index 0000000..28737f6
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+
+<p>June 5, 2006</p>
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.core/build.properties b/tmf/org.lttng.scope.tmf2.views.core/build.properties
new file mode 100644 (file)
index 0000000..e08e216
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               plugin.properties,\
+               about.html,\
+               .
+src.includes = about.html
+additional.bundles = org.eclipse.jdt.annotation
+jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
diff --git a/tmf/org.lttng.scope.tmf2.views.core/plugin.properties b/tmf/org.lttng.scope.tmf2.views.core/plugin.properties
new file mode 100644 (file)
index 0000000..92fd267
--- /dev/null
@@ -0,0 +1,11 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+Bundle-Vendor = LTTng Scope
+Bundle-Name = LTTng Scope TMF2 Core Plug-in
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/ChangeListenerHandler.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/ChangeListenerHandler.java
new file mode 100644 (file)
index 0000000..c2ddb73
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core;
+
+import javafx.beans.property.Property;
+import javafx.beans.value.ChangeListener;
+
+/**
+ * Simple class encapsulating a {@link ChangeListener} and its target property.
+ * It allows disabling and re-enabling the listener, by simply detaching it from
+ * its target, on demand. The calling class then only has one class to manage
+ * (this handler) instead of two.
+ *
+ * @author Alexandre Montplaisir
+ *
+ * @param <T>
+ *            The type of property
+ */
+public final class ChangeListenerHandler<T> {
+
+    private final Property<T> fTarget;
+    private final ChangeListener<T> fListener;
+
+    /**
+     * Build a new listener handler. The listener will be added to the target
+     * (enabled).
+     *
+     * @param target
+     *            The target property
+     * @param listener
+     *            The listener
+     */
+    public ChangeListenerHandler(Property<T> target, ChangeListener<T> listener) {
+        fTarget = target;
+        fListener = listener;
+        enable();
+    }
+
+    /**
+     * Attach the listener to its property, re-enabling it.
+     */
+    public void enable() {
+        fTarget.addListener(fListener);
+    }
+
+    /**
+     * Detach the listener from its property, disabling it.
+     */
+    public void disable() {
+        fTarget.removeListener(fListener);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/MathUtils.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/MathUtils.java
new file mode 100644 (file)
index 0000000..1ba0008
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core;
+
+/**
+ * Uncommon math utility methods.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class MathUtils {
+
+    private MathUtils() {}
+
+    /**
+     * Find the multiple of 'multipleOf' that is greater but closest to
+     * 'number'. If 'number' is already a multiple of 'multipleOf', the same
+     * value will be returned.
+     *
+     * @param number
+     *            The starting number
+     * @param multipleOf
+     *            We want the returned value to be a multiple of this number
+     * @return The closest, greater multiple
+     */
+    public static long roundToClosestHigherMultiple(long number, long multipleOf) {
+        return (long) (Math.ceil((double) number / multipleOf) * multipleOf);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/NestingBoolean.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/NestingBoolean.java
new file mode 100644 (file)
index 0000000..7155de4
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.value.ChangeListener;
+
+/**
+ * Utility class that serves as a wrapper around a single boolean flag that can
+ * be enabled/disabled. It counts the number of times {@link #disable()} is
+ * called, and only really re-enables the inner value when {@link #enable()} is
+ * called that many times.
+ *
+ * It is meant to be useful in multi-thread scenarios, where concurrent
+ * "critical sections" may want to disable something like a listener, and not
+ * have it really be re-enabled until all critical sections are finished. Thus
+ * it is thread-safe.
+ *
+ * The inner value is exposed through the {@link #enabledProperty()} method, which
+ * returns a {@link ReadOnlyBooleanProperty}. You can attach
+ * {@link ChangeListener}s to that property to get notified of inner value
+ * changes.
+ *
+ * It is "enabled" at creation time.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NestingBoolean {
+
+    private final AtomicInteger fDisabledCount = new AtomicInteger(0);
+    private final BooleanProperty fBoolean = new SimpleBooleanProperty(true);
+
+    /**
+     * Decrease the "disabled" count by 1. If it reaches (or already was at) 0
+     * then the value is truly enabled.
+     */
+    public synchronized void enable() {
+        /* Decrement the count but only if it is currently above 0 */
+        int ret = fDisabledCount.updateAndGet(value -> value > 0 ? value - 1 : 0);
+        if (ret == 0) {
+            fBoolean.set(true);
+        }
+    }
+
+    /**
+     * Increase the "disabled" count by 1. The inner value will necessarily be
+     * disabled after this call.
+     */
+    public synchronized void disable() {
+        fDisabledCount.incrementAndGet();
+        fBoolean.set(false);
+    }
+
+    /**
+     * Property representing the inner boolean value.
+     *
+     * @return The inner value
+     */
+    public ReadOnlyBooleanProperty enabledProperty() {
+        return fBoolean;
+    }
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/TimeRange.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/TimeRange.java
new file mode 100644 (file)
index 0000000..229b756
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Simple class representing a time range, encapsulating two timestamps stored
+ * as long's.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class TimeRange {
+
+    private final long fStartTime;
+    private final long fEndTime;
+
+    private TimeRange(long startTime, long endTime) {
+        if (endTime < startTime) {
+            throw new IllegalArgumentException("End of time range earlier than the start:[" + startTime + ", " + endTime + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        }
+        if (startTime < 0 || endTime < 0) {
+            throw new IllegalArgumentException("One of the bounds is negative:[" + startTime + ", " + endTime + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        }
+        fStartTime = startTime;
+        fEndTime = endTime;
+    }
+
+    /**
+     * Factory method, creating a new time range from its start and end.
+     *
+     * @param startTime
+     *            The start time of the range
+     * @param endTime
+     *            The end time of the range. Should be equal or greater than the
+     *            start time.
+     * @return The new time range
+     */
+    public static TimeRange of(long startTime, long endTime) {
+        // TODO Implement caching?
+        return new TimeRange(startTime, endTime);
+    }
+
+    /**
+     * Get the range's start time
+     *
+     * @return The start time
+     */
+    public long getStart() {
+        return fStartTime;
+    }
+
+    /**
+     * Get the range's end time
+     *
+     * @return The end time
+     */
+    public long getEnd() {
+        return fEndTime;
+    }
+
+    /**
+     * Get the duration, or "span", of this time range.
+     *
+     * @return The duration
+     */
+    public long getDuration() {
+        return (fEndTime - fStartTime);
+    }
+
+    /**
+     * Check if this time range contains, inclusively, the given timestamp.
+     *
+     * @param timestamp
+     *            The timestamp to check
+     * @return True if the timestamp is contained in the range, false otherwise
+     */
+    public boolean contains(long timestamp) {
+        return (fStartTime <= timestamp && timestamp <= fEndTime);
+    }
+
+    /**
+     * Check if the bounds of this time range are the exact same one (single
+     * timestamp).
+     *
+     * @return If this range represents a single timestamp
+     */
+    public boolean isSingleTimestamp() {
+        return (fStartTime == fEndTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fStartTime, fEndTime);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TimeRange other = (TimeRange) obj;
+        return (fStartTime == other.fStartTime
+                && fEndTime == other.fEndTime);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("start", fStartTime) //$NON-NLS-1$
+                .add("end", fEndTime) //$NON-NLS-1$
+                .toString();
+    }
+
+    /**
+     * Convert this range into a {@link TmfTimeRange}.
+     *
+     * @return The equivalent TmfTimeRange
+     */
+    public TmfTimeRange toTmfTimeRange() {
+        return new TmfTimeRange(TmfTimestamp.fromNanos(fStartTime), TmfTimestamp.fromNanos(fEndTime));
+    }
+
+    /**
+     * Create a {@link TimeRange} from a {@link TmfTimeRange}.
+     *
+     * @param range
+     *            The TmfTimeRange
+     * @return The TimeRange
+     */
+    public static TimeRange fromTmfTimeRange(TmfTimeRange range) {
+        return new TimeRange(range.getStartTime().toNanos(), range.getEndTime().toNanos());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/Activator.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/Activator.java
new file mode 100644 (file)
index 0000000..c659479
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.activator.internal;
+
+import org.lttng.scope.common.core.ScopeCoreActivator;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+
+/**
+ * Plugin activator
+ *
+ * @noreference This class should not be accessed outside of this plugin.
+ */
+public class Activator extends ScopeCoreActivator {
+
+    /**
+     * Return the singleton instance of this activator.
+     *
+     * @return The singleton instance
+     */
+    public static Activator instance() {
+        return ScopeCoreActivator.getInstance(Activator.class);
+    }
+
+    @Override
+    protected void startActions() {
+    }
+
+    @Override
+    protected void stopActions() {
+        ViewGroupContext.cleanup();
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/activator/internal/package-info.java
new file mode 100644 (file)
index 0000000..bcfea26
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.activator.internal;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/config/ConfigOption.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/config/ConfigOption.java
new file mode 100644 (file)
index 0000000..627f561
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.config;
+
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * Configuration option, which is basically a JavaFX property with a default
+ * value.
+ *
+ * @author Alexandre Montplaisir
+ *
+ * @param <T>
+ *            The type of property
+ */
+public class ConfigOption<T> extends SimpleObjectProperty<T> {
+
+    private final T fDefaultValue;
+
+    /**
+     * Constructor
+     *
+     * @param defaultValue
+     *            The initial and default value
+     */
+    public ConfigOption(T defaultValue) {
+        super(defaultValue);
+        fDefaultValue = defaultValue;
+    }
+
+    /**
+     * Retrieve the default value of this option. This does not modify the
+     * current value.
+     *
+     * @return The default value
+     */
+    public T getDefaultValue() {
+        return fDefaultValue;
+    }
+
+    /**
+     * Reset the current value to its default.
+     */
+    public void resetToDefault() {
+        set(fDefaultValue);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/SignalBridge.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/SignalBridge.java
new file mode 100644 (file)
index 0000000..f861514
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.context;
+
+import org.eclipse.tracecompass.tmf.core.signal.TmfSelectionRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
+import org.eclipse.tracecompass.tmf.core.signal.TmfSignalThrottler;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
+import org.eclipse.tracecompass.tmf.core.signal.TmfWindowRangeUpdatedSignal;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+
+/**
+ * Bridge between a {@link ViewGroupContext} and the {@link TmfSignalManager}.
+ * It sends equivalent "signals" back-and-forth between the two APIs.
+ *
+ * Note: Needs to be public because of the way TmfSignalManager works.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class SignalBridge {
+
+    private static final int SIGNAL_DELAY_MS = 200;
+
+    private final TmfSignalThrottler fVisibleRangeSignalThrottler = new TmfSignalThrottler(null, SIGNAL_DELAY_MS);
+
+    private final ViewGroupContext fViewContext;
+
+    /**
+     * Constructor
+     *
+     * @param viewContext
+     *            The view context this bridge will connect to
+     */
+    public SignalBridge(ViewGroupContext viewContext) {
+        TmfSignalManager.register(this);
+        fViewContext = viewContext;
+
+        viewContext.currentTraceProperty().addListener((obs, oldTrace, newTrace) -> {
+            if (newTrace == null) {
+                return;
+            }
+            TmfSignal newTraceSignal = new TmfTraceSelectedSignal(SignalBridge.this, newTrace);
+            TmfSignalManager.dispatchSignal(newTraceSignal);
+
+            viewContext.currentTraceFullRangeProperty().addListener((observable, oldRange, newRange) -> {
+                TmfTimeRange tmfTimeRange = newRange.toTmfTimeRange();
+                TmfSignal signal = new TmfTraceRangeUpdatedSignal(SignalBridge.this, viewContext.getCurrentTrace(), tmfTimeRange);
+                TmfSignalManager.dispatchSignal(signal);
+            });
+
+            viewContext.currentVisibleTimeRangeProperty().addListener((observable, oldRange, newRange) -> {
+                TmfTimeRange tmfTimeRange = newRange.toTmfTimeRange();
+                TmfSignal signal = new TmfWindowRangeUpdatedSignal(SignalBridge.this, tmfTimeRange);
+                fVisibleRangeSignalThrottler.queue(signal);
+            });
+
+            viewContext.currentSelectionTimeRangeProperty().addListener((observable, oldRange, newRange) -> {
+                TmfSignal signal;
+                if (newRange.isSingleTimestamp()) {
+                    ITmfTimestamp ts = TmfTimestamp.fromNanos(newRange.getStart());
+                    signal = new TmfSelectionRangeUpdatedSignal(SignalBridge.this, ts);
+                } else {
+                    ITmfTimestamp startTs = TmfTimestamp.fromNanos(newRange.getStart());
+                    ITmfTimestamp endTs = TmfTimestamp.fromNanos(newRange.getEnd());
+                    signal = new TmfSelectionRangeUpdatedSignal(SignalBridge.this, startTs, endTs);
+                }
+                TmfSignalManager.dispatchSignal(signal);
+            });
+
+        });
+    }
+
+    /**
+     * Dispose of this bridge, deregistering it from the signal manager.
+     */
+    public void dispose() {
+        TmfSignalManager.deregister(this);
+    }
+
+    // ------------------------------------------------------------------------
+    // Signal handlers
+    // ------------------------------------------------------------------------
+
+    /**
+     * Handler for the trace selected signal
+     *
+     * @param signal
+     *            Received signal
+     */
+    @TmfSignalHandler
+    public void traceSelected(final TmfTraceSelectedSignal signal) {
+        if (signal.getSource() == this) {
+            return;
+        }
+        ITmfTrace trace = signal.getTrace();
+        fViewContext.setCurrentTrace(trace);
+    }
+
+    /**
+     * Handler for the trace closed signal
+     *
+     * @param signal
+     *            Received signal
+     */
+    @TmfSignalHandler
+    public void traceClosed(final TmfTraceClosedSignal signal) {
+        if (signal.getSource() == this) {
+            return;
+        }
+        if (TmfTraceManager.getInstance().getActiveTrace() == null) {
+            fViewContext.setCurrentTrace(null);
+        }
+    }
+
+    /**
+     * Handler for the trace range updated signal
+     *
+     * @param signal
+     *            Received signal
+     */
+    @TmfSignalHandler
+    public void traceRangeUpdated(TmfTraceRangeUpdatedSignal signal) {
+        if (signal.getSource() == this) {
+            return;
+        }
+        /*
+         * This signal is a disaster, it's very inconsistent, has no guarantee
+         * of even showing up and sometimes gives values outside of the trace's
+         * own range. Best to ignore its contents completely.
+         */
+
+        ITmfTrace trace = fViewContext.getCurrentTrace();
+        if (trace == null) {
+            return;
+        }
+        long traceStart = trace.getStartTime().toNanos();
+        long traceEnd = trace.getEndTime().toNanos();
+        fViewContext.setCurrentTraceFullRange(TimeRange.of(traceStart, traceEnd));
+    }
+
+    /**
+     * Handler for the selection range updated signal
+     *
+     * @param signal
+     *            Received signal
+     */
+    @TmfSignalHandler
+    public void selectionRangeUpdated(final TmfSelectionRangeUpdatedSignal signal) {
+        if (signal.getSource() == this) {
+            return;
+        }
+        long rangeStart = signal.getBeginTime().toNanos();
+        long rangeEnd = signal.getEndTime().toNanos();
+
+        /* Sometimes the range is weird... */
+        if (rangeStart == Long.MAX_VALUE || rangeEnd == Long.MAX_VALUE) {
+            return;
+        }
+
+        /*
+         * This signal's end can be before its start time, against all logic.
+         */
+        TimeRange range;
+        if (rangeStart > rangeEnd) {
+            range = TimeRange.of(rangeEnd, rangeStart);
+        } else {
+            range = TimeRange.of(rangeStart, rangeEnd);
+        }
+
+        ITmfTrace trace = fViewContext.getCurrentTrace();
+        if (trace == null) {
+            return;
+        }
+        fViewContext.setCurrentSelectionTimeRange(range);
+    }
+
+    /**
+     * Handler for the window range updated signal
+     *
+     * @param signal
+     *            Received signal
+     */
+    @TmfSignalHandler
+    public void windowRangeUpdated(final TmfWindowRangeUpdatedSignal signal) {
+        if (signal.getSource() == this) {
+            return;
+        }
+        TmfTimeRange windowRange = signal.getCurrentRange();
+        ITmfTrace trace = fViewContext.getCurrentTrace();
+        if (windowRange == null || trace == null) {
+            return;
+        }
+        fViewContext.setCurrentVisibleTimeRange(TimeRange.fromTmfTimeRange(windowRange));
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/ViewGroupContext.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/ViewGroupContext.java
new file mode 100644 (file)
index 0000000..a683375
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.context;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * A common context for a group of views. Information is stored as properties,
+ * and views can add listeners to get notified of value changes.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ViewGroupContext {
+
+    /** Value representing uninitialized timestamps */
+    public static final TimeRange UNINITIALIZED_RANGE = TimeRange.of(0, 0);
+
+    private final SignalBridge fSignalBridge;
+
+    private final ObjectProperty<@Nullable ITmfTrace> fCurrentTrace = new SimpleObjectProperty<>(null);
+
+    private ObjectProperty<TimeRange> fCurrentTraceFullRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+    private ObjectProperty<TimeRange> fCurrentVisibleTimeRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+    private ObjectProperty<TimeRange> fCurrentSelectionRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+
+    /**
+     * The context is a singleton for now, but the framework could be extended
+     * to support several contexts (one per "view group") at the same time.
+     */
+    private static @Nullable ViewGroupContext INSTANCE;
+
+    private ViewGroupContext() {
+        fSignalBridge = new SignalBridge(this);
+    }
+
+    /**
+     * For now, there is only a single view context for the framework. This
+     * method returns this singleton instance.
+     *
+     * @return The view context
+     */
+    public static ViewGroupContext getCurrent() {
+        ViewGroupContext ctx = INSTANCE;
+        if (ctx != null) {
+            return ctx;
+        }
+        ctx = new ViewGroupContext();
+        INSTANCE = ctx;
+        return ctx;
+    }
+
+    /**
+     * Cleanup all view group contexts.
+     */
+    public static void cleanup() {
+        ViewGroupContext ctx = INSTANCE;
+        if (ctx != null) {
+            ctx.fSignalBridge.dispose();
+        }
+    }
+
+    /**
+     * Set the current trace being displayed by this view context.
+     *
+     * @param trace
+     *            The trace, can be null to indicate no trace
+     */
+    public void setCurrentTrace(@Nullable ITmfTrace trace) {
+        /* On trace change, adjust the other properties accordingly. */
+        if (trace == null) {
+            fCurrentTraceFullRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+            fCurrentVisibleTimeRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+            fCurrentSelectionRange = new SimpleObjectProperty<>(UNINITIALIZED_RANGE);
+
+        } else {
+            long traceStart = trace.getStartTime().toNanos();
+            long traceEnd = trace.getEndTime().toNanos();
+            long visibleRangeEnd = Math.min(traceStart + trace.getInitialRangeOffset().toNanos(), traceEnd);
+
+            fCurrentTraceFullRange = new SimpleObjectProperty<>((TimeRange.of(traceStart, traceEnd)));
+            fCurrentVisibleTimeRange = new SimpleObjectProperty<>((TimeRange.of(traceStart, visibleRangeEnd)));
+            fCurrentSelectionRange = new SimpleObjectProperty<>((TimeRange.of(traceStart, traceStart)));
+        }
+
+        fCurrentTrace.set(trace);
+    }
+
+    /**
+     * Retrieve the current trace displayed by this view context.
+     *
+     * @return The context's current trace. Can be null to indicate no trace.
+     */
+    public @Nullable ITmfTrace getCurrentTrace() {
+        return fCurrentTrace.get();
+    }
+
+    /**
+     * The current trace property.
+     *
+     * Make sure you use {@link #setCurrentTrace} to modify the current trace.
+     *
+     * @return The current trace property.
+     */
+    public ReadOnlyObjectProperty<@Nullable ITmfTrace> currentTraceProperty() {
+        return fCurrentTrace;
+    }
+
+    /**
+     * Set a new full range for the current trace
+     *
+     * @param range
+     *            The new full range of the trace
+     */
+    public void setCurrentTraceFullRange(TimeRange range) {
+        fCurrentTraceFullRange.set(range);
+    }
+
+    /**
+     * Get the full range of the current trace.
+     *
+     * @return The full range of the trace
+     */
+    public TimeRange getCurrentTraceFullRange() {
+        return fCurrentTraceFullRange.get();
+    }
+
+    /**
+     * The property representing the full range of the current trace.
+     *
+     * TODO This property should move to the trace object itself.
+     *
+     * @return The full range property
+     */
+    public ObjectProperty<TimeRange> currentTraceFullRangeProperty() {
+        return fCurrentTraceFullRange;
+    }
+
+    /**
+     * Set the current visible time range of this view context.
+     *
+     * @param range
+     *            The new visible time range
+     */
+    public void setCurrentVisibleTimeRange(TimeRange range) {
+        fCurrentVisibleTimeRange.set(range);
+    }
+
+    /**
+     * Retrieve the current visible time range of the view context.
+     *
+     * @return The current visible time range
+     */
+    public TimeRange getCurrentVisibleTimeRange() {
+        return fCurrentVisibleTimeRange.get();
+    }
+
+    /**
+     * The visible time range property. This indicates the time range bounded by
+     * the views of this context.
+     *
+     * @return The visible time range property
+     */
+    public ObjectProperty<TimeRange> currentVisibleTimeRangeProperty() {
+        return fCurrentVisibleTimeRange;
+    }
+
+    /**
+     * Set the current time range selection.
+     *
+     * @param range
+     *            The new selection range
+     */
+    public void setCurrentSelectionTimeRange(TimeRange range) {
+        fCurrentSelectionRange.set(range);
+    }
+
+    /**
+     * Retrieve the current time range selection
+     *
+     * @return The current selection range
+     */
+    public TimeRange getCurrentSelectionTimeRange() {
+        return fCurrentSelectionRange.get();
+    }
+
+    /**
+     * The property representing the current time range selection. This is the
+     * "highlighted" part of the trace, which can be selected by the mouse or
+     * other means, and on which action can act.
+     *
+     * @return The time range selection property
+     */
+    public ObjectProperty<TimeRange> currentSelectionTimeRangeProperty() {
+        return fCurrentSelectionRange;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/context/package-info.java
new file mode 100644 (file)
index 0000000..7ee533c
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.context;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/package-info.java
new file mode 100644 (file)
index 0000000..838f3e1
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/TimeGraphModelControl.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/TimeGraphModelControl.java
new file mode 100644 (file)
index 0000000..a29542b
--- /dev/null
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.control;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.view.TimeGraphModelView;
+
+import javafx.beans.value.ChangeListener;
+
+/**
+ * Control part of the timegraph MVC mechanism. It links a
+ * {@link TimeGraphModelView} to a {@link ITimeGraphModelProvider}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class TimeGraphModelControl {
+
+    private final ChangeListener<TimeRange> fVisibleRangeChangeListener = (obs, oldRange, newRange) -> seekVisibleRange(newRange);
+
+    private final ViewGroupContext fViewContext;
+    private final ITimeGraphModelProvider fRenderProvider;
+
+    private @Nullable TimeGraphModelView fView = null;
+
+    /**
+     * Constructor.
+     *
+     * The control links a model provider, and a view. But the view also needs a
+     * back-reference to the control. The suggested pattern is to do:
+     *
+     * <pre>
+     * ITimeGraphModelProvider provider = ...
+     * TimeGraphModelControl control = new TimeGraphModelControl(viewContext, provider);
+     * TimeGraphModelView view = new TimeGraphModelView(control);
+     * control.attachView(view);
+     * </pre>
+     *
+     * @param viewContext
+     *            The view context to which this timegraph belongs
+     * @param provider
+     *            The model provider that goes with this control
+     */
+    public TimeGraphModelControl(ViewGroupContext viewContext, ITimeGraphModelProvider provider) {
+        fViewContext = viewContext;
+        fRenderProvider = provider;
+
+        attachListeners(viewContext);
+    }
+
+    /**
+     * Attach a view to this control
+     *
+     * @param view
+     *            The view to attach
+     */
+    public void attachView(TimeGraphModelView view) {
+        fView = view;
+
+        /*
+         * Initially populate the view with the context of the current trace.
+         */
+        ITmfTrace trace = getViewContext().getCurrentTrace();
+        initializeForTrace(trace);
+    }
+
+    @Nullable TimeGraphModelView getView() {
+        return fView;
+    }
+
+    /**
+     * Dispose this control and its components.
+     */
+    public void dispose() {
+        if (fView != null) {
+            fView.dispose();
+        }
+    }
+
+    private void attachListeners(ViewGroupContext viewContext) {
+        viewContext.currentTraceProperty().addListener((observable, oldTrace, newTrace) -> {
+            initializeForTrace(newTrace);
+
+            viewContext.currentSelectionTimeRangeProperty().addListener((obs, oldRange, newRange) -> {
+                drawSelection(newRange);
+            });
+
+            viewContext.currentVisibleTimeRangeProperty().addListener(fVisibleRangeChangeListener);
+        });
+    }
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the view context to which this control belongs.
+     *
+     * @return The view context
+     */
+    public ViewGroupContext getViewContext() {
+        return fViewContext;
+    }
+
+    /**
+     * Get the model provider of this control
+     *
+     * @return The model provider
+     */
+    public ITimeGraphModelProvider getModelRenderProvider() {
+        return fRenderProvider;
+    }
+
+    // ------------------------------------------------------------------------
+    // Control -> View operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Initialize this timegraph for a new trace.
+     *
+     * @param trace
+     *            The trace to initialize in the view. If it is null it indcates
+     *            'no trace'.
+     */
+    public synchronized void initializeForTrace(@Nullable ITmfTrace trace) {
+        fRenderProvider.setTrace(trace);
+
+        TimeGraphModelView view = fView;
+        if (view == null) {
+            return;
+        }
+        view.clear();
+
+        if (trace == null) {
+            /* View will remain cleared, good */
+            return;
+        }
+
+        TimeRange currentVisibleRange = fViewContext.getCurrentVisibleTimeRange();
+        checkWindowTimeRange(currentVisibleRange);
+        view.seekVisibleRange(currentVisibleRange);
+    }
+
+    /**
+     * Repaint, without seeking anywhere else, the current displayed area of the
+     * view.
+     *
+     * This can be called whenever some settings like filters etc. have changed,
+     * so that a repaint will show updated information.
+     */
+    public void repaintCurrentArea() {
+        ITmfTrace trace = fViewContext.getCurrentTrace();
+        TimeRange currentRange = fViewContext.getCurrentVisibleTimeRange();
+        if (trace == null || currentRange == ViewGroupContext.UNINITIALIZED_RANGE) {
+            return;
+        }
+
+        TimeGraphModelView view = fView;
+        if (view != null) {
+            view.clear();
+            view.seekVisibleRange(currentRange);
+        }
+    }
+
+    void seekVisibleRange(TimeRange newRange) {
+        checkWindowTimeRange(newRange);
+        TimeGraphModelView view = fView;
+        if (view != null) {
+            view.seekVisibleRange(newRange);
+        }
+    }
+
+    void drawSelection(TimeRange selectionRange) {
+        checkWindowTimeRange(selectionRange);
+        TimeGraphModelView view = fView;
+        if (view != null) {
+            view.drawSelection(selectionRange);
+        }
+    }
+
+
+
+    // ------------------------------------------------------------------------
+    // View -> Control operations (Control external API)
+    // ------------------------------------------------------------------------
+
+    /**
+     * Change the current time range selection.
+     *
+     * Called by the view to indicate that the user has input a new time range
+     * selection from the view. The control will relay this to the rest of the
+     * framework.
+     *
+     * @param newSelectionRange
+     *            The new time range selection.
+     */
+    public void updateTimeRangeSelection(TimeRange newSelectionRange) {
+        fViewContext.setCurrentSelectionTimeRange(newSelectionRange);
+    }
+
+    /**
+     * Change the current visible time range.
+     *
+     * Called by the view whenever the user selects a new visible time range,
+     * for example by scrolling left or right.
+     *
+     * @param newVisibleRange
+     *            The new visible time range
+     * @param echo
+     *            This flag indicates if the view wants to receive the new time
+     *            range notification back to itself (via
+     *            {@link #seekVisibleRange}.
+     */
+    public void updateVisibleTimeRange(TimeRange newVisibleRange, boolean echo) {
+        checkTimeRange(newVisibleRange);
+
+        /*
+         * If 'echo' is 'off', we will avoid triggering our change listener on
+         * this modification by detaching then re-attaching it afterwards.
+         */
+        if (echo) {
+            fViewContext.setCurrentVisibleTimeRange(newVisibleRange);
+        } else {
+            fViewContext.currentVisibleTimeRangeProperty().removeListener(fVisibleRangeChangeListener);
+            fViewContext.setCurrentVisibleTimeRange(newVisibleRange);
+            fViewContext.currentVisibleTimeRangeProperty().addListener(fVisibleRangeChangeListener);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utils
+    // ------------------------------------------------------------------------
+
+    private static void checkTimeRange(TimeRange range) {
+        if (range.getStart() == Long.MAX_VALUE) {
+            throw new IllegalArgumentException("You are trying to make me believe the range starts at " + //$NON-NLS-1$
+                    range.getStart() + ". I do not believe you."); //$NON-NLS-1$
+        }
+        if (range.getEnd() == Long.MAX_VALUE) {
+            throw new IllegalArgumentException("You are trying to make me believe the range ends at " + //$NON-NLS-1$
+                    range.getEnd() + ". I do not believe you."); //$NON-NLS-1$
+        }
+    }
+
+    private void checkWindowTimeRange(TimeRange windowRange) {
+        checkTimeRange(windowRange);
+        TimeRange fullRange = fViewContext.getCurrentTraceFullRange();
+
+        if (windowRange.getStart() < fullRange.getStart()) {
+            throw new IllegalArgumentException("Requested window start time: " + windowRange.getStart() + //$NON-NLS-1$
+                    " is smaller than trace start time " + fullRange.getStart()); //$NON-NLS-1$
+        }
+        if (windowRange.getEnd() > fullRange.getEnd()) {
+            throw new IllegalArgumentException("Requested window end time: " + windowRange.getEnd() + //$NON-NLS-1$
+                    " is greater than trace end time " + fullRange.getEnd()); //$NON-NLS-1$
+        }
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/control/package-info.java
new file mode 100644 (file)
index 0000000..985b37b
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.control;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProvider.java
new file mode 100644 (file)
index 0000000..dc43a7a
--- /dev/null
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.ITimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.ITimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import javafx.beans.property.ObjectProperty;
+
+/**
+ * Base interface for time graph model providers.
+ *
+ * This object is responsible for the generation of the "tree" part of the
+ * timegraph, and other generic options like sorting and filtering modes.
+ *
+ * It also encapsulates one {@link ITimeGraphModelStateProvider} (which is
+ * responsible of providing state intervals), and zero or more
+ * {@link ITimeGraphModelArrowProvider} (which provide model-defined arrow
+ * series).
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ITimeGraphModelProvider {
+
+    // ------------------------------------------------------------------------
+    // Configuration option classes
+    // ------------------------------------------------------------------------
+
+    /**
+     * Class representing one sorting mode. A sorting mode is like a comparator
+     * to sort tree elements. Only one can be active at any time.
+     *
+     * The exact behavior of the sorting mode is defined by the model provider
+     * itself.
+     */
+    class SortingMode {
+
+        private final String fName;
+
+        public SortingMode(String name) {
+            fName = name;
+        }
+
+        public String getName() {
+            return fName;
+        }
+    }
+
+    /**
+     * Class representing a filter mode. A filter mode is like a filter applied
+     * on the list tree elements. Zero or more can be active at the same time.
+     *
+     * The exact behavior of the filter mode is defined by the model provider
+     * itself.
+     */
+    class FilterMode {
+
+        private final String fName;
+
+        public FilterMode(String name) {
+            fName = name;
+        }
+
+        public String getName() {
+            return fName;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // General methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get the name of this model provider. This can be used for example to name
+     * a corresponding view in the UI.
+     *
+     * @return The model provider's name
+     */
+    String getName();
+
+    /**
+     * Set the trace for which this model provider fetches its information.
+     *
+     * @param trace
+     *            The source trace
+     */
+    void setTrace(@Nullable ITmfTrace trace);
+
+
+    /**
+     * Get the trace for which this model provider fetches its information.
+     *
+     * @return The current trace
+     */
+    @Nullable ITmfTrace getTrace();
+
+    /**
+     * The property representing the target trace of this model provider.
+     *
+     * @return The trace property
+     */
+    ObjectProperty<@Nullable ITmfTrace> traceProperty();
+
+    // ------------------------------------------------------------------------
+    // Render providers
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get a tree render corresponding to the current configuration settings.
+     *
+     * @return A tree render
+     */
+    TimeGraphTreeRender getTreeRender();
+
+    /**
+     * Get the state provider supplied by this model provider.
+     *
+     * @return The state provider
+     */
+    ITimeGraphModelStateProvider getStateProvider();
+
+    /**
+     * Get the arrow providers supplied by this model provider.
+     *
+     * @return The arrow providers. May be empty but should not be null.
+     */
+    Collection<ITimeGraphModelArrowProvider> getArrowProviders();
+
+    // ------------------------------------------------------------------------
+    // Sorting modes
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get a list of all the available sorting modes for this provider.
+     *
+     * @return The sorting modes
+     */
+    List<SortingMode> getSortingModes();
+
+    /**
+     * Get the current sorting mode. There should always be one and only one.
+     *
+     * @return The current sorting mode
+     */
+    SortingMode getCurrentSortingMode();
+
+    /**
+     * Change the configured sorting mode to another one.
+     *
+     * @param index
+     *            The index of the corresponding mode in the list returned by
+     *            {@link #getSortingModes()}.
+     */
+    void setCurrentSortingMode(int index);
+
+    // ------------------------------------------------------------------------
+    // Filter modes
+    // ------------------------------------------------------------------------
+
+    /**
+     * Get a list of all the available filter modes for this provider.
+     *
+     * @return The list of available filter modes. It may be empty but should
+     *         not be null.
+     */
+    List<FilterMode> getFilterModes();
+
+    /**
+     * Enable the specified filter mode.
+     *
+     * @param index
+     *            The index of the filter mode in the list returned by
+     *            {@link #getFilterModes()}.
+     */
+    void enableFilterMode(int index);
+
+    /**
+     * Disable the specified filter mode.
+     *
+     * @param index
+     *            The index of the filter mode in the list returned by
+     *            {@link #getFilterModes()}.
+     */
+    void disableFilterMode(int index);
+
+    /**
+     * Get the currently active filter modes.
+     *
+     * @return The active filter modes. There might be 0 or more.
+     */
+    Set<FilterMode> getActiveFilterModes();
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProviderFactory.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/ITimeGraphModelProviderFactory.java
new file mode 100644 (file)
index 0000000..940ab92
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
+
+import java.util.function.Supplier;
+
+/**
+ * Factory for {@link ITimeGraphModelProvider} objects.
+ *
+ * Used to register possible time graphs to the framework using the
+ * {@link TimeGraphModelProviderManager}.
+ *
+ * @author Alexandre Montplaisir
+ */
+@FunctionalInterface
+public interface ITimeGraphModelProviderFactory extends Supplier<ITimeGraphModelProvider> {
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/Messages.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/Messages.java
new file mode 100644 (file)
index 0000000..29442b5
--- /dev/null
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Package message bundle
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String DefaultSortingModeName;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {}
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProvider.java
new file mode 100644 (file)
index 0000000..18fa89d
--- /dev/null
@@ -0,0 +1,180 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
+
+import static org.lttng.scope.common.core.NonNullUtils.nullToEmptyString;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.ITimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.ITimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * Base implementation of {@link ITimeGraphModelProvider}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphModelProvider implements ITimeGraphModelProvider {
+
+    /**
+     * A "default" sorting mode, for use when only one is needed.
+     */
+    protected static final SortingMode DEFAULT_SORTING_MODE = new SortingMode(nullToEmptyString(Messages.DefaultSortingModeName));
+
+    private final String fName;
+    private final List<SortingMode> fSortingModes;
+    private final List<FilterMode> fFilterModes;
+
+    private final ITimeGraphModelStateProvider fStateProvider;
+    private final List<ITimeGraphModelArrowProvider> fArrowProviders;
+
+    private final Set<FilterMode> fActiveFilterModes = new HashSet<>();
+    private SortingMode fCurrentSortingMode;
+
+    private final ObjectProperty<@Nullable ITmfTrace> fTraceProperty = new SimpleObjectProperty<>();
+
+    /**
+     * Constructor
+     *
+     * @param name
+     *            The name of this provider
+     * @param sortingModes
+     *            The available sorting modes
+     * @param filterModes
+     *            The available filter modes
+     * @param stateProvider
+     *            The state provider part of this model provider
+     * @param arrowProviders
+     *            The arrow provider(s) supplied by this model provider
+     */
+    public TimeGraphModelProvider(String name,
+            @Nullable List<SortingMode> sortingModes,
+            @Nullable List<FilterMode> filterModes,
+            ITimeGraphModelStateProvider stateProvider,
+            @Nullable List<ITimeGraphModelArrowProvider> arrowProviders) {
+        fName = name;
+
+        fStateProvider = stateProvider;
+        stateProvider.traceProperty().bind(fTraceProperty);
+
+        if (sortingModes == null || sortingModes.isEmpty()) {
+            fSortingModes = ImmutableList.of(DEFAULT_SORTING_MODE);
+        } else {
+            fSortingModes = ImmutableList.copyOf(sortingModes);
+
+        }
+        fCurrentSortingMode = fSortingModes.get(0);
+
+        if (filterModes == null || filterModes.isEmpty()) {
+            fFilterModes = ImmutableList.of();
+        } else {
+            fFilterModes = ImmutableList.copyOf(filterModes);
+        }
+
+        if (arrowProviders == null || arrowProviders.isEmpty()) {
+            fArrowProviders = ImmutableList.of();
+        } else {
+            fArrowProviders = ImmutableList.copyOf(arrowProviders);
+        }
+        fArrowProviders.forEach(ap -> ap.traceProperty().bind(fTraceProperty));
+    }
+
+    @Override
+    public final String getName() {
+        return fName;
+    }
+
+    @Override
+    public final void setTrace(@Nullable ITmfTrace trace) {
+        fTraceProperty.set(trace);
+    }
+
+    @Override
+    public final @Nullable ITmfTrace getTrace() {
+        return fTraceProperty.get();
+    }
+
+    @Override
+    public final ObjectProperty<@Nullable ITmfTrace> traceProperty() {
+        return fTraceProperty;
+    }
+
+    @Override
+    public final ITimeGraphModelStateProvider getStateProvider() {
+        return fStateProvider;
+    }
+
+    @Override
+    public final List<ITimeGraphModelArrowProvider> getArrowProviders() {
+        return fArrowProviders;
+    }
+
+    // ------------------------------------------------------------------------
+    // Render generation methods. Implementation left to subclasses.
+    // ------------------------------------------------------------------------
+
+    @Override
+    public abstract TimeGraphTreeRender getTreeRender();
+
+    // ------------------------------------------------------------------------
+    // Sorting modes
+    // ------------------------------------------------------------------------
+
+    @Override
+    public final List<SortingMode> getSortingModes() {
+        return fSortingModes;
+    }
+
+    @Override
+    public final SortingMode getCurrentSortingMode() {
+        return fCurrentSortingMode;
+    }
+
+    @Override
+    public final void setCurrentSortingMode(int index) {
+        fCurrentSortingMode = fSortingModes.get(index);
+    }
+
+    // ------------------------------------------------------------------------
+    // Filter modes
+    // ------------------------------------------------------------------------
+
+    @Override
+    public final List<FilterMode> getFilterModes() {
+        return fFilterModes;
+    }
+
+    @Override
+    public final void enableFilterMode(int index) {
+        fActiveFilterModes.add(fFilterModes.get(index));
+    }
+
+    @Override
+    public final void disableFilterMode(int index) {
+        fActiveFilterModes.remove(fFilterModes.get(index));
+    }
+
+    @Override
+    public final Set<FilterMode> getActiveFilterModes() {
+        return ImmutableSet.copyOf(fActiveFilterModes);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProviderManager.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/TimeGraphModelProviderManager.java
new file mode 100644 (file)
index 0000000..3dad27e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Manager for the time graph model providers.
+ *
+ * Components can register their possible time graphs, and views etc. may query
+ * and instantiate them.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class TimeGraphModelProviderManager {
+
+    /**
+     * Interface for classes displaying Time Graphs. Implement this interface and
+     * register your class using {@link #registerOutput} to receive notifications
+     * about current and newly-registered time graph providers.
+     */
+    public static interface TimeGraphOutput {
+
+        /**
+         * Callback called by the provider manager when model providers are registered.
+         *
+         * @param factory
+         *            The model provider factory
+         */
+        void providerRegistered(ITimeGraphModelProviderFactory factory);
+    }
+
+    private static final TimeGraphModelProviderManager INSTANCE = new TimeGraphModelProviderManager();
+
+    private TimeGraphModelProviderManager() {}
+
+    /**
+     * Get the singleton instance of this manager.
+     *
+     * @return The instance
+     */
+    public static TimeGraphModelProviderManager instance() {
+        return INSTANCE;
+    }
+
+    private final Set<ITimeGraphModelProviderFactory> fRegisteredProviderFactories = new LinkedHashSet<>();
+    private final Set<TimeGraphOutput> fRegisteredOutputs = new HashSet<>();
+
+    /**
+     * Register a time graph provider by specifying its
+     * {@link ITimeGraphModelProviderFactory}. If a factory is already registered,
+     * this method will have no effect.
+     *
+     * @param factory
+     *            The provider's factory
+     */
+    public synchronized void registerProviderFactory(ITimeGraphModelProviderFactory factory) {
+        boolean added = fRegisteredProviderFactories.add(factory);
+        if (added) {
+            fRegisteredOutputs.forEach(output -> output.providerRegistered(factory));
+        }
+    }
+
+    /**
+     * Register a time graph output to this manager. Upon registration,
+     * notifications will be sent, using {@link TimeGraphOutput#providerRegistered},
+     * for all existing providers. Additional notifications will be sent for future
+     * registered providers.
+     *
+     * @param output
+     *            The time graph output to register
+     */
+    public synchronized void registerOutput(TimeGraphOutput output) {
+        /* Send notifications for currently-registered factories */
+        fRegisteredProviderFactories.forEach(factory -> output.providerRegistered(factory));
+        fRegisteredOutputs.add(output);
+    }
+
+    /**
+     * Unregister a previously-registered output, so that it does not receive any
+     * more notifications. Has no effect if the output was not already registered.
+     *
+     * @param output
+     *            The output to unregister
+     */
+    public synchronized void unregisterOutput(TimeGraphOutput output) {
+        fRegisteredOutputs.remove(output);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/ITimeGraphModelArrowProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/ITimeGraphModelArrowProvider.java
new file mode 100644 (file)
index 0000000..bee5fa6
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows;
+
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+
+/**
+ * Provider for timegraph arrow series.
+ *
+ * It can be used stand-alone (ie, for testing) but usually would be part of a
+ * {@link org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ITimeGraphModelArrowProvider {
+
+    /**
+     * Property representing the trace this arrow provider fetches its data for.
+     *
+     * @return The trace property
+     */
+    ObjectProperty<@Nullable ITmfTrace> traceProperty();
+
+    /**
+     * Property indicating if this specific arrow provider is currently enabled
+     * or not.
+     *
+     * Normally the controls should not send queries to this provider if it is
+     * disabled (they would only query this state), but the arrow provider is
+     * free to make use of this property for other reasons.
+     *
+     * @return The enabled property
+     */
+    BooleanProperty enabledProperty();
+
+    /**
+     * Get the arrow series supplied by this arrow provider. A single provider
+     * supplies one and only one arrow series.
+     *
+     * @return The arrow series
+     */
+    TimeGraphArrowSeries getArrowSeries();
+
+    /**
+     * Get a render of arrows from this arrow provider.
+     *
+     * @param treeRender
+     *            The tree render for which the query is done
+     * @param timeRange
+     *            The time range of the query. The provider may decide to
+     *            include arrows partially inside this range, or not.
+     * @param task
+     *            An optional task parameter, which can be checked for
+     *            cancellation to stop processing at any point and return.
+     * @return The corresponding arrow render
+     */
+    TimeGraphArrowRender getArrowRender(TimeGraphTreeRender treeRender, TimeRange timeRange, @Nullable FutureTask<?> task);
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/TimeGraphModelArrowProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/TimeGraphModelArrowProvider.java
new file mode 100644 (file)
index 0000000..f3833cb
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * Basic implementation of {@link ITimeGraphModelArrowProvider}. It takes care
+ * of basic definitions of the interface, and lets query definition (the
+ * {@link #getArrowRender} method) up to the subclass.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphModelArrowProvider implements ITimeGraphModelArrowProvider {
+
+    private final ObjectProperty<@Nullable ITmfTrace> fTraceProperty = new SimpleObjectProperty<>(null);
+    private final BooleanProperty fEnabledProperty = new SimpleBooleanProperty(false);
+    private final TimeGraphArrowSeries fArrowSeries;
+
+    /**
+     * Constructor
+     *
+     * @param arrowSeries
+     *            The arrow series that will be represented by this arrow
+     *            provider
+     */
+    public TimeGraphModelArrowProvider(TimeGraphArrowSeries arrowSeries) {
+        fArrowSeries = arrowSeries;
+    }
+
+    @Override
+    public final ObjectProperty<@Nullable ITmfTrace> traceProperty() {
+        return fTraceProperty;
+    }
+
+    @Override
+    public BooleanProperty enabledProperty() {
+        return fEnabledProperty;
+    }
+
+    @Override
+    public final TimeGraphArrowSeries getArrowSeries() {
+        return fArrowSeries;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/arrows/package-info.java
new file mode 100644 (file)
index 0000000..e6f3fbe
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/ITimeGraphDrawnEventProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/ITimeGraphDrawnEventProvider.java
new file mode 100644 (file)
index 0000000..a9631a9
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents;
+
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+
+/**
+ * Interface defining providers of drawn events.
+ *
+ * A "drawn event" is a symbol in a timegraph representing one particular
+ * (trace) event. Contrary to a trace event, a drawn event has both horizontal
+ * and vertical coordinates, being time and its corresponding time graph entry.
+ *
+ * @author Alexandre Montplaisir
+ * @see TimeGraphDrawnEventProviderManager
+ */
+public interface ITimeGraphDrawnEventProvider {
+
+    /**
+     * The trace from which the provider will fetch its events. The Property
+     * mechanisms can be used to sync with trace changes elsewhere in the
+     * framework.
+     *
+     * @return The target trace
+     */
+    ObjectProperty<@Nullable ITmfTrace> traceProperty();
+
+    /**
+     * The 'enabled' property of this provider. A provider can be created and
+     * registered to the manager, but considered disabled so that it stops
+     * painting its events.
+     *
+     * For example, if the user can uncheck elements in a list showing all
+     * existing event providers, this could disable said providers without
+     * completely destroying them.
+     *
+     * @return The enabled property
+     */
+    BooleanProperty enabledProperty();
+
+    /**
+     * Return the {@link TimeGraphDrawnEventSeries} provided by this provider. A
+     * single provider should manage a single event series; different providers
+     * should be implemented for different series.
+     *
+     * @return The event series of this provider
+     */
+    TimeGraphDrawnEventSeries getEventSeries();
+
+    /**
+     * Query for a render of drawn events on this provider.
+     *
+     * @param treeRender
+     *            The tree render of the timegraph to paint on
+     * @param timeRange
+     *            The time range of the timegraph (and trace) to run the query
+     *            on.
+     * @param task
+     *            If this method is called from within a {@link FutureTask}, it
+     *            can optionally be passed here. If the execution is expected to
+     *            take a long time, the implementation is suggested to
+     *            periodically check this parameter for cancellation.
+     * @return The corresponding event render that contains the result of the
+     *         query
+     */
+    TimeGraphDrawnEventRender getEventRender(TimeGraphTreeRender treeRender,
+            TimeRange timeRange, @Nullable FutureTask<?> task);
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProvider.java
new file mode 100644 (file)
index 0000000..2ace590
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * Basic implementation of {@link ITimeGraphDrawnEventProvider}.
+ *
+ * Implementation of the {@link #getEventRender} method is left to subclasses.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphDrawnEventProvider implements ITimeGraphDrawnEventProvider {
+
+    private final ObjectProperty<@Nullable ITmfTrace> fTraceProperty = new SimpleObjectProperty<>(null);
+    private final BooleanProperty fEnabledProperty = new SimpleBooleanProperty(false);
+    private final TimeGraphDrawnEventSeries fDrawnEventSeries;
+
+    /**
+     * Constructor
+     *
+     * @param drawnEventSeries
+     *            The event series provided by this provider.
+     */
+    protected TimeGraphDrawnEventProvider(TimeGraphDrawnEventSeries drawnEventSeries) {
+        fDrawnEventSeries = drawnEventSeries;
+    }
+
+    @Override
+    public final ObjectProperty<@Nullable ITmfTrace> traceProperty() {
+        return fTraceProperty;
+    }
+
+    @Override
+    public final BooleanProperty enabledProperty() {
+        return fEnabledProperty;
+    }
+
+    @Override
+    public final TimeGraphDrawnEventSeries getEventSeries() {
+        return fDrawnEventSeries;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProviderManager.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/TimeGraphDrawnEventProviderManager.java
new file mode 100644 (file)
index 0000000..4aac391
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents;
+
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableSet;
+
+/**
+ * Manager of {@link ITimeGraphDrawnEventProvider}s.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class TimeGraphDrawnEventProviderManager {
+
+    private static final Comparator<ITimeGraphDrawnEventProvider> COMPARATOR =
+            Comparator.comparing(provider -> provider.getEventSeries().getSeriesName());
+
+    private static final TimeGraphDrawnEventProviderManager INSTANCE = new TimeGraphDrawnEventProviderManager();
+
+    private final ObservableSet<ITimeGraphDrawnEventProvider> fRegisteredProviders =
+            FXCollections.observableSet(new TreeSet<>(COMPARATOR));
+
+    private TimeGraphDrawnEventProviderManager() {}
+
+    /**
+     * Get the singleton instance.
+     *
+     * @return The instance
+     */
+    public static TimeGraphDrawnEventProviderManager instance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Return the {@link ObservableSet} of registered providers.
+     *
+     * You can use {@link Set#add} and {@link Set#remove} to register and
+     * deregister providers, but also the {@link ObservableSet#addListener}
+     * methods to be notified of provider changes.
+     *
+     * @return The registered providers
+     */
+    public ObservableSet<ITimeGraphDrawnEventProvider> getRegisteredProviders() {
+        return fRegisteredProviders;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/drawnevents/package-info.java
new file mode 100644 (file)
index 0000000..3afaaa8
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/messages.properties b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/messages.properties
new file mode 100644 (file)
index 0000000..96fad2f
--- /dev/null
@@ -0,0 +1,10 @@
+###############################################################################
+# Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+DefaultSortingModeName = Default
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/package-info.java
new file mode 100644 (file)
index 0000000..f977cbd
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/ITimeGraphModelStateProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/ITimeGraphModelStateProvider.java
new file mode 100644 (file)
index 0000000..2422328
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.states;
+
+import java.util.List;
+import java.util.concurrent.FutureTask;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.StateDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import javafx.beans.property.ObjectProperty;
+
+/**
+ * Provider for timegraph state intervals.
+ *
+ * It can be used stand-alone (ie, for testing) but usually would be part of a
+ * {@link org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface ITimeGraphModelStateProvider {
+
+    /**
+     * Property representing the trace this arrow provider fetches its data for.
+     *
+     * @return The trace property
+     */
+    ObjectProperty<@Nullable ITmfTrace> traceProperty();
+
+    /**
+     * Get an aggregated list of {@link StateDefinition} used in this provider.
+     *
+     * @return The state definitions used in this provider
+     */
+    List<StateDefinition> getStateDefinitions();
+
+    /**
+     * Get a state render for a single tree element
+     *
+     * @param treeElement
+     *            The tree element to target
+     * @param timeRange
+     *            The time range of the query
+     * @param resolution
+     *            The resolution, in timestamp units, of the query. For example,
+     *            a resolution of 1000 means the provider does not need to
+     *            return states that don't cross at least 1 1000-units borders.
+     * @param task
+     *            An optional task parameter, which can be checked for
+     *            cancellation to stop processing at any point and return.
+     * @return The corresponding state render for this tree element and settings
+     */
+    TimeGraphStateRender getStateRender(TimeGraphTreeElement treeElement,
+            TimeRange timeRange, long resolution, @Nullable FutureTask<?> task);
+
+    /**
+     * Helper method to fetch all the state renders for all tree elements of a
+     * *tree* render.
+     *
+     * Default implementation simply calls {@link #getStateRender} on each tree
+     * element sequentially, but more advanced providers could override it if
+     * they can provide a faster mechanism.
+     *
+     * @param treeRender
+     *            The tree render for which to prepare all the state renders
+     * @param timeRange
+     *            The time range of the query
+     * @param resolution
+     *            The resolution of the query, see {@link #getStateRender}.
+     * @param task
+     *            The optional task parameter which can be used for
+     *            cancellation.
+     * @return The corresponding state renders
+     */
+    default List<TimeGraphStateRender> getStateRenders(TimeGraphTreeRender treeRender, TimeRange timeRange, long resolution, @Nullable FutureTask<?> task) {
+        return treeRender.getAllTreeElements().stream()
+                .map(treeElem -> getStateRender(treeElem, timeRange, resolution, task))
+                .collect(Collectors.toList());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/TimeGraphModelStateProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/TimeGraphModelStateProvider.java
new file mode 100644 (file)
index 0000000..0fa4250
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.states;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.StateDefinition;
+
+import com.google.common.collect.ImmutableList;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * Basic implementation of {@link ITimeGraphModelStateProvider}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphModelStateProvider implements ITimeGraphModelStateProvider {
+
+    private final ObjectProperty<@Nullable ITmfTrace> fTraceProperty = new SimpleObjectProperty<>(null);
+
+    private final List<StateDefinition> fStateDefinitions;
+
+    /**
+     * Constructor
+     *
+     * @param stateDefinitions
+     *            The state definitions used in this provider
+     */
+    public TimeGraphModelStateProvider(List<StateDefinition> stateDefinitions) {
+        fStateDefinitions = ImmutableList.copyOf(stateDefinitions);
+    }
+
+    @Override
+    public ObjectProperty<@Nullable ITmfTrace> traceProperty() {
+        return fTraceProperty;
+    }
+
+    @Override
+    public List<StateDefinition> getStateDefinitions() {
+        return fStateDefinitions;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/states/package-info.java
new file mode 100644 (file)
index 0000000..f1f8b6c
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.states;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelArrowProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelArrowProvider.java
new file mode 100644 (file)
index 0000000..3e79da8
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
+
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.TimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import ca.polymtl.dorsal.libdelorean.ITmfStateSystem;
+
+/**
+ * Basic implementation of a {@link TimeGraphModelArrowProvider} backed by a
+ * state system.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class StateSystemModelArrowProvider extends TimeGraphModelArrowProvider {
+
+    private final String fStateSystemModuleId;
+
+    private transient @Nullable ITmfStateSystem fStateSystem = null;
+
+    /**
+     * Constructor
+     *
+     * @param arrowSeries
+     *            The arrow series that will be represented by this arrow
+     *            provider
+     * @param stateSystemModuleId
+     *            The ID of the state system from which the information should
+     *            be fetched
+     */
+    public StateSystemModelArrowProvider(TimeGraphArrowSeries arrowSeries,
+            String stateSystemModuleId) {
+        super(arrowSeries);
+        fStateSystemModuleId = stateSystemModuleId;
+
+        /*
+         * Change listener which will take care of keeping the target state
+         * system up to date.
+         */
+        traceProperty().addListener((obs, oldValue, newValue) -> {
+            ITmfTrace trace = newValue;
+            if (trace == null) {
+                fStateSystem = null;
+                return;
+            }
+
+            // FIXME Remove the extra thread once we move to Jabberwocky
+            Thread thread = new Thread(() -> {
+                fStateSystem = TmfStateSystemAnalysisModule.getStateSystem(trace, fStateSystemModuleId);
+            });
+            thread.start();
+        });
+    }
+
+    /**
+     * The state system from which the data should be fetched. This will be kept
+     * in sync with the {@link #traceProperty}.
+     *
+     * @return The target state system. It will be null if the current trace is
+     *         null.
+     */
+    protected final @Nullable ITmfStateSystem getStateSystem() {
+        return fStateSystem;
+    }
+
+    @Override
+    public abstract TimeGraphArrowRender getArrowRender(TimeGraphTreeRender treeRender, TimeRange timeRange, @Nullable FutureTask<?> task);
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelProvider.java
new file mode 100644 (file)
index 0000000..2b251dc
--- /dev/null
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.function.Function;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.TimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.ITimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.ITimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import ca.polymtl.dorsal.libdelorean.ITmfStateSystem;
+import ca.polymtl.dorsal.libdelorean.exceptions.StateSystemDisposedException;
+import ca.polymtl.dorsal.libdelorean.interval.ITmfStateInterval;
+
+/**
+ * Basic implementation of a {@link TimeGraphModelProvider} backed by a state
+ * system.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class StateSystemModelProvider extends TimeGraphModelProvider {
+
+    /**
+     * The context of a tree render. Should contain all the information to
+     * generate the corresponding tree render, according to all configuration
+     * options like sorting, filtering etc. specified by the user.
+     */
+    protected static final class TreeRenderContext {
+
+        /** Trace name */
+        public final String traceName;
+        /** State system */
+        public final ITmfStateSystem ss;
+        /** Sorting mode */
+        public final SortingMode sortingMode;
+        /** Filter modes */
+        public final Set<FilterMode> filterModes;
+        /** Full query */
+        public final List<ITmfStateInterval> fullQueryAtRangeStart;
+
+        /**
+         * Constructor
+         *
+         * @param ss
+         *            State system
+         * @param sortingMode
+         *            Current sorting mode
+         * @param filterModes
+         *            Current filter modes
+         * @param fullQueryAtRangeStart
+         *            Full query at the start of the time range.
+         */
+        public TreeRenderContext(String traceName,
+                ITmfStateSystem ss,
+                SortingMode sortingMode,
+                Set<FilterMode> filterModes,
+                List<ITmfStateInterval> fullQueryAtRangeStart) {
+            this.traceName = traceName;
+            this.ss = ss;
+            this.sortingMode = sortingMode;
+            this.filterModes = filterModes;
+            this.fullQueryAtRangeStart = fullQueryAtRangeStart;
+        }
+    }
+
+    /**
+     * Class to encapsulate a cached {@link TimeGraphTreeRender}. This render
+     * should never change, except if the number of attributes in the state
+     * system does (for example, if queries were made before the state system
+     * was done building).
+     */
+    private static final class CachedTreeRender {
+
+        public final int nbAttributes;
+        public final TimeGraphTreeRender treeRender;
+
+        public CachedTreeRender(int nbAttributes, TimeGraphTreeRender treeRender) {
+            this.nbAttributes = nbAttributes;
+            this.treeRender = treeRender;
+        }
+    }
+
+    private final String fStateSystemModuleId;
+    private final Function<TreeRenderContext, TimeGraphTreeRender> fTreeRenderFunction;
+
+    private final Map<ITmfStateSystem, CachedTreeRender> fLastTreeRenders = new WeakHashMap<>();
+
+    private transient @Nullable ITmfStateSystem fStateSystem = null;
+
+    /**
+     * Constructor
+     *
+     * @param name
+     *            The name of this provider
+     * @param sortingModes
+     *            The available sorting modes
+     * @param filterModes
+     *            The available filter modes
+     * @param stateProvider
+     *            The state provider part of this model provider
+     * @param arrowProviders
+     *            The arrow provider(s) supplied by this model provider
+     * @param stateSystemModuleId
+     *            ID of the state system from which data will be fetched
+     * @param treeRenderFunction
+     *            Function to generate a tree render for a given tree context
+     */
+    public StateSystemModelProvider(String name,
+            @Nullable List<SortingMode> sortingModes,
+            @Nullable List<FilterMode> filterModes,
+            ITimeGraphModelStateProvider stateProvider,
+            @Nullable List<ITimeGraphModelArrowProvider> arrowProviders,
+            String stateSystemModuleId,
+            Function<TreeRenderContext, TimeGraphTreeRender> treeRenderFunction) {
+
+        super(name, sortingModes, filterModes, stateProvider, arrowProviders);
+
+        fStateSystemModuleId = stateSystemModuleId;
+        fTreeRenderFunction = treeRenderFunction;
+
+        /*
+         * Change listener which will take care of keeping the target state
+         * system up to date.
+         */
+        traceProperty().addListener((obs, oldValue, newValue) -> {
+            ITmfTrace trace = newValue;
+            if (trace == null) {
+                fStateSystem = null;
+                return;
+            }
+
+            /*
+             * Set the state system in another thread, so that if it blocks on
+             * waitForInitialization, it does not block the application
+             * thread...
+             *
+             * FIXME We ought to get rid of this blocking in Jabberwocky.
+             */
+            Thread thread = new Thread(() -> {
+                fStateSystem = TmfStateSystemAnalysisModule.getStateSystem(trace, fStateSystemModuleId);
+            });
+            thread.start();
+        });
+    }
+
+    // ------------------------------------------------------------------------
+    // Render generation methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    public @NonNull TimeGraphTreeRender getTreeRender() {
+        ITmfStateSystem ss = fStateSystem;
+        if (ss == null) {
+            /* This trace does not provide the expected state system */
+            return TimeGraphTreeRender.EMPTY_RENDER;
+        }
+
+      CachedTreeRender cachedRender = fLastTreeRenders.get(ss);
+      if (cachedRender != null && cachedRender.nbAttributes == ss.getNbAttributes()) {
+          /* The last render is still valid, we can re-use it */
+          return cachedRender.treeRender;
+      }
+
+        /* First generate the tree render context */
+        List<ITmfStateInterval> fullStateAtStart;
+        try {
+            fullStateAtStart = ss.queryFullState(ss.getStartTime());
+        } catch (StateSystemDisposedException e) {
+            return TimeGraphTreeRender.EMPTY_RENDER;
+        }
+
+        ITmfTrace trace = getTrace();
+        String traceName = (trace == null ? "" : trace.getName()); //$NON-NLS-1$
+
+        TreeRenderContext treeContext = new TreeRenderContext(traceName,
+                ss,
+                getCurrentSortingMode(),
+                getActiveFilterModes(),
+                fullStateAtStart);
+
+        /* Generate a new tree render */
+        TimeGraphTreeRender treeRender = fTreeRenderFunction.apply(treeContext);
+
+        fLastTreeRenders.put(ss, new CachedTreeRender(ss.getNbAttributes(), treeRender));
+        return treeRender;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelStateProvider.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemModelStateProvider.java
new file mode 100644 (file)
index 0000000..797a9f9
--- /dev/null
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.FutureTask;
+import java.util.function.Function;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.statesystem.TmfStateSystemAnalysisModule;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.MathUtils;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.TimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.StateDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.BasicTimeGraphStateInterval;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.MultiStateInterval;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateInterval;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+import com.google.common.collect.Iterables;
+
+import ca.polymtl.dorsal.libdelorean.ITmfStateSystem;
+import ca.polymtl.dorsal.libdelorean.exceptions.AttributeNotFoundException;
+import ca.polymtl.dorsal.libdelorean.exceptions.StateSystemDisposedException;
+import ca.polymtl.dorsal.libdelorean.interval.ITmfStateInterval;
+
+/**
+ * Basic implementation of a {@link TimeGraphModelStateProvider} backed by a state
+ * system.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class StateSystemModelStateProvider extends TimeGraphModelStateProvider {
+
+    /**
+     * The context of a single state interval. Should contain all the
+     * information required to generate the state interval in the render (name,
+     * color, etc.)
+     */
+    protected static final class StateIntervalContext {
+
+        /** State system */
+        public final ITmfStateSystem ss;
+        /** Base tree element */
+        public final StateSystemTimeGraphTreeElement baseTreeElement;
+        /** Source interval */
+        public final ITmfStateInterval sourceInterval;
+
+        /**
+         * Constructor
+         *
+         * @param ss
+         *            State system
+         * @param baseTreeElement
+         *            Tree element for which the data should be fetched. It may
+         *            not correspond directly to the state's tree element, a
+         *            relative path may be used, for example for additional data
+         *            stored in a separate attribute.
+         * @param sourceInterval
+         *            The state system interval which will be represented by the
+         *            model state interval
+         */
+        public StateIntervalContext(ITmfStateSystem ss,
+                StateSystemTimeGraphTreeElement baseTreeElement,
+                ITmfStateInterval sourceInterval) {
+            this.ss = ss;
+            this.baseTreeElement = baseTreeElement;
+            this.sourceInterval = sourceInterval;
+        }
+    }
+
+    private final String fStateSystemModuleId;
+    private final Function<StateIntervalContext, TimeGraphStateInterval> fIntervalMappingFunction;
+
+    /**
+     * This state system here is not necessarily the same as the one in the
+     * {@link StateSystemModelProvider}!
+     */
+    private transient @Nullable ITmfStateSystem fStateSystem = null;
+
+    /**
+     * Constructor
+     *
+     * TODO Maybe merge the various Functions into a single class?
+     *
+     * @param stateDefinitions
+     *            The state definitions used in this provider
+     * @param stateSystemModuleId
+     *            The ID of the state system from which to fetch the information
+     * @param stateNameMappingFunction
+     *            Mapping function from state interval context to state name
+     * @param labelMappingFunction
+     *            Mapping function from state interval context to state label
+     * @param colorMappingFunction
+     *            Mapping function from state interval context to state color
+     * @param lineThicknessMappingFunction
+     *            Mapping function from state interval context to line thickness
+     * @param propertiesMappingFunction
+     *            Mapping function from state interval context to properties
+     */
+    public StateSystemModelStateProvider(
+            List<StateDefinition> stateDefinitions,
+            String stateSystemModuleId,
+            Function<StateIntervalContext, String> stateNameMappingFunction,
+            Function<StateIntervalContext, @Nullable String> labelMappingFunction,
+            Function<StateIntervalContext, ConfigOption<ColorDefinition>> colorMappingFunction,
+            Function<StateIntervalContext, ConfigOption<LineThickness>> lineThicknessMappingFunction,
+            Function<StateIntervalContext, Map<String, String>> propertiesMappingFunction) {
+
+        super(stateDefinitions);
+
+        fStateSystemModuleId = stateSystemModuleId;
+
+        fIntervalMappingFunction = ssCtx -> {
+            return new BasicTimeGraphStateInterval(
+                    ssCtx.sourceInterval.getStartTime(),
+                    ssCtx.sourceInterval.getEndTime(),
+                    ssCtx.baseTreeElement,
+                    stateNameMappingFunction.apply(ssCtx),
+                    labelMappingFunction.apply(ssCtx),
+                    colorMappingFunction.apply(ssCtx),
+                    lineThicknessMappingFunction.apply(ssCtx),
+                    propertiesMappingFunction.apply(ssCtx));
+        };
+
+        /*
+         * Change listener which will take care of keeping the target state
+         * system up to date.
+         */
+        traceProperty().addListener((obs, oldValue, newValue) -> {
+            ITmfTrace trace = newValue;
+            if (trace == null) {
+                fStateSystem = null;
+                return;
+            }
+
+            // FIXME Remove the extra thread once we move to Jabberwocky
+            Thread thread = new Thread(() -> {
+                fStateSystem = TmfStateSystemAnalysisModule.getStateSystem(trace, fStateSystemModuleId);
+            });
+            thread.start();
+        });
+
+    }
+
+    // ------------------------------------------------------------------------
+    // Render generation methods
+    // ------------------------------------------------------------------------
+
+    @Override
+    public TimeGraphStateRender getStateRender(TimeGraphTreeElement treeElement,
+            TimeRange timeRange, long resolution, @Nullable FutureTask<?> task) {
+
+        ITmfStateSystem ss = fStateSystem;
+        /*
+         * Sometimes ss is null with uninitialized or empty views, just keep the model
+         * empty.
+         */
+        if (ss == null
+                || (task != null && task.isCancelled())
+                /* "Title" entries should be ignored */
+                || !(treeElement instanceof StateSystemTimeGraphTreeElement)) {
+
+            return TimeGraphStateRender.EMPTY_RENDER;
+        }
+        StateSystemTimeGraphTreeElement treeElem = (StateSystemTimeGraphTreeElement) treeElement;
+
+        /* Prepare the state intervals */
+        List<TimeGraphStateInterval> intervals;
+        try {
+            intervals = queryHistoryRange(ss, treeElem,
+                    timeRange.getStart(), timeRange.getEnd(), resolution, task);
+        } catch (AttributeNotFoundException | StateSystemDisposedException e) {
+            intervals = Collections.emptyList();
+        }
+
+        return new TimeGraphStateRender(timeRange, treeElement, intervals);
+    }
+
+    private List<TimeGraphStateInterval> queryHistoryRange(ITmfStateSystem ss,
+            StateSystemTimeGraphTreeElement treeElem,
+            final long t1, final long t2, final long resolution,
+            @Nullable FutureTask<?> task)
+            throws AttributeNotFoundException, StateSystemDisposedException {
+
+        /* Validate the parameters. */
+        if (t2 < t1 || resolution <= 0) {
+            throw new IllegalArgumentException(ss.getSSID() + " Start:" + t1 + ", End:" + t2 + ", Resolution:" + resolution); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        }
+
+        final List<TimeGraphStateInterval> modelIntervals = new LinkedList<>();
+        final int attributeQuark = treeElem.getSourceQuark();
+        ITmfStateInterval lastAddedInterval = null;
+
+        /* Actual valid start/end time of the range query. */
+        long tStart = Math.max(t1, ss.getStartTime());
+        long tEnd = Math.min(t2, ss.getCurrentEndTime());
+
+        /*
+         * First, iterate over the "resolution points" and keep all matching
+         * state intervals.
+         */
+        for (long ts = tStart; ts <= tEnd - resolution; ts += resolution) {
+            /*
+             * Skip queries if the corresponding interval was already included
+             */
+            if (lastAddedInterval != null && lastAddedInterval.getEndTime() >= ts) {
+                long nextTOffset = MathUtils.roundToClosestHigherMultiple(lastAddedInterval.getEndTime() - tStart, resolution);
+                long nextTs = tStart + nextTOffset;
+                if (nextTs == ts) {
+                    /*
+                     * The end time of the last interval happened to be exactly
+                     * equal to the next resolution point. We will go to the
+                     * resolution point after that then.
+                     */
+                    ts = nextTs;
+                } else {
+                    /* 'ts' will get incremented at next loop */
+                    ts = nextTs - resolution;
+                }
+                continue;
+            }
+
+            ITmfStateInterval stateSystemInterval = ss.querySingleState(ts, attributeQuark);
+
+            /*
+             * Only pick the interval if it fills the current resolution range,
+             * from 'ts' to 'ts + resolution' (or 'ts2').
+             */
+            long ts2 = ts + resolution;
+            if (stateSystemInterval.getStartTime() <= ts && stateSystemInterval.getEndTime() >= ts2) {
+                TimeGraphStateInterval interval = ssIntervalToModelInterval(ss, treeElem, stateSystemInterval);
+                modelIntervals.add(interval);
+                lastAddedInterval = stateSystemInterval;
+            }
+        }
+
+        /*
+         * For the very last interval, we'll use ['tEnd - resolution', 'tEnd']
+         * as a range condition instead.
+         */
+        long ts = Math.max(tStart, tEnd - resolution);
+        long ts2 = tEnd;
+        if (lastAddedInterval != null && lastAddedInterval.getEndTime() >= ts) {
+            /* Interval already included */
+        } else {
+            ITmfStateInterval stateSystemInterval = ss.querySingleState(ts, attributeQuark);
+            if (stateSystemInterval.getStartTime() <= ts && stateSystemInterval.getEndTime() >= ts2) {
+                TimeGraphStateInterval interval = ssIntervalToModelInterval(ss, treeElem, stateSystemInterval);
+                modelIntervals.add(interval);
+            }
+        }
+
+        /*
+         * 'modelIntervals' now contains all the "real" intervals we will want
+         * to display in the view. Poly-filla the holes in between each using
+         * multi-state intervals.
+         */
+        if (modelIntervals.size() < 2) {
+            return modelIntervals;
+        }
+
+        List<TimeGraphStateInterval> filledIntervals = new LinkedList<>();
+        /*
+         * Add the first real interval. There might be a multi-state at the
+         * beginning.
+         */
+        long firstRealIntervalStartTime = modelIntervals.get(0).getStartTime();
+        if (firstRealIntervalStartTime > tStart) {
+            filledIntervals.add(new MultiStateInterval(tStart, firstRealIntervalStartTime - 1, treeElem));
+        }
+        filledIntervals.add(modelIntervals.get(0));
+
+        for (int i = 1; i < modelIntervals.size(); i++) {
+            TimeGraphStateInterval interval1 = modelIntervals.get(i - 1);
+            TimeGraphStateInterval interval2 = modelIntervals.get(i);
+            long bound1 = interval1.getEndTime();
+            long bound2 = interval2.getStartTime();
+
+            /* (we've already inserted 'interval1' on the previous loop.) */
+            if (bound1 + 1 != bound2) {
+                TimeGraphStateInterval multiStateInterval = new MultiStateInterval(bound1 + 1, bound2 - 1, treeElem);
+                filledIntervals.add(multiStateInterval);
+            }
+            filledIntervals.add(interval2);
+        }
+
+        /* Add a multi-state at the end too, if needed */
+        long lastRealIntervalEndTime = Iterables.getLast(modelIntervals).getEndTime();
+        if (lastRealIntervalEndTime < t2) {
+            filledIntervals.add(new MultiStateInterval(lastRealIntervalEndTime + 1, t2, treeElem));
+        }
+
+        return filledIntervals;
+    }
+
+    private TimeGraphStateInterval ssIntervalToModelInterval(ITmfStateSystem ss,
+            StateSystemTimeGraphTreeElement treeElem, ITmfStateInterval interval) {
+        StateIntervalContext siCtx = new StateIntervalContext(ss, treeElem, interval);
+        return fIntervalMappingFunction.apply(siCtx);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemTimeGraphTreeElement.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/StateSystemTimeGraphTreeElement.java
new file mode 100644 (file)
index 0000000..b7a1cf7
--- /dev/null
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
+
+import java.util.List;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+/**
+ * Implementation of a {@link TimeGraphTreeElement} specific for use by
+ * {@link StateSystemModelProvider}. It links a state system quark to the tree
+ * element.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class StateSystemTimeGraphTreeElement extends TimeGraphTreeElement {
+
+    private final int fSourceQuark;
+
+    /**
+     * Constructor
+     *
+     * @param name
+     *            The name this tree element should have.
+     * @param children
+     *            The children tree elements. You can pass an empty list for no
+     *            children.
+     * @param sourceQuark
+     *            The state system quark wrapped by this tree element
+     */
+    public StateSystemTimeGraphTreeElement(String name,
+            List<TimeGraphTreeElement> children,
+            int sourceQuark) {
+        super(name, children);
+        fSourceQuark = sourceQuark;
+    }
+
+    /**
+     * Get the quark wrapped by this tree element.
+     *
+     * @return The source quark
+     */
+    public int getSourceQuark() {
+        return fSourceQuark;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/provider/statesystem/package-info.java
new file mode 100644 (file)
index 0000000..1c01f45
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.provider.statesystem;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/ColorDefinition.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/ColorDefinition.java
new file mode 100644 (file)
index 0000000..805bc61
--- /dev/null
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
+
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * Basic headless color definition, using RGB(A) values.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ColorDefinition {
+
+    /** Minimum value for the parameters */
+    public static final int MIN = 0;
+    /** Maximum value for the parameters */
+    public static final int MAX = 255;
+
+    /** Red RGB component */
+    public final int fRed;
+    /** Green RGB component */
+    public final int fGreen;
+    /** Blue RGB component */
+    public final int fBlue;
+    /** Alpha component */
+    public final int fAlpha;
+
+    /**
+     * Specify a color by RGB values, using maximum (full) alpha.
+     *
+     * @param red
+     *            Red component, from 0 to 255
+     * @param green
+     *            Green component, from 0 to 255
+     * @param blue
+     *            Blue component, from 0 to 255
+     */
+    public ColorDefinition(int red, int green, int blue) {
+        this(red, green, blue, MAX);
+    }
+
+    /**
+     * Specify a color by RGBA values.
+     *
+     * @param red
+     *            Red component, from 0 to 255
+     * @param green
+     *            Green component, from 0 to 255
+     * @param blue
+     *            Blue component, from 0 to 255
+     * @param alpha
+     *            Alpha (opacity) component, from 0 to 255
+     */
+    public ColorDefinition(int red, int green, int blue, int alpha) {
+        checkValue(red);
+        checkValue(green);
+        checkValue(blue);
+        checkValue(alpha);
+
+        fRed = red;
+        fGreen = green;
+        fBlue = blue;
+        fAlpha = alpha;
+    }
+
+    private static void checkValue(int value) throws IllegalArgumentException {
+        if (value < MIN || value > MAX) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fRed, fGreen, fBlue, fAlpha);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        ColorDefinition other = (ColorDefinition) obj;
+        return (fRed == other.fRed
+                && fGreen == other.fGreen
+                && fBlue == other.fBlue
+                && fAlpha == other.fAlpha);
+    }
+
+    @Override
+    public String toString() {
+        return IntStream.of(fRed, fGreen, fBlue, fAlpha)
+                .mapToObj(String::valueOf)
+                .collect(Collectors.joining(", ", "[", "]")); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/FlatUIColors.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/FlatUIColors.java
new file mode 100644 (file)
index 0000000..9f79049
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
+
+/**
+ * Definition of UI colors, from https://flatuicolors.com/ .
+ */
+@SuppressWarnings("javadoc")
+public interface FlatUIColors {
+
+    ColorDefinition TURQUOISE       = new ColorDefinition( 26, 188, 156);
+    ColorDefinition DARK_TURQUOISE  = new ColorDefinition( 22, 160, 133);
+    ColorDefinition GREEN           = new ColorDefinition( 46, 204, 113);
+    ColorDefinition DARK_GREEN      = new ColorDefinition( 39, 174,  96);
+    ColorDefinition BLUE            = new ColorDefinition( 52, 152, 219);
+    ColorDefinition DARK_BLUE       = new ColorDefinition( 41, 128, 185);
+    ColorDefinition PURPLE          = new ColorDefinition(155,  89, 182);
+    ColorDefinition DARK_PURPLE     = new ColorDefinition(142,  68, 173);
+    ColorDefinition BLUE_GRAY       = new ColorDefinition( 52,  73,  94);
+    ColorDefinition DARK_BLUE_GRAY  = new ColorDefinition( 44,  62,  80);
+    ColorDefinition YELLOW          = new ColorDefinition(241, 196,  15);
+    ColorDefinition DARK_YELLOW     = new ColorDefinition(243, 156,  18);
+    ColorDefinition ORANGE          = new ColorDefinition(230, 126,  34);
+    ColorDefinition DARK_ORANGE     = new ColorDefinition(211,  84,   0);
+    ColorDefinition RED             = new ColorDefinition(231,  76,  60);
+    ColorDefinition DARK_RED        = new ColorDefinition(192,  57,  43);
+    ColorDefinition VERY_LIGHT_GRAY = new ColorDefinition(236, 240, 241);
+    ColorDefinition LIGHT_GRAY      = new ColorDefinition(189, 195, 199);
+    ColorDefinition GRAY            = new ColorDefinition(149, 165, 166);
+    ColorDefinition DARK_GRAY       = new ColorDefinition(127, 140, 141);
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/LineThickness.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/LineThickness.java
new file mode 100644 (file)
index 0000000..ea76856
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
+
+/**
+ * Headless definitions of possible values of state interval line thicknesses
+ */
+public enum LineThickness {
+
+    /** Normal, full thickness */
+    NORMAL,
+    /**
+     * Small thickness, should be between {@link #NORMAL} and {@link #TINY} and
+     * distinguishable from those two.
+     */
+    SMALL,
+    /**
+     * Tiny line thickness. The line should be as small as possible but still
+     * have the color distinguishable.
+     */
+    TINY
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/StateDefinition.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/StateDefinition.java
new file mode 100644 (file)
index 0000000..830cf54
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
+
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+
+/**
+ * Class defining the UI reprsentation of a state, as well as default values for
+ * them.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class StateDefinition {
+
+    private final String fName;
+    private final ConfigOption<ColorDefinition> fColor;
+    private final ConfigOption<LineThickness> fLineThickness;
+
+    /**
+     * Build a new state definition. Re-use the same object where the same
+     * definition apply, so that the same configuration property is used.
+     *
+     * @param name
+     *            The name of the definition
+     * @param defaultColor
+     *            The default color to use
+     * @param defaultLineThickness
+     *            The default line thickness to use
+     */
+    public StateDefinition(String name, ColorDefinition defaultColor, LineThickness defaultLineThickness) {
+        fName = name;
+        fColor = new ConfigOption<>(defaultColor);
+        fLineThickness = new ConfigOption<>(defaultLineThickness);
+    }
+
+    /**
+     * Get the name of this definition.
+     *
+     * @return The name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Get the color property of this definition.
+     *
+     * @return The color property
+     */
+    public ConfigOption<ColorDefinition> getColor() {
+        return fColor;
+    }
+
+    /**
+     * Get the line thickness property of this definition.
+     *
+     * @return The line thickness property
+     */
+    public ConfigOption<LineThickness> getLineThickness() {
+        return fLineThickness;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/TimeGraphEvent.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/TimeGraphEvent.java
new file mode 100644 (file)
index 0000000..b552242
--- /dev/null
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+/**
+ * A time graph event is a virtual representation of a location on the time
+ * graph, defined by its timestamp and tree element.
+ *
+ * It does not have a directly corresponding UI representation, but is used as
+ * an intermediate construct in intervals and drawn events, for example.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphEvent {
+
+    private final long fTimestamp;
+    private final TimeGraphTreeElement fTreeElement;
+
+    /**
+     * Constructor
+     *
+     * @param timestamp
+     *            The timestamp of the event
+     * @param treeElement
+     *            The tree element to which this even belong
+     */
+    public TimeGraphEvent(long timestamp, TimeGraphTreeElement treeElement) {
+        fTimestamp = timestamp;
+        fTreeElement = treeElement;
+    }
+
+    /**
+     * Get the timestamp of this event.
+     *
+     * @return The timestamp
+     */
+    public long getTimestamp() {
+        return fTimestamp;
+    }
+
+    /**
+     * Get the tree element of this event.
+     *
+     * @return The tree element
+     */
+    public TimeGraphTreeElement getTreeElement() {
+        return fTreeElement;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fTimestamp, fTreeElement);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TimeGraphEvent other = (TimeGraphEvent) obj;
+        return (fTimestamp == other.fTimestamp
+                && Objects.equals(fTreeElement, other.fTreeElement));
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrow.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrow.java
new file mode 100644 (file)
index 0000000..3601932
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+
+/**
+ * Model representation of a timegraph arrow.
+ *
+ * An arrow links two {@link TimeGraphEvent}s, and has a direction (a start
+ * event and a end event). The two events can belong to different tree elements,
+ * or the same one.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphArrow {
+
+    private final TimeGraphEvent fStartEvent;
+    private final TimeGraphEvent fEndEvent;
+    private final TimeGraphArrowSeries fArrowSeries;
+
+    /**
+     * Constructor
+     *
+     * @param startEvent
+     *            The start event of this arrow
+     * @param endEvent
+     *            The end event of this arrow. The drawn arrowhead should be on
+     *            this side
+     * @param series
+     *            The series to which the arrow belongs. This series contains
+     *            all the styling information.
+     */
+    public TimeGraphArrow(TimeGraphEvent startEvent, TimeGraphEvent endEvent, TimeGraphArrowSeries series) {
+        fStartEvent = startEvent;
+        fEndEvent = endEvent;
+        fArrowSeries = series;
+    }
+
+    /**
+     * Get the start event of this arrow.
+     *
+     * @return The start event
+     */
+    public TimeGraphEvent getStartEvent() {
+        return fStartEvent;
+    }
+
+    /**
+     * Get the end event of this arrow.
+     *
+     * @return The end event
+     */
+    public TimeGraphEvent getEndEvent() {
+        return fEndEvent;
+    }
+
+    /**
+     * Get the series of this arrow.
+     *
+     * @return The arrow series
+     */
+    public TimeGraphArrowSeries getArrowSeries() {
+        return fArrowSeries;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowRender.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowRender.java
new file mode 100644 (file)
index 0000000..e019784
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Render of time graph arrows, containing all possible arrows of a single
+ * series for a given time range.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphArrowRender {
+
+    /** Empty arrow render, can be used instead of a null value */
+    public static final TimeGraphArrowRender EMPTY_RENDER =
+            new TimeGraphArrowRender(TimeRange.of(0, 0), Collections.EMPTY_LIST);
+
+    private final TimeRange fTimeRange;
+    private final Collection<TimeGraphArrow> fArrows;
+
+    /**
+     * Constructor
+     *
+     * @param range
+     *            Time range of this arrow render. For reference only, should
+     *            probably match the time range of the query that created this
+     *            render.
+     * @param arrows
+     *            The arrows contained in this render.
+     */
+    public TimeGraphArrowRender(TimeRange range, Iterable<TimeGraphArrow> arrows) {
+        fTimeRange = range;
+        fArrows = ImmutableList.copyOf(arrows);
+    }
+
+    /**
+     * Get the time range of this arrow render.
+     *
+     * @return The time range
+     */
+    public TimeRange getTimeRange() {
+        return fTimeRange;
+    }
+
+    /**
+     * Get the arrows contained in this render.
+     *
+     * @return The arrows
+     */
+    public Collection<TimeGraphArrow> getArrows() {
+        return fArrows;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowSeries.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/TimeGraphArrowSeries.java
new file mode 100644 (file)
index 0000000..951bc1c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+
+/**
+ * Definition of a time graph arrow series.
+ *
+ * It contains all the styling information (name, color, line type, etc.) for
+ * the arrows part of this series. It does not however contain the arrows
+ * themselves, it is just the definition. The arrows will keep reference of what
+ * series they belong to.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphArrowSeries {
+
+    /**
+     * Headless definition of the style of the arrow line.
+     */
+    public enum LineStyle {
+        /** Full line */
+        FULL,
+        /** Line composed of dotsf */
+        DOTTED,
+        /** Line composed of dashes */
+        DASHED;
+    }
+
+    private final String fSeriesName;
+    private final ColorDefinition fColor;
+    private final LineStyle fLineStyle;
+
+    /**
+     * Constructor
+     *
+     * @param seriesName
+     *            Name of this series
+     * @param color
+     *            Suggested color for arrows of this series
+     * @param lineStyle
+     *            Suggested line style for arrows of this series
+     */
+    public TimeGraphArrowSeries(String seriesName, ColorDefinition color, LineStyle lineStyle) {
+        fSeriesName = seriesName;
+        fColor = color;
+        fLineStyle = lineStyle;
+    }
+
+    /**
+     * Get the name of this series.
+     *
+     * @return The series's name
+     */
+    public String getSeriesName() {
+        return fSeriesName;
+    }
+
+    /**
+     * Get the suggested color of this series.
+     *
+     * @return The series's color
+     */
+    public ColorDefinition getColor() {
+        return fColor;
+    }
+
+    /**
+     * Get the suggested line style of this series.
+     *
+     * @return The series's line style
+     */
+    public LineStyle getLineStyle() {
+        return fLineStyle;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/arrows/package-info.java
new file mode 100644 (file)
index 0000000..7ac3a8f
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEvent.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEvent.java
new file mode 100644 (file)
index 0000000..ffd3c46
--- /dev/null
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+
+/**
+ * Model representation of a drawn event.
+ *
+ * A drawn event is a UI representation of a single {@link TimeGraphEvent}, with
+ * a given style.
+ *
+ * Additional properties can also be part of the drawn event, for example to be
+ * shown on mouse-over.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphDrawnEvent {
+
+    private final TimeGraphEvent fTimeGraphEvent;
+    private final TimeGraphDrawnEventSeries fEventSeries;
+    private final @Nullable Supplier<Map<String, String>> fPropertySupplier;
+
+    /**
+     * Constructor
+     *
+     * @param event
+     *            Time graph event (location)
+     * @param eventSeries
+     *            Series of this event, which contains all the styling
+     *            information
+     * @param propertySupplier
+     *            Supplier of additional properties, which can be accessed only
+     *            the first time {@link #getProperties()} is called.
+     */
+    public TimeGraphDrawnEvent(TimeGraphEvent event,
+            TimeGraphDrawnEventSeries eventSeries,
+            @Nullable Supplier<Map<String, String>> propertySupplier) {
+        fTimeGraphEvent = event;
+        fEventSeries = eventSeries;
+        fPropertySupplier = propertySupplier;
+    }
+
+    /**
+     * Get the timegraph event wrapped by this drawn event.
+     *
+     * @return The time graph event
+     */
+    public TimeGraphEvent getEvent() {
+        return fTimeGraphEvent;
+    }
+
+    /**
+     * Get the event series of this drawn event.
+     *
+     * @return The event's series
+     */
+    public TimeGraphDrawnEventSeries getEventSeries() {
+        return fEventSeries;
+    }
+
+    /**
+     * Get the additional properties of this drawn event.
+     *
+     * @return The event's properties
+     */
+    public Map<String, String> getProperties() {
+        Supplier<Map<String, String>> supplier = fPropertySupplier;
+        if (supplier == null) {
+            return Collections.EMPTY_MAP;
+        }
+        return supplier.get();
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventRender.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventRender.java
new file mode 100644 (file)
index 0000000..afa3515
--- /dev/null
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents;
+
+import java.util.Collection;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Render of time graph drawn events. It can cover several (usually all) tree
+ * elements, unlike the TimeGraphStateRender which covers only a single element.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphDrawnEventRender {
+
+    private final TimeRange fTimeRange;
+    private final Collection<TimeGraphDrawnEvent> fEvents;
+
+    /**
+     * Constructor
+     *
+     * @param timeRange
+     *            The time range of this draw event render. Should usually match
+     *            the time range of the query that created this render.
+     * @param events
+     *            The drawn events contained in this render
+     */
+    public TimeGraphDrawnEventRender(TimeRange timeRange,
+            Iterable<TimeGraphDrawnEvent> events) {
+
+        fTimeRange = timeRange;
+        fEvents = ImmutableList.copyOf(events);
+    }
+
+    /**
+     * Get the time range of this render
+     *
+     * @return The time range
+     */
+    public TimeRange getTimeRange() {
+        return fTimeRange;
+    }
+
+    /**
+     * Get the drawn events that are part of this render.
+     *
+     * @return The drawn events
+     */
+    public Collection<TimeGraphDrawnEvent> getEvents() {
+        return fEvents;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventSeries.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/TimeGraphDrawnEventSeries.java
new file mode 100644 (file)
index 0000000..c5c46f1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents;
+
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+
+/**
+ * Definition of a time graph arrow series.
+ *
+ * It contains all the styling information (symbols, color, etc.) for the events
+ * that are part of this series. It does not contain the events themselves, the
+ * events will keep a reference to their series instead.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphDrawnEventSeries {
+
+    /**
+     * Headless symbol style definitions
+     */
+    public enum SymbolStyle {
+        /** Circle */
+        CIRCLE,
+        /** Cross */
+        CROSS,
+        /** Star */
+        STAR,
+        /** Square */
+        SQUARE,
+        /** Diamond */
+        DIAMOND,
+        /** Triangle */
+        TRIANGLE;
+    }
+
+    private final String fSeriesName;
+    private final ConfigOption<ColorDefinition> fColor;
+    private final ConfigOption<SymbolStyle> fSymbolStyle;
+
+    /**
+     * Constructor.
+     *
+     * The parameters passed as {@link ConfigOption}s can be modified at
+     * runtime.
+     *
+     * @param seriesName
+     *            Name of this event series
+     * @param color
+     *            Color for this event series.
+     * @param symbolStyle
+     *            Symbol style for this series
+     */
+    public TimeGraphDrawnEventSeries(String seriesName,
+            ConfigOption<ColorDefinition> color,
+            ConfigOption<SymbolStyle> symbolStyle) {
+
+        fSeriesName = seriesName;
+        fColor = color;
+        fSymbolStyle = symbolStyle;
+    }
+
+    /**
+     * Get the name of this series
+     *
+     * @return This series's name
+     */
+    public String getSeriesName() {
+        return fSeriesName;
+    }
+
+    /**
+     * Get the configurable color of this series.
+     *
+     * @return This series's color
+     */
+    public ConfigOption<ColorDefinition> getColor() {
+        return fColor;
+    }
+
+    /**
+     * Get the configurable symbol style of this series.
+     *
+     * @return This series's symbol style
+     */
+    public ConfigOption<SymbolStyle> getSymbolStyle() {
+        return fSymbolStyle;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/drawnevents/package-info.java
new file mode 100644 (file)
index 0000000..772894a
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/package-info.java
new file mode 100644 (file)
index 0000000..9006109
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.render;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/BasicTimeGraphStateInterval.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/BasicTimeGraphStateInterval.java
new file mode 100644 (file)
index 0000000..82bdac4
--- /dev/null
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.states;
+
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * Basic implementation of {@link TimeGraphStateInterval}.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class BasicTimeGraphStateInterval implements TimeGraphStateInterval {
+
+    private final TimeGraphEvent fStartEvent;
+    private final TimeGraphEvent fEndEvent;
+
+    private final String fStateName;
+    private final @Nullable String fLabel;
+    private final ConfigOption<ColorDefinition> fColor;
+    private final ConfigOption<LineThickness> fLineThickness;
+
+    private final Map<String, String> fProperties;
+
+    /**
+     * Constructor.
+     *
+     * It requires supplying the start time, end time, and tree element. The
+     * corresponding {@link TimeGraphEvent} will be created from those.
+     *
+     * @param start
+     *            Start time
+     * @param end
+     *            End time
+     * @param treeElement
+     *            Tree element of this interval
+     * @param stateName
+     *            State name
+     * @param label
+     *            Label, see {@link #getLabel()}
+     * @param color
+     *            Color
+     * @param lineThickness
+     *            Line thickness
+     * @param properties
+     *            Properties
+     */
+    public BasicTimeGraphStateInterval(long start,
+            long end,
+            TimeGraphTreeElement treeElement,
+            String stateName,
+            @Nullable String label,
+            ConfigOption<ColorDefinition> color,
+            ConfigOption<LineThickness> lineThickness,
+            Map<String, String> properties) {
+
+        if (start > end || start < 0 || end < 0) {
+            throw new IllegalArgumentException();
+        }
+
+        fStartEvent = new TimeGraphEvent(start, treeElement);
+        fEndEvent = new TimeGraphEvent(end, treeElement);
+
+        fStateName = stateName;
+        fLabel = label;
+        fColor = color;
+        fLineThickness = lineThickness;
+        fProperties = properties;
+    }
+
+    @Override
+    public TimeGraphEvent getStartEvent() {
+        return fStartEvent;
+    }
+
+    @Override
+    public TimeGraphEvent getEndEvent() {
+        return fEndEvent;
+    }
+
+    @Override
+    public String getStateName() {
+        return fStateName;
+    }
+
+    @Override
+    public @Nullable String getLabel() {
+        return fLabel;
+    }
+
+    @Override
+    public ConfigOption<ColorDefinition> getColorDefinition() {
+        return fColor;
+    }
+
+    @Override
+    public ConfigOption<LineThickness> getLineThickness() {
+        return fLineThickness;
+    }
+
+    @Override
+    public boolean isMultiState() {
+        return false;
+    }
+
+    @Override
+    public Map<String, String> getProperties() {
+        return fProperties;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fStartEvent, fEndEvent, fStateName, fLabel, fColor, fLineThickness);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        BasicTimeGraphStateInterval other = (BasicTimeGraphStateInterval) obj;
+        return (Objects.equals(fStartEvent, other.fStartEvent)
+                && Objects.equals(fEndEvent, other.fEndEvent)
+                && Objects.equals(fStateName, other.fStateName)
+                && Objects.equals(fLabel, other.fLabel)
+                && Objects.equals(fColor, other.fColor)
+                && fLineThickness == other.fLineThickness);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("start", fStartEvent.getTimestamp()) //$NON-NLS-1$
+                .add("end", fEndEvent.getTimestamp()) //$NON-NLS-1$
+                .add("name", fStateName) //$NON-NLS-1$
+                .toString();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/MultiStateInterval.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/MultiStateInterval.java
new file mode 100644 (file)
index 0000000..4610fc0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.states;
+
+import java.util.Collections;
+
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+/**
+ * Dummy interval model object representing a "multi-state", which means a case
+ * where more than one state exists for a given pixel.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class MultiStateInterval extends BasicTimeGraphStateInterval {
+
+    /** Configuration option for the line thickness of multi-state intervals */
+    public static final ConfigOption<LineThickness> MULTI_STATE_THICKNESS_OPTION = new ConfigOption<>(LineThickness.NORMAL);
+
+    private static final String MULTI_STATE_NAME = "Multi-state"; //$NON-NLS-1$
+    private static final ColorDefinition MULTI_STATE_COLOR = new ColorDefinition(0, 0, 0);
+    private static final ConfigOption<ColorDefinition> MULTI_STATE_COLOR_OPTION = new ConfigOption<>(MULTI_STATE_COLOR);
+
+    /**
+     * Constructor
+     *
+     * @param startTime
+     *            Start time
+     * @param endTime
+     *            End time
+     * @param treeElement
+     *            The tree element to which this interval is associated
+     */
+    public MultiStateInterval(long startTime, long endTime, TimeGraphTreeElement treeElement) {
+        super(startTime,
+                endTime,
+                treeElement,
+                MULTI_STATE_NAME,
+                /* Label */
+                null,
+                MULTI_STATE_COLOR_OPTION,
+                MULTI_STATE_THICKNESS_OPTION,
+                Collections.emptyMap());
+    }
+
+    @Override
+    public boolean isMultiState() {
+        return true;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateInterval.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateInterval.java
new file mode 100644 (file)
index 0000000..dccb595
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.states;
+
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+/**
+ * Base interface for time graph state interval, which is defined by a start
+ * time, end time, and a state.
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface TimeGraphStateInterval {
+
+    /**
+     * Return the start event of this interval.
+     *
+     * @return The start event
+     */
+    TimeGraphEvent getStartEvent();
+
+    /**
+     * Return the end event of this interval.
+     *
+     * @return The end event
+     */
+    TimeGraphEvent getEndEvent();
+
+    /**
+     * Get the name of the state represented by this interval.
+     *
+     * @return The state name
+     */
+    String getStateName();
+
+    /**
+     * Get the label of this interval. This means the text that is meant to be
+     * displayed alongside the interval's color. It may or may not be the same
+     * as the {@link #getStateName()}.
+     *
+     * @return The optional label of this interval
+     */
+    @Nullable String getLabel();
+
+    /**
+     * Get the color suggested for this interval.
+     *
+     * @return The color of this interval
+     */
+    ConfigOption<ColorDefinition> getColorDefinition();
+
+    /**
+     * Get the suggested line thickness of this interval.
+     *
+     * @return The line thickness
+     */
+    ConfigOption<LineThickness> getLineThickness();
+
+    /**
+     * Indicate if this interval represents multiple states, or a single one.
+     *
+     * @return If this interval is a multi-state one
+     */
+    boolean isMultiState();
+
+    /**
+     * Get the properties associated with this state interval. This is extra,
+     * generic data that can be attached to the interval. Views can display
+     * those in a tooltip, for example.
+     *
+     * @return The additional properties.
+     */
+    Map<String, String> getProperties();
+
+    /**
+     * Get the start time of this interval, which is effectively the timestamp
+     * of the start event.
+     *
+     * @return The start time
+     */
+    default long getStartTime() {
+        return getStartEvent().getTimestamp();
+    }
+
+    /**
+     * Get the end time of this interval, which is effectively the timestamp of
+     * the end event.
+     *
+     * @return The end time
+     */
+    default long getEndTime() {
+        return getEndEvent().getTimestamp();
+    }
+
+    /**
+     * Get the duration of this interval, effectively end time minus start time.
+     *
+     * @return The duration
+     */
+    default long getDuration() {
+        return (getEndTime() - getStartTime());
+    }
+
+    /**
+     * Get the tree element of this interval's start and end events.
+     *
+     * @return The tree element
+     */
+    default TimeGraphTreeElement getTreeElement() {
+        return getStartEvent().getTreeElement();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateRender.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/TimeGraphStateRender.java
new file mode 100644 (file)
index 0000000..bd17413
--- /dev/null
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.states;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * "Segment" of a time graph, representing the states of a single tree element
+ * for a given time range.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphStateRender {
+
+    /** Non-null reference to a dummy/empty render */
+    public static final TimeGraphStateRender EMPTY_RENDER = new TimeGraphStateRender(TimeRange.of(0, 0), TimeGraphTreeElement.DUMMY_ELEMENT, Collections.emptyList());
+
+    private final TimeRange fTimeRange;
+    private final TimeGraphTreeElement fTreeElement;
+    private final List<TimeGraphStateInterval> fStateIntervals;
+
+    /**
+     * Constructor
+     *
+     * @param timeRange
+     *            The time range of this state render. This is for informative
+     *            use mostly, and usually matches the time range that was
+     *            requested for the query that generated this render.
+     * @param treeElement
+     *            This render contains state intervals of this single tree
+     *            element
+     * @param stateIntervals
+     *            The state intervals that are part of this render. Their range
+     *            should normally match the 'timeRange' parameter.
+     */
+    public TimeGraphStateRender(TimeRange timeRange,
+            TimeGraphTreeElement treeElement,
+            List<TimeGraphStateInterval> stateIntervals) {
+
+        fTimeRange = timeRange;
+        fTreeElement = treeElement;
+        fStateIntervals = ImmutableList.copyOf(stateIntervals);
+    }
+
+    /**
+     * Get the time range of this interval.
+     *
+     * @return The time range
+     */
+    public TimeRange getTimeRange() {
+        return fTimeRange;
+    }
+
+    /**
+     * Get the tree element to which the intervals of this render belongs.
+     *
+     * @return The tree element
+     */
+    public TimeGraphTreeElement getTreeElement() {
+        return fTreeElement;
+    }
+
+    /**
+     * Get the state intervals that are part of this state render
+     *
+     * @return The state intervals
+     */
+    public List<TimeGraphStateInterval> getStateIntervals() {
+        return fStateIntervals;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/states/package-info.java
new file mode 100644 (file)
index 0000000..945c684
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.states;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeElement.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeElement.java
new file mode 100644 (file)
index 0000000..1488135
--- /dev/null
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.tree;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * The "tree element" is the unit of a timegraph represented by a single line.
+ * State intervals are aligned with tree elements to represent the states of the
+ * attribute represented by its tree element.
+ *
+ * Tree elements can have children, which allows representing them as a tree
+ * structure. At the visualization layer, sub-trees could be allowed to be
+ * expanded/collapsed, which can then change the number of visible tree elements
+ * in the timegraph.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphTreeElement {
+
+    /** Non-null reference to a dummy element */
+    public static final TimeGraphTreeElement DUMMY_ELEMENT = new TimeGraphTreeElement("Dummy", Collections.emptyList()); //$NON-NLS-1$
+
+    private final String fName;
+    private final List<TimeGraphTreeElement> fChildElements;
+
+    /**
+     * Constructor, build a tree element by specifying its name and children
+     * elements.
+     *
+     * @param name
+     *            The name this tree element should have.
+     * @param children
+     *            The children tree elements. You can pass an empty list for no
+     *            children.
+     */
+    public TimeGraphTreeElement(String name, List<TimeGraphTreeElement> children) {
+        fName = name;
+        fChildElements = ImmutableList.copyOf(children);
+    }
+
+    /**
+     * Get the name of this tree element.
+     *
+     * @return The element's name
+     */
+    public String getName() {
+        return fName;
+    }
+
+    /**
+     * Get the child elements of this tree element.
+     *
+     * @return The child elements
+     */
+    public List<TimeGraphTreeElement> getChildElements() {
+        return fChildElements;
+    }
+
+    /**
+     * Determine if and how this tree element corresponds to a component of a
+     * trace event.
+     *
+     * For example, if this tree element represents "CPU #2", then the predicate
+     * should return true for all trace events belonging to CPU #2.
+     *
+     * The method returns null if this tree element does not correspond to a
+     * particular aspect of trace events.
+     *
+     * @return The event matching predicate, if there is one
+     */
+    public @Nullable Predicate<ITmfEvent> getEventMatching() {
+        /* Sub-classes can override */
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fName, fChildElements);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TimeGraphTreeElement other = (TimeGraphTreeElement) obj;
+        return Objects.equals(fName, other.fName)
+                && Objects.equals(fChildElements, other.fChildElements);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("fName", fName) //$NON-NLS-1$
+            .add("fChildElements", fChildElements.toString()) //$NON-NLS-1$
+            .toString();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeRender.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/TimeGraphTreeRender.java
new file mode 100644 (file)
index 0000000..4be4991
--- /dev/null
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.tree;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.common.core.StreamUtils.StreamFlattener;
+
+/**
+ * Render of a tree of the timegraph. Contains the tree elements that compose
+ * the current tree.
+ *
+ * In a timegraph, the "tree" part is usually shown on the left-hand side, and
+ * lists the tree elements, which represent attributes of a model. A tree render
+ * is a "snapshot" of this tree that is valid for a given timestamp or
+ * timerange.
+ *
+ * Some timegraphs may use a tree that is valid for the whole time range of a
+ * trace. Other timegraphs may display a different tree for different parts of
+ * the trace.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphTreeRender {
+
+    /**
+     * A static reference to an empty render, which can be used to represent an
+     * uninitialized state for example (by comparing with ==).
+     */
+    public static final TimeGraphTreeRender EMPTY_RENDER = new TimeGraphTreeRender(TimeGraphTreeElement.DUMMY_ELEMENT);
+
+    private final TimeGraphTreeElement fRootElement;
+
+    /**
+     * Constructor
+     *
+     * @param rootElement
+     *            The root element of the tree
+     */
+    public TimeGraphTreeRender(TimeGraphTreeElement rootElement) {
+        fRootElement = rootElement;
+    }
+
+    /**
+     * Return the root element of this tree.
+     *
+     * @return The root element
+     */
+    public TimeGraphTreeElement getRootElement() {
+        return fRootElement;
+    }
+
+    /**
+     * Get a flattened view of all the tree elements in this render.
+     *
+     * This should also contains all the child elements that are also contained
+     * in each element's {@link TimeGraphTreeElement#getChildElements()}. It can
+     * be used to run an action on all elements of a render.
+     *
+     * @return A list of all the tree elements
+     */
+    public List<TimeGraphTreeElement> getAllTreeElements() {
+        StreamFlattener<TimeGraphTreeElement> flattener = new StreamFlattener<>(i -> i.getChildElements().stream());
+        return flattener.flatten(getRootElement()).collect(Collectors.toList());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fRootElement);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TimeGraphTreeRender other = (TimeGraphTreeRender) obj;
+        return (Objects.equals(fRootElement, other.fRootElement));
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/model/render/tree/package-info.java
new file mode 100644 (file)
index 0000000..16722c7
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.model.render.tree;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/TimeGraphModelView.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/TimeGraphModelView.java
new file mode 100644 (file)
index 0000000..f8c36dc
--- /dev/null
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.core.timegraph.view;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.core.timegraph.control.TimeGraphModelControl;
+
+/**
+ * Base class for time graph view objects.
+ *
+ * A view is attached to a {@link TimeGraphModelControl} (passed at the
+ * constructor), which will then call the view's method accordingly to
+ * reposition, repaint, etc. the timegraph according to actions taken elsewhere
+ * in the framework.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphModelView {
+
+    private final TimeGraphModelControl fControl;
+
+    /**
+     * Constructor. Build a new view by specifying its corresponding control.
+     * You will most probably want to call
+     * {@link TimeGraphModelControl#attachView} afterwards, this is not done
+     * automatically!
+     *
+     * @param control
+     *            The control that will manage this view
+     */
+    public TimeGraphModelView(TimeGraphModelControl control) {
+        fControl = control;
+    }
+
+    /**
+     * Retrieve the control managing this view.
+     *
+     * @return The model control
+     */
+    public final TimeGraphModelControl getControl() {
+        return fControl;
+    }
+
+    /**
+     * Dispose this view
+     */
+    public final void dispose() {
+        disposeImpl();
+    }
+
+    /**
+     * Get the view context of this view/control.
+     *
+     * @return The current view context
+     */
+    public final ViewGroupContext getViewContext() {
+        return getControl().getViewContext();
+    }
+
+    // ------------------------------------------------------------------------
+    // Template methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Abstract implementation of a dispose method. Sub-classes should clean up
+     * the state they add to the base view class.
+     */
+    protected abstract void disposeImpl();
+
+    /**
+     * Request the timegraph to be completely cleared of its contents, for
+     * example when no trace is opened at all.
+     */
+    public abstract void clear();
+
+    /**
+     * This should be called whenever the visible window moves, including zoom
+     * level changes.
+     *
+     * @param newVisibleRange
+     *            The range to where the view should be seeked
+     */
+    public abstract void seekVisibleRange(TimeRange newVisibleRange);
+
+    /**
+     * Draw a new selection rectangle. The previous one, if any, will be
+     * removed.
+     *
+     * @param selectionRange
+     *            The selection that should be drawn
+     */
+    public abstract void drawSelection(TimeRange selectionRange);
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/RenderToJson.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/RenderToJson.java
new file mode 100644 (file)
index 0000000..e803824
--- /dev/null
@@ -0,0 +1,97 @@
+package org.lttng.scope.tmf2.views.core.timegraph.view.json;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+
+import com.google.common.base.Charsets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+final class RenderToJson {
+
+    private RenderToJson() {}
+
+    private static final boolean PRETTY_PRINT = true;
+    private static final int VERSION = 1;
+
+    private static final String VERSION_KEY = "version"; //$NON-NLS-1$
+    private static final String INTERVALS_KEY = "intervals"; //$NON-NLS-1$
+
+    private static final String TREE_ELEMENT_KEY = "elem"; //$NON-NLS-1$
+    private static final String START_TIME_KEY = "start"; //$NON-NLS-1$
+    private static final String END_TIME_KEY = "end"; //$NON-NLS-1$
+    private static final String STATE_NAME_KEY = "state"; //$NON-NLS-1$
+    private static final String COLOR_KEY = "color"; //$NON-NLS-1$
+
+    private static final Path OUTPUT_FILE = Paths.get("/home/alexandre/json-output"); //$NON-NLS-1$
+    static {
+        try {
+            if (Files.exists(OUTPUT_FILE)) {
+                Files.delete(OUTPUT_FILE);
+            }
+            Files.createFile(OUTPUT_FILE);
+        } catch (IOException e) {
+        }
+    }
+
+    private static final Gson GSON;
+    static {
+        if (PRETTY_PRINT) {
+            GSON = new GsonBuilder().setPrettyPrinting().create();
+        } else {
+            GSON = new Gson();
+        }
+    }
+
+    public static void printRenderTo1(List<TimeGraphStateRender> renders) {
+        String json = GSON.toJson(renders);
+        try (Writer bw = Files.newBufferedWriter(OUTPUT_FILE, Charsets.UTF_8)) {
+            bw.write(json);
+            bw.flush();
+        } catch (IOException e1) {
+        }
+    }
+
+    public static void printRenderTo(List<TimeGraphStateRender> renders) {
+        try (Writer bw = Files.newBufferedWriter(OUTPUT_FILE, Charsets.UTF_8)) {
+            JSONObject root = new JSONObject();
+            root.put(VERSION_KEY, VERSION);
+
+            JSONArray intervalsRoot = new JSONArray();
+            root.put(INTERVALS_KEY, intervalsRoot);
+
+            renders.stream()
+                    .flatMap(render -> render.getStateIntervals().stream())
+                    .forEach(interval -> {
+                        try {
+                            JSONObject intervalObject = new JSONObject();
+                            intervalObject.put(TREE_ELEMENT_KEY, interval.getStartEvent().getTreeElement().getName());
+                            intervalObject.put(START_TIME_KEY, interval.getStartEvent().getTimestamp());
+                            intervalObject.put(END_TIME_KEY, interval.getEndEvent().getTimestamp());
+                            intervalObject.put(STATE_NAME_KEY, interval.getStateName());
+                            intervalObject.put(COLOR_KEY, interval.getColorDefinition().toString());
+
+                            intervalsRoot.put(intervalObject);
+                        } catch (JSONException e) {
+                            /* Skip this interval */
+                        }
+                    });
+
+            String json = (PRETTY_PRINT ? root.toString(1) : root.toString());
+            bw.write(json);
+            bw.flush();
+
+        } catch (JSONException | IOException e) {
+        }
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/TimeGraphJsonOutput.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/TimeGraphJsonOutput.java
new file mode 100644 (file)
index 0000000..51c02c4
--- /dev/null
@@ -0,0 +1,44 @@
+package org.lttng.scope.tmf2.views.core.timegraph.view.json;
+
+import java.util.List;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.control.TimeGraphModelControl;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.core.timegraph.view.TimeGraphModelView;
+
+public class TimeGraphJsonOutput extends TimeGraphModelView {
+
+    public TimeGraphJsonOutput(TimeGraphModelControl control) {
+        super(control);
+    }
+
+    @Override
+    public void disposeImpl() {
+    }
+
+    @Override
+    public void clear() {
+        // TODO
+    }
+
+    @Override
+    public void seekVisibleRange(TimeRange newVisibleRange) {
+        /* Generate JSON for the visible area */
+        ITimeGraphModelProvider provider = getControl().getModelRenderProvider();
+
+        TimeGraphTreeRender treeRender = provider.getTreeRender();
+        List<TimeGraphStateRender> stateRenders = provider.getStateProvider().getStateRenders(treeRender,
+                newVisibleRange, 1, null);
+
+        RenderToJson.printRenderTo(stateRenders);
+    }
+
+    @Override
+    public void drawSelection(TimeRange selectionRange) {
+        // TODO NYI
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/json/package-info.java
new file mode 100644 (file)
index 0000000..babd29a
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.view.json;
diff --git a/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/package-info.java b/tmf/org.lttng.scope.tmf2.views.core/src/org/lttng/scope/tmf2/views/core/timegraph/view/package-info.java
new file mode 100644 (file)
index 0000000..278940d
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.core.timegraph.view;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.classpath b/tmf/org.lttng.scope.tmf2.views.ui/.classpath
new file mode 100644 (file)
index 0000000..0be7b35
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+               <attributes>
+                       <attribute name="annotationpath" value="/org.lttng.scope.common.core/annotations"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+               <attributes>
+                       <attribute name="annotationpath" value="/org.lttng.scope.common.core/annotations"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.project b/tmf/org.lttng.scope.tmf2.views.ui/.project
new file mode 100644 (file)
index 0000000..f37a151
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>org.lttng.scope.tmf2.views.ui</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ManifestBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.SchemaBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+       </natures>
+</projectDescription>
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.resources.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..99f26c0
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.runtime.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.core.runtime.prefs
new file mode 100644 (file)
index 0000000..5a0ad22
--- /dev/null
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.core.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..bf5f566
--- /dev/null
@@ -0,0 +1,436 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.codeComplete.argumentPrefixes=
+org.eclipse.jdt.core.codeComplete.argumentSuffixes=
+org.eclipse.jdt.core.codeComplete.fieldPrefixes=f
+org.eclipse.jdt.core.codeComplete.fieldSuffixes=
+org.eclipse.jdt.core.codeComplete.localPrefixes=
+org.eclipse.jdt.core.codeComplete.localSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
+org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
+org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
+org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=info
+org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
+org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
+org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
+org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
+org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.doc.comment.support=enabled
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=error
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.comparingIdentical=error
+org.eclipse.jdt.core.compiler.problem.deadCode=error
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=error
+org.eclipse.jdt.core.compiler.problem.fallthroughCase=error
+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
+org.eclipse.jdt.core.compiler.problem.fieldHiding=error
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=error
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=error
+org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=error
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=error
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
+org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=error
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=error
+org.eclipse.jdt.core.compiler.problem.missingDefaultCase=error
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=enabled
+org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=error
+org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags
+org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=protected
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=error
+org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning
+org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
+org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=info
+org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning
+org.eclipse.jdt.core.compiler.problem.nullReference=error
+org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
+org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=error
+org.eclipse.jdt.core.compiler.problem.parameterAssignment=error
+org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error
+org.eclipse.jdt.core.compiler.problem.potentialNullReference=error
+org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning
+org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
+org.eclipse.jdt.core.compiler.problem.redundantNullCheck=error
+org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=error
+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=error
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
+org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=error
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=error
+org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=disabled
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.unclosedCloseable=error
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=error
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
+org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
+org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=error
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=disabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=disabled
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLabel=error
+org.eclipse.jdt.core.compiler.problem.unusedLocal=error
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
+org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=warning
+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error
+org.eclipse.jdt.core.compiler.source=1.8
+org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
+org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=false
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=250
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
+org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.ui.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644 (file)
index 0000000..2591ac9
--- /dev/null
@@ -0,0 +1,65 @@
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_tmf-style
+formatter_settings_version=12
+org.eclipse.jdt.ui.exception.name=e
+org.eclipse.jdt.ui.gettersetter.use.is=true
+org.eclipse.jdt.ui.keywordthis=false
+org.eclipse.jdt.ui.overrideannotation=true
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_functional_interfaces=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.insert_inferred_type_arguments=false
+sp_cleanup.make_local_variable_final=false
+sp_cleanup.make_parameters_final=false
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=true
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_redundant_type_arguments=false
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_anonymous_class_creation=false
+sp_cleanup.use_blocks=true
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_lambda=true
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.api.tools.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644 (file)
index 0000000..ce92bf3
--- /dev/null
@@ -0,0 +1,102 @@
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_DEFAULT_METHOD=Ignore
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_ANNOTATION=Warning
+INVALID_JAVADOC_TAG=Warning
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Warning
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Ignore
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+changed_execution_env=Error
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+incompatible_api_component_version_report_major_without_breaking_change=Warning
+incompatible_api_component_version_report_minor_without_api_change=Warning
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.prefs b/tmf/org.lttng.scope.tmf2.views.ui/.settings/org.eclipse.pde.prefs
new file mode 100644 (file)
index 0000000..01d624d
--- /dev/null
@@ -0,0 +1,32 @@
+compilers.f.unresolved-features=1
+compilers.f.unresolved-plugins=1
+compilers.incompatible-environment=1
+compilers.p.build=1
+compilers.p.build.bin.includes=0
+compilers.p.build.encodings=2
+compilers.p.build.java.compiler=2
+compilers.p.build.java.compliance=1
+compilers.p.build.missing.output=2
+compilers.p.build.output.library=1
+compilers.p.build.source.library=0
+compilers.p.build.src.includes=0
+compilers.p.deprecated=1
+compilers.p.discouraged-class=1
+compilers.p.internal=1
+compilers.p.missing-packages=1
+compilers.p.missing-version-export-package=2
+compilers.p.missing-version-import-package=2
+compilers.p.missing-version-require-bundle=2
+compilers.p.no-required-att=0
+compilers.p.not-externalized-att=1
+compilers.p.unknown-attribute=1
+compilers.p.unknown-class=1
+compilers.p.unknown-element=1
+compilers.p.unknown-identifier=1
+compilers.p.unknown-resource=1
+compilers.p.unresolved-ex-points=0
+compilers.p.unresolved-import=0
+compilers.s.create-docs=false
+compilers.s.doc-folder=doc
+compilers.s.open-tags=1
+eclipse.preferences.version=1
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/META-INF/MANIFEST.MF b/tmf/org.lttng.scope.tmf2.views.ui/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..4503ded
--- /dev/null
@@ -0,0 +1,29 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %Bundle-Name
+Bundle-Vendor: %Bundle-Vendor
+Bundle-Version: 0.2.0.qualifier
+Bundle-Localization: plugin
+Bundle-SymbolicName: org.lttng.scope.tmf2.views.ui;singleton:=true
+Bundle-Activator: org.lttng.scope.tmf2.views.ui.activator.internal.Activator
+Bundle-ActivationPolicy: lazy
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.ui,
+ org.lttng.scope.common.core,
+ org.lttng.scope.common.ui,
+ org.lttng.scope.tmf2.views.core,
+ org.eclipse.tracecompass.tmf.core,
+ org.eclipse.tracecompass.tmf.ui,
+ org.eclipse.core.resources
+Export-Package: org.lttng.scope.tmf2.views.ui.activator.internal;x-internal:=true,
+ org.lttng.scope.tmf2.views.ui.jfx,
+ org.lttng.scope.tmf2.views.ui.jfx.examples,
+ org.lttng.scope.tmf2.views.ui.timeline,
+ org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph,
+ org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar,
+ org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav
+Import-Package: com.google.common.annotations,
+ com.google.common.base,
+ com.google.common.collect,
+ com.google.common.primitives
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/about.html b/tmf/org.lttng.scope.tmf2.views.ui/about.html
new file mode 100644 (file)
index 0000000..c258ef5
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
+<title>About</title>
+</head>
+<body lang="EN-US">
+<h2>About This Content</h2>
+<p>June 5, 2006</p>    
+<h3>License</h3>
+
+<p>The Eclipse Foundation makes available all content in this plug-in (&quot;Content&quot;).  Unless otherwise 
+indicated below, the Content is provided to you under the terms and conditions of the
+Eclipse Public License Version 1.0 (&quot;EPL&quot;).  A copy of the EPL is available 
+at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
+For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
+
+<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is 
+being redistributed by another party (&quot;Redistributor&quot;) and different terms and conditions may
+apply to your use of any object code in the Content.  Check the Redistributor's license that was 
+provided with the Content.  If no such license exists, contact the Redistributor.  Unless otherwise
+indicated below, the terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/build.properties b/tmf/org.lttng.scope.tmf2.views.ui/build.properties
new file mode 100644 (file)
index 0000000..4e8d15e
--- /dev/null
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               icons/,\
+               .,\
+               about.html,\
+               plugin.properties,\
+               plugin.xml
+src.includes = about.html
+additional.bundles = org.eclipse.jdt.annotation
+jars.extra.classpath = platform:/plugin/org.eclipse.jdt.annotation
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/config.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/config.gif
new file mode 100755 (executable)
index 0000000..cade785
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/config.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/help.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/help.gif
new file mode 100644 (file)
index 0000000..9d70301
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/help.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/legend.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/legend.gif
new file mode 100644 (file)
index 0000000..f40ce6b
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/legend.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_back.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_back.gif
new file mode 100644 (file)
index 0000000..04b5a05
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_back.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_fwd.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_fwd.gif
new file mode 100644 (file)
index 0000000..b8aa056
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_arrow_fwd.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_back.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_back.gif
new file mode 100644 (file)
index 0000000..ed3e7c2
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_back.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_fwd.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_fwd.gif
new file mode 100644 (file)
index 0000000..da6c4b6
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_bookmark_fwd.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_back.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_back.gif
new file mode 100644 (file)
index 0000000..328e97e
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_back.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_fwd.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_fwd.gif
new file mode 100644 (file)
index 0000000..c39cf62
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_event_fwd.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_back.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_back.gif
new file mode 100644 (file)
index 0000000..69c57a9
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_back.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_fwd.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_fwd.gif
new file mode 100644 (file)
index 0000000..c5371ff
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/nav_statechange_fwd.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_full.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_full.gif
new file mode 100644 (file)
index 0000000..fd0c669
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_full.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_in.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_in.gif
new file mode 100644 (file)
index 0000000..31441e3
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_in.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_out.gif b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_out.gif
new file mode 100644 (file)
index 0000000..1f87400
Binary files /dev/null and b/tmf/org.lttng.scope.tmf2.views.ui/icons/toolbar/zoom_out.gif differ
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/plugin.properties b/tmf/org.lttng.scope.tmf2.views.ui/plugin.properties
new file mode 100644 (file)
index 0000000..de78703
--- /dev/null
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+Bundle-Vendor = LTTng Scope
+Bundle-Name = LTTng Scope TMF2 Views UI Plug-in
+
+view.timeline.name = Timeline
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/plugin.xml b/tmf/org.lttng.scope.tmf2.views.ui/plugin.xml
new file mode 100644 (file)
index 0000000..5fc9a46
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<plugin>
+   <extension
+         point="org.eclipse.ui.views">
+      <view
+            allowMultiple="true"
+            category="org.eclipse.linuxtools.tmf.ui.views.category"
+            class="org.lttng.scope.tmf2.views.ui.timeline.TimelineView"
+            id="org.lttng.scope.views.timeline"
+            name="%view.timeline.name"
+            restorable="true">
+      </view>
+   </extension>
+
+</plugin>
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/Activator.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/Activator.java
new file mode 100644 (file)
index 0000000..ec33727
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.activator.internal;
+
+import org.lttng.scope.common.ui.ScopeUIActivator;
+
+import javafx.application.Platform;
+
+/**
+ * The activator class controls the plug-in life cycle
+ *
+ * @noreference This class should not be accessed outside of this plugin.
+ */
+public class Activator extends ScopeUIActivator {
+
+    /**
+     * Return the singleton instance of this activator.
+     *
+     * @return The singleton instance
+     */
+    public static Activator instance() {
+        return ScopeUIActivator.getInstance(Activator.class);
+    }
+
+    @Override
+    protected void startActions() {
+        Platform.setImplicitExit(false);
+    }
+
+    @Override
+    protected void stopActions() {
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/activator/internal/package-info.java
new file mode 100644 (file)
index 0000000..6df2667
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.activator.internal;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Arrow.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Arrow.java
new file mode 100644 (file)
index 0000000..8f312aa
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
+ * Unported License. To view a copy of this license, visit
+ * https://creativecommons.org/licenses/by-sa/3.0/.
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.beans.InvalidationListener;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.scene.Group;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Line;
+
+/**
+ * JavaFX node representing a line with an arrow head at the end point.
+ *
+ * Initially inspired from http://stackoverflow.com/a/41353991/4227853.
+ */
+public class Arrow extends Group {
+
+    private static final double ARROW_HEAD_LENGTH = 5;
+    private static final double ARROW_HEAD_WIDTH = 3;
+
+    private final Line line;
+
+    /**
+     * Constructor specifying the initial coordinates of the arrow. The arrow
+     * head is drawn at the end point.
+     *
+     * @param startX
+     *            X of start point
+     * @param startY
+     *            Y of end point
+     * @param endX
+     *            X of end point
+     * @param endY
+     *            Y of end point.
+     */
+    public Arrow(double startX, double startY, double endX, double endY) {
+        this();
+        setStartX(startX);
+        setStartY(startY);
+        setEndX(endX);
+        setEndY(endY);
+    }
+
+    /**
+     * Constructor with default coordinates (usually (0,0)).
+     */
+    public Arrow() {
+        this(new Line(), new Line(), new Line());
+    }
+
+    private Arrow(Line line, Line arrow1, Line arrow2) {
+        super(line, arrow1, arrow2);
+        this.line = line;
+
+        /* The color of the arrow should follow the color of the main line. */
+        arrow1.strokeProperty().bind(line.strokeProperty());
+        arrow2.strokeProperty().bind(line.strokeProperty());
+
+        /* Listener to redraw the arrow parts when the line position changes. */
+        InvalidationListener updater = o -> {
+            double ex = getEndX();
+            double ey = getEndY();
+            double sx = getStartX();
+            double sy = getStartY();
+
+            arrow1.setEndX(ex);
+            arrow1.setEndY(ey);
+            arrow2.setEndX(ex);
+            arrow2.setEndY(ey);
+
+            if (ex == sx && ey == sy) {
+                /* The line is just a point. Don't draw the arrowhead. */
+                arrow1.setStartX(ex);
+                arrow1.setStartY(ey);
+                arrow2.setStartX(ex);
+                arrow2.setStartY(ey);
+            } else {
+                double factor = ARROW_HEAD_LENGTH / Math.hypot(sx-ex, sy-ey);
+                double factorO = ARROW_HEAD_WIDTH / Math.hypot(sx-ex, sy-ey);
+
+                // part in direction of main line
+                double dx = (sx - ex) * factor;
+                double dy = (sy - ey) * factor;
+
+                // part ortogonal to main line
+                double ox = (sx - ex) * factorO;
+                double oy = (sy - ey) * factorO;
+
+                arrow1.setStartX(ex + dx - oy);
+                arrow1.setStartY(ey + dy + ox);
+                arrow2.setStartX(ex + dx + oy);
+                arrow2.setStartY(ey + dy - ox);
+            }
+        };
+
+        /* Attach updater to properties */
+        startXProperty().addListener(updater);
+        startYProperty().addListener(updater);
+        endXProperty().addListener(updater);
+        endYProperty().addListener(updater);
+        updater.invalidated(null);
+    }
+
+    /**
+     * Set the value of the property startX.
+     *
+     * @param value
+     *            The new value
+     */
+    public final void setStartX(double value) {
+        line.setStartX(value);
+    }
+
+    /**
+     * Get the value of the property startX.
+     *
+     * @return The current value
+     */
+    public final double getStartX() {
+        return line.getStartX();
+    }
+
+    /**
+     * The startX property
+     *
+     * @return The startX property
+     */
+    public final DoubleProperty startXProperty() {
+        return line.startXProperty();
+    }
+
+    /**
+     * Set the value of the property startY.
+     *
+     * @param value
+     *            The new value
+     */
+    public final void setStartY(double value) {
+        line.setStartY(value);
+    }
+
+    /**
+     * Get the value of the property startY.
+     *
+     * @return The current value
+     */
+    public final double getStartY() {
+        return line.getStartY();
+    }
+
+    /**
+     * The startY property
+     *
+     * @return The startY property
+     */
+    public final DoubleProperty startYProperty() {
+        return line.startYProperty();
+    }
+
+    /**
+     * Set the value of the property endX.
+     *
+     * @param value
+     *            The new value
+     */
+    public final void setEndX(double value) {
+        line.setEndX(value);
+    }
+
+    /**
+     * Get the value of the property endX.
+     *
+     * @return The current value
+     */
+    public final double getEndX() {
+        return line.getEndX();
+    }
+
+    /**
+     * The endX property
+     *
+     * @return The endX property
+     */
+    public final DoubleProperty endXProperty() {
+        return line.endXProperty();
+    }
+
+    /**
+     * Set the value of the property endY.
+     *
+     * @param value
+     *            The new value
+     */
+    public final void setEndY(double value) {
+        line.setEndY(value);
+    }
+
+    /**
+     * Get the value of the property endY.
+     *
+     * @return The current value
+     */
+    public final double getEndY() {
+        return line.getEndY();
+    }
+
+    /**
+     * The endY property
+     *
+     * @return The endY property
+     */
+    public final DoubleProperty endYProperty() {
+        return line.endYProperty();
+    }
+
+    /**
+     * Set the value of the stroke property.
+     *
+     * @param value
+     *            The new value
+     */
+    public final void setStroke(@Nullable Paint value) {
+        line.setStroke(value);
+    }
+
+    /**
+     * Get the value of the stroke property.
+     *
+     * @return The current value
+     */
+    public final @Nullable Paint getStroke() {
+        return line.getStroke();
+    }
+
+    /**
+     * The stroke property, which determines the lines' color.
+     *
+     * @return The stroke property
+     */
+    public final ObjectProperty<@Nullable Paint> strokeProperty() {
+        return line.strokeProperty();
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/CountingGridPane.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/CountingGridPane.java
new file mode 100644 (file)
index 0000000..8ed93ea
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import javafx.scene.Node;
+import javafx.scene.layout.GridPane;
+
+/**
+ * Extension of a {@link GridPane} which tracks the number of inserted rows.
+ *
+ * Make sure you only add rows to the pane using {@link #appendRow(Node...)} or
+ * else it will give weird results!
+ *
+ * @author Alexandre Montplaisir
+ */
+public class CountingGridPane extends GridPane {
+
+    private int fNbRows = 0;
+
+    /**
+     * Add a row of nodes to this grid pane. Not thread-safe!
+     *
+     * @param children
+     *            The contents of the row
+     */
+    public void appendRow(Node... children) {
+        addRow(fNbRows++, children);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxColorFactory.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxColorFactory.java
new file mode 100644 (file)
index 0000000..422de49
--- /dev/null
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+
+import javafx.scene.paint.Color;
+
+public final class JfxColorFactory {
+
+    private JfxColorFactory() {}
+
+    private static final Map<ColorDefinition, Color> COLOR_MAP = new ConcurrentHashMap<>();
+    private static final Map<ColorDefinition, Color> DERIVED_COLOR_MAP = new ConcurrentHashMap<>();
+
+    /**
+     * Instantiate a {@link Color} from a {@link ColorDefinition} object.
+     *
+     * @param colorDef
+     *            The ColorDefinition
+     * @return The Color object
+     */
+    public static synchronized Color getColorFromDef(ColorDefinition colorDef) {
+        Color color = COLOR_MAP.get(colorDef);
+        if (color == null) {
+            color = Color.rgb(colorDef.fRed, colorDef.fGreen, colorDef.fBlue, (double) colorDef.fAlpha / (double) ColorDefinition.MAX);
+            COLOR_MAP.put(colorDef, color);
+        }
+        return color;
+    }
+
+    public static synchronized Color getDerivedColorFromDef(ColorDefinition colorDef) {
+        Color color = DERIVED_COLOR_MAP.get(colorDef);
+        if (color == null) {
+            color = getColorFromDef(colorDef);
+            color = color.desaturate().darker();
+           DERIVED_COLOR_MAP.put(colorDef, color);
+        }
+        return color;
+    }
+
+    /**
+     * Convert a JavaFX {@link Color} to its equivalent {@link ColorDefinition}.
+     *
+     * @param color
+     *            The color to convert
+     * @return A corresponding ColorDefinition
+     */
+    public static ColorDefinition colorToColorDef(Color color) {
+        /*
+         * ColorDefintion works with integer values 0 to 255, but JavaFX colors
+         * works with doubles 0.0 to 0.1
+         */
+        int red = (int) Math.round(color.getRed() * 255);
+        int green = (int) Math.round(color.getGreen() * 255);
+        int blue = (int) Math.round(color.getBlue() * 255);
+        int opacity = (int) Math.round(color.getOpacity() * 255);
+        return new ColorDefinition(red, green, blue, opacity);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxImageFactory.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxImageFactory.java
new file mode 100644 (file)
index 0000000..c42d66d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.scene.image.Image;
+
+/**
+ * Factory for JavaFX {@link Image}s. This will allow caching the Image objects,
+ * allowing any class to re-use already read images.
+ *
+ * @author Alexandre Montplaisir
+ * @noreference This cache is only valid for classes within the same jar/plugin.
+ *              The resource paths would not work from different plugins.
+ */
+public final class JfxImageFactory {
+
+    private static final JfxImageFactory INSTANCE = new JfxImageFactory();
+
+    private JfxImageFactory() {}
+
+    /**
+     * Get the singleton instance of this factory.
+     *
+     * @return The instance
+     */
+    public static JfxImageFactory instance() {
+        return INSTANCE;
+    }
+
+    private final Map<String, Image> fImages = new HashMap<>();
+
+    /**
+     * Get the {@link Image} for a given path within the jar's resources.
+     *
+     * @param resourcePath
+     *            The path to the image resource. It should be a standard
+     *            .gif/.png/.jpg etc. file.
+     * @return The corresponding Image.
+     */
+    public synchronized @Nullable Image getImageFromResource(String resourcePath) {
+        Image image = fImages.get(resourcePath);
+        if (image == null) {
+            try (InputStream is = getClass().getResourceAsStream(resourcePath)) {
+                if (is == null) {
+                    /* The image was not found, the path is invalid */
+                    return null;
+                }
+                image = new Image(is);
+            } catch (IOException e) {
+                return null;
+            }
+            fImages.put(resourcePath, image);
+        }
+        return image;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxUtils.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/JfxUtils.java
new file mode 100644 (file)
index 0000000..f0556ea
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javafx.application.Platform;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.Node;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.OverrunStyle;
+import javafx.scene.text.Font;
+import javafx.stage.Screen;
+import javafx.stage.Window;
+
+/**
+ * JavaFX-related utilities
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class JfxUtils {
+
+    /**
+     * Double property with a non-modifiable value of 0. For things that should
+     * remain at 0.
+     */
+    public static final ReadOnlyDoubleProperty ZERO_PROPERTY = new SimpleDoubleProperty(0);
+
+
+    private static final MethodHandles.Lookup LOOKUP = requireNonNull(MethodHandles.lookup());
+    private static final MethodHandle COMPUTE_CLIPPED_TEXT_HANDLE;
+    static {
+        MethodHandle handle = null;
+        try {
+            Class<?> c = Class.forName("com.sun.javafx.scene.control.skin.Utils"); //$NON-NLS-1$
+            Method method = c.getDeclaredMethod("computeClippedText", //$NON-NLS-1$
+                    Font.class, String.class, double.class, OverrunStyle.class, String.class);
+            method.setAccessible(true);
+            handle = LOOKUP.unreflect(method);
+        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException e) {
+            /* Should not fail if we did everything correctly. */
+        }
+        COMPUTE_CLIPPED_TEXT_HANDLE = requireNonNull(handle);
+    }
+
+    /**
+     * Accessor for the
+     * com.sun.javafx.scene.control.skin.Utils.computeClippedText() method.
+     *
+     * This method implements the logic to clip Label strings. It can be useful
+     * for other types, like Text. Unfortunately it is not public, but this
+     * accessor allows calling it through reflection. It makes use of
+     * {@link MethodHandle#invokeExact}, which should be close to just as fast
+     * as a standard compiled method call.
+     *
+     * @param font
+     *            The font of the text that will be used
+     * @param text
+     *            The string to clip
+     * @param width
+     *            The maximum width we want to limit the string to
+     * @param type
+     *            The {@link OverrunStyle}
+     * @param ellipsisString
+     *            The string to use as ellipsis
+     * @return The clipped string, or "ERROR" if an error happened.
+     *         Unfortunately we lose the exception typing due to the reflection
+     *         call, so we do not want to throw "Throwable" here.
+     */
+    public static String computeClippedText(Font font, String text, double width,
+            OverrunStyle type, String ellipsisString) {
+        try {
+            String str = (String) COMPUTE_CLIPPED_TEXT_HANDLE.invokeExact(font, text, width, type, ellipsisString);
+            return requireNonNull(str);
+        } catch (Throwable e) {
+            return "ERROR"; //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Run the given {@link Runnable} on the UI/main/application thread.
+     *
+     * If you know for sure you are *not* on the main thread, you should use
+     * {@link Platform#runLater} to queue the runnable for the main thread.
+     *
+     * If you are not sure, you can use this method. The difference with
+     * {@link Platform#runLater} is that if you are actually already on the UI
+     * thread, the runnable will be run immediately. Whereas calling runLater
+     * from the UI will just queue the runnable at the end of the queue.
+     *
+     * @param r
+     *            The runnable to run on the main thread
+     */
+    public static void runOnMainThread(Runnable r) {
+        if (Platform.isFxApplicationThread()) {
+            r.run();
+        } else {
+            Platform.runLater(r);
+        }
+    }
+
+    /**
+     * Utility method to center a Dialog/Alert on the middle of the current
+     * screen. Used to workaround a "bug" with the current version of JavaFX (or
+     * the SWT/JavaFX embedding?) where alerts always show on the primary
+     * screen, not necessarily the current one.
+     *
+     * @param dialog
+     *            The dialog to reposition. It must be already shown, or else
+     *            this will do nothing.
+     * @param referenceNode
+     *            The dialog should be moved to the same screen as this node's
+     *            window.
+     */
+    public static void centerDialogOnScreen(Dialog<?> dialog, Node referenceNode) {
+        Window window = referenceNode.getScene().getWindow();
+        Rectangle2D windowRectangle = new Rectangle2D(window.getX(), window.getY(), window.getWidth(), window.getHeight());
+
+        List<Screen> screens = Screen.getScreensForRectangle(windowRectangle);
+        Screen screen = screens.stream()
+                .findFirst()
+                .orElse(Screen.getPrimary());
+
+        Rectangle2D screenBounds = screen.getBounds();
+        dialog.setX((screenBounds.getWidth() - dialog.getWidth()) / 2 + screenBounds.getMinX());
+//        dialog.setY((screenBounds.getHeight() - dialog.getHeight()) / 2 + screenBounds.getMinY());
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Logo.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/Logo.java
new file mode 100644 (file)
index 0000000..242b67f
--- /dev/null
@@ -0,0 +1,76 @@
+package org.lttng.scope.tmf2.views.ui.jfx;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.animation.Animation;
+import javafx.animation.Interpolator;
+import javafx.animation.RotateTransition;
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+public class Logo extends Application {
+
+    private static final String BACKGROUND_STYLE = "-fx-background-color: rgba(255, 255, 255, 255);"; //$NON-NLS-1$
+
+    private static final Color LTTNG_PURPLE = Color.web("#996ABC"); //$NON-NLS-1$
+    private static final Color LTTNG_LIGHT_BLUE = Color.web("#C3DEF4"); //$NON-NLS-1$
+
+    @Override
+    public void start(@Nullable Stage stage) throws Exception {
+        if (stage == null) {
+            return;
+        }
+
+        Rectangle clipRect1 = new Rectangle(-130, -130, 115, 115);
+        Rectangle clipRect2 = new Rectangle(15, -130, 115, 115);
+        Rectangle clipRect3 = new Rectangle(-130, 15, 115, 115);
+        Rectangle clipRect4 = new Rectangle(15, 15, 115, 115);
+        Group clip = new Group(clipRect1, clipRect2, clipRect3, clipRect4);
+
+        Circle spinnanCircle = new Circle(100);
+        spinnanCircle.setFill(null);
+        spinnanCircle.setStrokeWidth(30);
+        spinnanCircle.setStroke(LTTNG_PURPLE);
+        spinnanCircle.setClip(clip);
+
+        Circle magCircle = new Circle(60);
+        magCircle.setFill(null);
+        magCircle.setStrokeWidth(25);
+        magCircle.setStroke(LTTNG_LIGHT_BLUE);
+
+        Rectangle magHandle = new Rectangle(-12.5, 60, 25, 110);
+        magHandle.setFill(LTTNG_LIGHT_BLUE);
+
+        Group mag = new Group(magCircle, magHandle);
+
+        Group root = new Group(spinnanCircle, mag);
+        root.setRotate(30);
+        root.relocate(0, 0);
+
+        Pane pane = new Pane(root);
+        pane.setStyle(BACKGROUND_STYLE);
+
+        RotateTransition spinnan = new RotateTransition(Duration.seconds(4), spinnanCircle);
+        spinnan.setByAngle(360);
+        spinnan.setCycleCount(Animation.INDEFINITE);
+        spinnan.setInterpolator(Interpolator.LINEAR);
+
+        Scene scene = new Scene(pane);
+        stage.setScene(scene);
+        stage.show();
+
+        spinnan.play();
+    }
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ArrowExample.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ArrowExample.java
new file mode 100644 (file)
index 0000000..898af02
--- /dev/null
@@ -0,0 +1,54 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.jfx.Arrow;
+
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+
+public class ArrowExample extends Application {
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(@Nullable Stage primaryStage) throws Exception {
+        if (primaryStage == null) {
+            return;
+        }
+
+        Pane root = new Pane();
+        Arrow arrow = new Arrow();
+        arrow.setStroke(Color.GREEN);
+        root.getChildren().add(arrow);
+
+        root.setOnMouseClicked(evt -> {
+            switch (evt.getButton()) {
+            case PRIMARY:
+                // set pos of end with arrow head
+                arrow.setEndX(evt.getX());
+                arrow.setEndY(evt.getY());
+                break;
+            case SECONDARY:
+                // set pos of end without arrow head
+                arrow.setStartX(evt.getX());
+                arrow.setStartY(evt.getY());
+                break;
+            case MIDDLE:
+            case NONE:
+            default:
+                break;
+            }
+        });
+
+        Scene scene = new Scene(root, 400, 400);
+
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example.java
new file mode 100644 (file)
index 0000000..ea46481
--- /dev/null
@@ -0,0 +1,61 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+
+@NonNullByDefault({})
+public class Example extends Application {
+    @Override
+    public void start(Stage stage) {
+        Node content = new Rectangle(1000, 700, Color.GREEN);
+        ScrollPane scrollPane = new ScrollPane(content);
+        scrollPane.setPrefSize(500, 300);
+
+        ChangeListener<Object> changeListener = new ChangeListener<Object>() {
+            @Override
+            public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
+                double hmin = scrollPane.getHmin();
+                double hmax = scrollPane.getHmax();
+                double hvalue = scrollPane.getHvalue();
+                double contentWidth = content.getLayoutBounds().getWidth();
+                double viewportWidth = scrollPane.getViewportBounds().getWidth();
+
+                double hoffset =
+                    Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);
+
+                double vmin = scrollPane.getVmin();
+                double vmax = scrollPane.getVmax();
+                double vvalue = scrollPane.getVvalue();
+                double contentHeight = content.getLayoutBounds().getHeight();
+                double viewportHeight = scrollPane.getViewportBounds().getHeight();
+
+                double voffset =
+                    Math.max(0,  contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);
+
+                System.out.printf("Offset: [%.1f, %.1f] width: %.1f height: %.1f %n",
+                        hoffset, voffset, viewportWidth, viewportHeight);
+            }
+        };
+        scrollPane.viewportBoundsProperty().addListener(changeListener);
+        scrollPane.hvalueProperty().addListener(changeListener);
+        scrollPane.vvalueProperty().addListener(changeListener);
+
+        Scene scene = new Scene(scrollPane, 640, 480);
+        stage.setScene(scene);
+
+        stage.show();
+    }
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example2.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/Example2.java
new file mode 100644 (file)
index 0000000..c15f98e
--- /dev/null
@@ -0,0 +1,79 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import javafx.application.Application;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.layout.Pane;
+import javafx.stage.Stage;
+
+@NonNullByDefault({})
+public class Example2 extends Application {
+
+    private static final double TEN_BILLIONS = 10000000000.0;
+
+    @Override
+    public void start(Stage stage) {
+//        Node content = new Rectangle(1000, 700, Color.GREEN);
+        Pane pane = new Pane();
+        pane.setPrefSize(TEN_BILLIONS, TEN_BILLIONS);
+        ScrollPane scrollPane = new ScrollPane(pane);
+        scrollPane.setPrefSize(500, 300);
+
+        ChangeListener<Object> changeListener = new ChangeListener<Object>() {
+            @Override
+            public void changed(ObservableValue<? extends Object> observable, Object oldValue, Object newValue) {
+                System.out.println("source=" + observable.toString());
+
+                double hmin = scrollPane.getHmin();
+                double hmax = scrollPane.getHmax();
+                double hvalue = scrollPane.getHvalue();
+                double contentWidth = pane.getLayoutBounds().getWidth();
+                double viewportWidth = scrollPane.getViewportBounds().getWidth();
+
+                double hoffset =
+                    Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);
+
+                double vmin = scrollPane.getVmin();
+                double vmax = scrollPane.getVmax();
+                double vvalue = scrollPane.getVvalue();
+                double contentHeight = pane.getLayoutBounds().getHeight();
+                double viewportHeight = scrollPane.getViewportBounds().getHeight();
+
+                double voffset =
+                    Math.max(0,  contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);
+
+                System.out.printf("Offset: [%.1f, %.1f] width: %.1f height: %.1f %n",
+                        hoffset, voffset, viewportWidth, viewportHeight);
+            }
+        };
+        scrollPane.viewportBoundsProperty().addListener(changeListener);
+        scrollPane.hvalueProperty().addListener(changeListener);
+        scrollPane.vvalueProperty().addListener(changeListener);
+
+        /* Drawing on the region */
+        Canvas canvas1 = new Canvas(100, 100);
+        canvas1.relocate(TEN_BILLIONS - 100, 0);
+        canvas1.getGraphicsContext2D().strokeOval(60, 60, 30, 30);
+
+        Canvas canvas2 = new Canvas(100, 100);
+        canvas2.relocate(TEN_BILLIONS - 100, TEN_BILLIONS - 100);
+        canvas2.getGraphicsContext2D().fillOval(60, 60, 30, 30);
+
+        pane.getChildren().addAll(canvas1, canvas2);
+
+        /* Showing the scene */
+        Scene scene = new Scene(scrollPane, 640, 480);
+        stage.setScene(scene);
+
+        stage.show();
+    }
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleCanvas.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleCanvas.java
new file mode 100644 (file)
index 0000000..496090c
--- /dev/null
@@ -0,0 +1,55 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.ArcType;
+import javafx.stage.Stage;
+
+@NonNullByDefault({})
+public class ExampleCanvas extends Application {
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(Stage primaryStage) {
+        primaryStage.setTitle("Drawing Operations Test");
+        Group root = new Group();
+        Canvas canvas = new Canvas(300, 250);
+        GraphicsContext gc = canvas.getGraphicsContext2D();
+        drawShapes(gc);
+        root.getChildren().add(canvas);
+        primaryStage.setScene(new Scene(root));
+        primaryStage.show();
+    }
+
+    private static void drawShapes(GraphicsContext gc) {
+        gc.setFill(Color.GREEN);
+        gc.setStroke(Color.BLUE);
+        gc.setLineWidth(5);
+        gc.strokeLine(40, 10, 10, 40);
+        gc.fillOval(10, 60, 30, 30);
+        gc.strokeOval(60, 60, 30, 30);
+        gc.fillRoundRect(110, 60, 30, 30, 10, 10);
+        gc.strokeRoundRect(160, 60, 30, 30, 10, 10);
+        gc.fillArc(10, 110, 30, 30, 45, 240, ArcType.OPEN);
+        gc.fillArc(60, 110, 30, 30, 45, 240, ArcType.CHORD);
+        gc.fillArc(110, 110, 30, 30, 45, 240, ArcType.ROUND);
+        gc.strokeArc(10, 160, 30, 30, 45, 240, ArcType.OPEN);
+        gc.strokeArc(60, 160, 30, 30, 45, 240, ArcType.CHORD);
+        gc.strokeArc(110, 160, 30, 30, 45, 240, ArcType.ROUND);
+        gc.fillPolygon(new double[]{10, 40, 10, 40},
+                       new double[]{210, 210, 240, 240}, 4);
+        gc.strokePolygon(new double[]{60, 90, 60, 90},
+                         new double[]{210, 210, 240, 240}, 4);
+        gc.strokePolyline(new double[]{110, 140, 110, 140},
+                          new double[]{210, 210, 240, 240}, 4);
+    }
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag.java
new file mode 100644 (file)
index 0000000..ced9162
--- /dev/null
@@ -0,0 +1,85 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.application.Application;
+import javafx.event.EventHandler;
+import javafx.scene.Cursor;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.stage.Stage;
+
+/**
+ * @web http://java-buddy.blogspot.com/
+ */
+public class ExampleMouseDrag extends Application {
+
+    private double orgSceneX, orgSceneY;
+    private double orgTranslateX, orgTranslateY;
+
+    @Override
+    public void start(@Nullable Stage primaryStage) {
+        if (primaryStage == null) {
+            return;
+        }
+
+        //Create Circles
+        Circle circleRed = new Circle(50.0, Color.RED);
+        circleRed.setCursor(Cursor.HAND);
+        circleRed.setOnMousePressed(circleOnMousePressedEventHandler);
+        circleRed.setOnMouseDragged(circleOnMouseDraggedEventHandler);
+
+        Circle circleGreen = new Circle(50.0, Color.GREEN);
+        circleGreen.setCursor(Cursor.MOVE);
+        circleGreen.setCenterX(150);
+        circleGreen.setCenterY(150);
+        circleGreen.setOnMousePressed(circleOnMousePressedEventHandler);
+        circleGreen.setOnMouseDragged(circleOnMouseDraggedEventHandler);
+
+        Circle circleBlue = new Circle(50.0, Color.BLUE);
+        circleBlue.setCursor(Cursor.CROSSHAIR);
+        circleBlue.setTranslateX(300);
+        circleBlue.setTranslateY(100);
+        circleBlue.setOnMousePressed(circleOnMousePressedEventHandler);
+        circleBlue.setOnMouseDragged(circleOnMouseDraggedEventHandler);
+
+        Group root = new Group();
+        root.getChildren().addAll(circleRed, circleGreen, circleBlue);
+
+        primaryStage.setResizable(false);
+        primaryStage.setScene(new Scene(root, 400,350));
+
+        primaryStage.setTitle("java-buddy");
+        primaryStage.show();
+    }
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    private EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
+        @Override
+        public void handle(MouseEvent t) {
+            orgSceneX = t.getSceneX();
+            orgSceneY = t.getSceneY();
+            orgTranslateX = ((Circle)(t.getSource())).getTranslateX();
+            orgTranslateY = ((Circle)(t.getSource())).getTranslateY();
+        }
+    };
+
+    private EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
+        @Override
+        public void handle(MouseEvent t) {
+            double offsetX = t.getSceneX() - orgSceneX;
+            double offsetY = t.getSceneY() - orgSceneY;
+            double newTranslateX = orgTranslateX + offsetX;
+            double newTranslateY = orgTranslateY + offsetY;
+
+            ((Circle)(t.getSource())).setTranslateX(newTranslateX);
+            ((Circle)(t.getSource())).setTranslateY(newTranslateY);
+        }
+    };
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag2.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ExampleMouseDrag2.java
new file mode 100644 (file)
index 0000000..6421fa6
--- /dev/null
@@ -0,0 +1,382 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.application.Application;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Region;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeLineCap;
+import javafx.stage.Stage;
+
+public class ExampleMouseDrag2 extends Application {
+
+//    public static Image image = new Image("http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/320px-Siberischer_tiger_de_edit02.jpg");
+    public Image image = new Image("file:///home/alexandre/Desktop/tiger.jpg");
+//    public Image image = new Image( getClass().getResource( "/home/alexandre/Desktop/tiger.jpg").toExternalForm());
+
+    SelectionModel selectionModel = new SelectionModel();
+
+    DragMouseGestures dragMouseGestures = new DragMouseGestures();
+
+    static Random rnd = new Random();
+
+    @Override
+    public void start(@Nullable Stage primaryStage) {
+        if (primaryStage == null) {
+            return;
+        }
+
+        Pane pane = new Pane();
+        pane.setStyle("-fx-background-color:white");
+
+        new RubberBandSelection( pane);
+
+        double width = 200;
+        double height = 160;
+
+        double padding = 20;
+        for( int row=0; row < 4; row++) {
+            for( int col=0; col < 4; col++) {
+
+                Selectable selectable = new Selectable( width, height);
+                selectable.relocate( padding * (col+1) + width * col, padding * (row + 1) + height * row);
+
+                pane.getChildren().add(selectable);
+
+                dragMouseGestures.makeDraggable(selectable);
+
+            }
+        }
+
+        Label infoLabel = new Label( "Drag on scene for Rubberband Selection. Shift+Click to add to selection, CTRL+Click to toggle selection. Drag selected nodes for multi-dragging.");
+        pane.getChildren().add( infoLabel);
+
+        Scene scene = new Scene( pane, 1600, 900);
+        scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm());
+
+        primaryStage.setScene( scene);
+        primaryStage.show();
+
+
+
+    }
+
+    private class Selectable extends Region {
+
+        ImageView view;
+
+        public Selectable( double width, double height) {
+
+            view = new ImageView( image);
+            view.setFitWidth(width);
+            view.setFitHeight(height);
+
+            getChildren().add( view);
+
+            this.setPrefSize(width, height);
+        }
+
+    }
+
+    private class SelectionModel {
+
+        Set<Node> selection = new HashSet<>();
+
+        public void add( Node node) {
+
+            if( !node.getStyleClass().contains("highlight")) {
+                node.getStyleClass().add( "highlight");
+            }
+
+            selection.add( node);
+        }
+
+        public void remove( Node node) {
+            node.getStyleClass().remove( "highlight");
+            selection.remove( node);
+        }
+
+        public void clear() {
+
+            while( !selection.isEmpty()) {
+                remove( selection.iterator().next());
+            }
+
+        }
+
+        public boolean contains( Node node) {
+            return selection.contains(node);
+        }
+
+//        public int size() {
+//            return selection.size();
+//        }
+
+        public void log() {
+            System.out.println( "Items in model: " + Arrays.asList( selection.toArray()));
+        }
+
+    }
+
+    private class DragMouseGestures {
+
+        final DragContext dragContext = new DragContext();
+
+        private boolean enabled = false;
+
+        public void makeDraggable(final Node node) {
+
+            node.setOnMousePressed(onMousePressedEventHandler);
+            node.setOnMouseDragged(onMouseDraggedEventHandler);
+            node.setOnMouseReleased(onMouseReleasedEventHandler);
+
+        }
+
+        EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                // don't do anything if the user is in the process of adding to the selection model
+                if( event.isControlDown() || event.isShiftDown()) {
+                    return;
+                }
+
+                Node node = (Node) event.getSource();
+
+                dragContext.x = node.getTranslateX() - event.getSceneX();
+                dragContext.y = node.getTranslateY() - event.getSceneY();
+
+                // clear the model if the current node isn't in the selection => new selection
+                if( !selectionModel.contains(node)) {
+                    selectionModel.clear();
+                    selectionModel.add( node);
+                }
+
+                // flag that the mouse released handler should consume the event, so it won't bubble up to the pane which has a rubberband selection mouse released handler
+                enabled = true;
+
+                // prevent rubberband selection handler
+                event.consume();
+            }
+        };
+
+        EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                if( !enabled) {
+                    return;
+                }
+
+                // all in selection
+                for( Node node: selectionModel.selection) {
+                    node.setTranslateX( dragContext.x + event.getSceneX());
+                    node.setTranslateY( dragContext.y + event.getSceneY());
+                }
+
+            }
+        };
+
+        EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                // prevent rubberband selection handler
+                if( enabled) {
+
+                    // set node's layout position to current position,remove translate coordinates
+                    for( Node node: selectionModel.selection) {
+                        fixPosition(node);
+                    }
+
+                    enabled = false;
+
+                    event.consume();
+                }
+            }
+        };
+
+        /**
+         * Set node's layout position to current position, remove translate coordinates.
+         * @param node
+         */
+        private void fixPosition( Node node) {
+
+            double x = node.getTranslateX();
+            double y = node.getTranslateY();
+
+            node.relocate(node.getLayoutX() + x, node.getLayoutY() + y);
+
+            node.setTranslateX(0);
+            node.setTranslateY(0);
+
+        }
+
+        class DragContext {
+
+            double x;
+            double y;
+
+        }
+
+    }
+
+    private class RubberBandSelection {
+
+        final DragContext dragContext = new DragContext();
+        final Rectangle rect;
+
+        Pane group;
+        boolean enabled = false;
+
+        public RubberBandSelection( Pane group) {
+
+            this.group = group;
+
+            rect = new Rectangle( 0,0,0,0);
+            rect.setStroke(Color.BLUE);
+            rect.setStrokeWidth(1);
+            rect.setStrokeLineCap(StrokeLineCap.ROUND);
+            rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
+
+            group.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressedEventHandler);
+            group.addEventHandler(MouseEvent.MOUSE_DRAGGED, onMouseDraggedEventHandler);
+            group.addEventHandler(MouseEvent.MOUSE_RELEASED, onMouseReleasedEventHandler);
+
+        }
+
+        EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                // simple flag to prevent multiple handling of this event or we'd get an exception because rect is already on the scene
+                // eg if you drag with left mouse button and while doing that click the right mouse button
+                if( enabled) {
+                    return;
+                }
+
+                dragContext.mouseAnchorX = event.getSceneX();
+                dragContext.mouseAnchorY = event.getSceneY();
+
+                rect.setX(dragContext.mouseAnchorX);
+                rect.setY(dragContext.mouseAnchorY);
+                rect.setWidth(0);
+                rect.setHeight(0);
+
+                group.getChildren().add( rect);
+
+                event.consume();
+
+                enabled = true;
+            }
+        };
+
+        EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                if( !event.isShiftDown() && !event.isControlDown()) {
+                    selectionModel.clear();
+                }
+
+                for( Node node: group.getChildren()) {
+
+                    if( node instanceof Selectable) {
+                        if( node.getBoundsInParent().intersects( rect.getBoundsInParent())) {
+
+                            if( event.isShiftDown()) {
+
+                                selectionModel.add( node);
+
+                            } else if( event.isControlDown()) {
+
+                                if( selectionModel.contains( node)) {
+                                    selectionModel.remove( node);
+                                } else {
+                                    selectionModel.add( node);
+                                }
+                            } else {
+                                selectionModel.add( node);
+                            }
+
+                        }
+                    }
+
+                }
+
+                selectionModel.log();
+
+                rect.setX(0);
+                rect.setY(0);
+                rect.setWidth(0);
+                rect.setHeight(0);
+
+                group.getChildren().remove( rect);
+
+                event.consume();
+
+                enabled = false;
+            }
+        };
+
+        EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
+
+            @Override
+            public void handle(MouseEvent event) {
+
+                double offsetX = event.getSceneX() - dragContext.mouseAnchorX;
+                double offsetY = event.getSceneY() - dragContext.mouseAnchorY;
+
+                if( offsetX > 0) {
+                    rect.setWidth( offsetX);
+                } else {
+                    rect.setX(event.getSceneX());
+                    rect.setWidth(dragContext.mouseAnchorX - rect.getX());
+                }
+
+                if( offsetY > 0) {
+                    rect.setHeight( offsetY);
+                } else {
+                    rect.setY(event.getSceneY());
+                    rect.setHeight(dragContext.mouseAnchorY - rect.getY());
+                }
+
+                event.consume();
+
+            }
+        };
+
+        private final class DragContext {
+
+            public double mouseAnchorX;
+            public double mouseAnchorY;
+
+
+        }
+    }
+
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/FadeTransitionEx.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/FadeTransitionEx.java
new file mode 100644 (file)
index 0000000..8bb8d30
--- /dev/null
@@ -0,0 +1,39 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.animation.FadeTransition;
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+public class FadeTransitionEx extends Application {
+
+  public static void main(String[] args) {
+    Application.launch(args);
+  }
+
+  @Override
+  public void start(@Nullable Stage primaryStage) {
+      if (primaryStage == null) {
+          return;
+      }
+
+    Group group = new Group();
+    Rectangle rect = new Rectangle(20,20,200,200);
+
+    FadeTransition ft = new FadeTransition(Duration.millis(5000), rect);
+    ft.setFromValue(1.0);
+    ft.setToValue(0.0);
+    ft.play();
+
+    group.getChildren().add(rect);
+
+    Scene scene = new Scene(group, 300, 200);
+    primaryStage.setScene(scene);
+    primaryStage.show();
+  }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ParallelTransitionExample.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ParallelTransitionExample.java
new file mode 100644 (file)
index 0000000..5487861
--- /dev/null
@@ -0,0 +1,74 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.animation.Animation;
+import javafx.animation.FadeTransition;
+import javafx.animation.ParallelTransition;
+import javafx.animation.RotateTransition;
+import javafx.animation.ScaleTransition;
+import javafx.animation.TranslateTransition;
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+
+public class ParallelTransitionExample extends Application {
+
+    public static void main(String[] args) {
+        Application.launch(args);
+    }
+
+    @Override
+    public void start(@Nullable Stage primaryStage) {
+        if (primaryStage == null) {
+            return;
+        }
+
+        Group group = new Group();
+        Rectangle rectParallel = new Rectangle(20, 20, 200, 200);
+
+        FadeTransition fadeTransition = new FadeTransition(Duration.millis(3000),
+                rectParallel);
+        fadeTransition.setFromValue(1.0f);
+        fadeTransition.setToValue(0.3f);
+        fadeTransition.setCycleCount(2);
+        fadeTransition.setAutoReverse(true);
+
+        TranslateTransition translateTransition = new TranslateTransition(
+                Duration.millis(2000), rectParallel);
+        translateTransition.setFromX(50);
+        translateTransition.setToX(350);
+        translateTransition.setCycleCount(2);
+        translateTransition.setAutoReverse(true);
+
+        RotateTransition rotateTransition = new RotateTransition(
+                Duration.millis(3000), rectParallel);
+        rotateTransition.setByAngle(180f);
+        rotateTransition.setCycleCount(4);
+        rotateTransition.setAutoReverse(true);
+
+        ScaleTransition scaleTransition = new ScaleTransition(
+                Duration.millis(2000), rectParallel);
+        scaleTransition.setToX(2f);
+        scaleTransition.setToY(2f);
+        scaleTransition.setCycleCount(2);
+        scaleTransition.setAutoReverse(true);
+
+        ParallelTransition parallelTransition = new ParallelTransition();
+        parallelTransition.getChildren().addAll(fadeTransition,
+                translateTransition,
+                rotateTransition,
+                scaleTransition);
+        parallelTransition.setCycleCount(Animation.INDEFINITE);
+        parallelTransition.play();
+
+        group.getChildren().add(rectParallel);
+
+        Scene scene = new Scene(group, 300, 200);
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/SwtToobar2.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/SwtToobar2.java
new file mode 100644 (file)
index 0000000..1377299
--- /dev/null
@@ -0,0 +1,78 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.ToolItem;
+
+@NonNullByDefault({})
+public class SwtToobar2 {
+
+    private Shell shell;
+
+    public SwtToobar2() {
+        Display display = new Display();
+        shell = new Shell(display, SWT.SHELL_TRIM);
+        shell.setLayout(new FillLayout(SWT.VERTICAL));
+        shell.setSize(50, 100);
+
+        ToolBar toolbar = new ToolBar(shell, SWT.FLAT);
+        ToolItem itemDrop = new ToolItem(toolbar, SWT.DROP_DOWN);
+        itemDrop.setText("drop menu");
+
+        itemDrop.addSelectionListener(new SelectionAdapter() {
+
+            Menu dropMenu = null;
+
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if (dropMenu == null) {
+                    dropMenu = new Menu(shell, SWT.POP_UP);
+                    shell.setMenu(dropMenu);
+                    MenuItem itemCheck = new MenuItem(dropMenu, SWT.CHECK);
+                    itemCheck.setText("checkbox");
+                    MenuItem itemRadio = new MenuItem(dropMenu, SWT.RADIO);
+                    itemRadio.setText("radio1");
+                    MenuItem itemRadio2 = new MenuItem(dropMenu, SWT.RADIO);
+                    itemRadio2.setText("radio2");
+                }
+
+                if (e.detail == SWT.ARROW) {
+                    // Position the menu below and vertically aligned with the
+                    // the drop down tool button.
+                    final ToolItem toolItem = (ToolItem) e.widget;
+                    final ToolBar toolBar = toolItem.getParent();
+
+                    Point point = toolBar.toDisplay(new Point(e.x, e.y));
+                    dropMenu.setLocation(point.x, point.y);
+                    dropMenu.setVisible(true);
+                }
+
+            }
+
+        });
+
+        shell.open();
+
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+
+        display.dispose();
+    }
+
+    public static void main(String[] args) {
+        new SwtToobar2();
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/TestSwtToolbar.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/TestSwtToolbar.java
new file mode 100644 (file)
index 0000000..c4f77be
--- /dev/null
@@ -0,0 +1,62 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+/*******************************************************************************
+ * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * ToolBar example snippet: place a drop down menu in a tool bar
+ *
+ * For a list of all SWT example snippets see
+ * http://www.eclipse.org/swt/snippets/
+ */
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+public class TestSwtToolbar {
+
+    public static void main(String[] args) {
+        final Display display = new Display();
+        final Shell shell = new Shell(display);
+
+        final ToolBar toolBar = new ToolBar(shell, SWT.NONE);
+        Rectangle clientArea = shell.getClientArea();
+        toolBar.setLocation(clientArea.x, clientArea.y);
+
+        final Menu menu = new Menu(shell, SWT.POP_UP);
+        for (int i = 0; i < 8; i++) {
+            MenuItem item = new MenuItem(menu, SWT.PUSH);
+            item.setText("Item " + i);
+        }
+
+        final ToolItem item = new ToolItem(toolBar, SWT.DROP_DOWN);
+        item.addListener(SWT.Selection, event -> {
+            if (event.detail == SWT.ARROW) {
+                Rectangle rect = item.getBounds();
+                Point pt = new Point(rect.x, rect.y + rect.height);
+                pt = toolBar.toDisplay(pt);
+                menu.setLocation(pt.x, pt.y);
+                menu.setVisible(true);
+            }
+        });
+
+        toolBar.pack();
+        shell.pack();
+        shell.open();
+        while (!shell.isDisposed()) {
+            if (!display.readAndDispatch()) {
+                display.sleep();
+            }
+        }
+        menu.dispose();
+        display.dispose();
+    }
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ZoomExample.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/ZoomExample.java
new file mode 100644 (file)
index 0000000..0409003
--- /dev/null
@@ -0,0 +1,194 @@
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
+
+import java.net.MalformedURLException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+import javafx.application.Application;
+import javafx.geometry.Bounds;
+import javafx.geometry.Point2D;
+import javafx.geometry.Pos;
+import javafx.scene.Group;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.SplitPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.StackPane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.stage.Stage;
+
+@NonNullByDefault({})
+public class ZoomExample extends Application {
+
+    public static Region createContent() {
+        double width = 1000;
+        double height = 1000;
+
+        Canvas canvas = new Canvas(width, height);
+        GraphicsContext gc = canvas.getGraphicsContext2D();
+
+        gc.setFill(Color.LIGHTGREY);
+        gc.fillRect(0, 0, width, height);
+
+        gc.setStroke(Color.BLUE);
+        gc.beginPath();
+
+        for (int i = 50; i < width; i += 50) {
+            gc.moveTo(i, 0);
+            gc.lineTo(i, height);
+        }
+
+        for (int i = 50; i < height; i += 50) {
+            gc.moveTo(0, i);
+            gc.lineTo(width, i);
+        }
+        gc.stroke();
+
+        Pane content = new Pane(
+                new Circle(50, 50, 20),
+                new Circle(120, 90, 20, Color.RED),
+                new Circle(200, 70, 20, Color.GREEN)
+        );
+
+        StackPane result = new StackPane(canvas, content);
+        result.setAlignment(Pos.TOP_LEFT);
+
+        class DragData {
+
+            double startX;
+            double startY;
+            double startLayoutX;
+            double startLayoutY;
+            Node dragTarget;
+        }
+
+        DragData dragData = new DragData();
+
+        content.setOnMousePressed(evt -> {
+            if (evt.getTarget() != content) {
+                // initiate drag gesture, if a child of content receives the
+                // event to prevent ScrollPane from panning.
+                evt.consume();
+                evt.setDragDetect(true);
+            }
+        });
+
+        content.setOnDragDetected(evt -> {
+            Node n = (Node) evt.getTarget();
+            if (n != content) {
+                // set start paremeters
+                while (n.getParent() != content) {
+                    n = n.getParent();
+                }
+                dragData.startX = evt.getX();
+                dragData.startY = evt.getY();
+                dragData.startLayoutX = n.getLayoutX();
+                dragData.startLayoutY = n.getLayoutY();
+                dragData.dragTarget = n;
+                n.startFullDrag();
+                evt.consume();
+            }
+        });
+
+        // stop dragging when mouse is released
+        content.setOnMouseReleased(evt -> dragData.dragTarget = null);
+
+        content.setOnMouseDragged(evt -> {
+            if (dragData.dragTarget != null) {
+                // move dragged node
+                dragData.dragTarget.setLayoutX(evt.getX() + dragData.startLayoutX - dragData.startX);
+                dragData.dragTarget.setLayoutY(evt.getY() + dragData.startLayoutY - dragData.startY);
+//                Point2D p = new Point2D(evt.getX(), evt.getY());
+                evt.consume();
+            }
+        });
+
+        return result;
+    }
+
+    @Override
+    public void start(Stage primaryStage) throws MalformedURLException {
+        Region zoomTarget = createContent();
+        zoomTarget.setPrefSize(1000, 1000);
+        zoomTarget.setOnDragDetected(evt -> {
+            Node target = (Node) evt.getTarget();
+            while (target != zoomTarget && target != null) {
+                target = target.getParent();
+            }
+            if (target != null) {
+                target.startFullDrag();
+            }
+        });
+
+        Group group = new Group(zoomTarget);
+
+        // stackpane for centering the content, in case the ScrollPane viewport
+        // is larget than zoomTarget
+        StackPane content = new StackPane(group);
+        group.layoutBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
+            // keep it at least as large as the content
+            content.setMinWidth(newBounds.getWidth());
+            content.setMinHeight(newBounds.getHeight());
+        });
+
+        ScrollPane scrollPane = new ScrollPane(content);
+        scrollPane.setPannable(true);
+        scrollPane.viewportBoundsProperty().addListener((observable, oldBounds, newBounds) -> {
+            // use vieport size, if not too small for zoomTarget
+            content.setPrefSize(newBounds.getWidth(), newBounds.getHeight());
+        });
+
+        content.setOnScroll(evt -> {
+            if (evt.isControlDown()) {
+                evt.consume();
+
+                final double zoomFactor = evt.getDeltaY() > 0 ? 1.2 : 1 / 1.2;
+
+                Bounds groupBounds = group.getLayoutBounds();
+                final Bounds viewportBounds = scrollPane.getViewportBounds();
+
+                // calculate pixel offsets from [0, 1] range
+                double valX = scrollPane.getHvalue() * (groupBounds.getWidth() - viewportBounds.getWidth());
+                double valY = scrollPane.getVvalue() * (groupBounds.getHeight() - viewportBounds.getHeight());
+
+                // convert content coordinates to zoomTarget coordinates
+                Point2D posInZoomTarget = zoomTarget.parentToLocal(group.parentToLocal(new Point2D(evt.getX(), evt.getY())));
+
+                // calculate adjustment of scroll position (pixels)
+                Point2D adjustment = zoomTarget.getLocalToParentTransform().deltaTransform(posInZoomTarget.multiply(zoomFactor - 1));
+
+                // do the resizing
+                zoomTarget.setScaleX(zoomFactor * zoomTarget.getScaleX());
+                zoomTarget.setScaleY(zoomFactor * zoomTarget.getScaleY());
+
+                // refresh ScrollPane scroll positions & content bounds
+                scrollPane.layout();
+
+                // convert back to [0, 1] range
+                // (too large/small values are automatically corrected by ScrollPane)
+                groupBounds = group.getLayoutBounds();
+                scrollPane.setHvalue((valX + adjustment.getX()) / (groupBounds.getWidth() - viewportBounds.getWidth()));
+                scrollPane.setVvalue((valY + adjustment.getY()) / (groupBounds.getHeight() - viewportBounds.getHeight()));
+            }
+        });
+
+        StackPane left = new StackPane(new Label("Left Menu"));
+        SplitPane root = new SplitPane(left, scrollPane);
+
+        Scene scene = new Scene(root, 800, 600);
+
+        primaryStage.setScene(scene);
+        primaryStage.show();
+    }
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/application.css b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/application.css
new file mode 100644 (file)
index 0000000..517666b
--- /dev/null
@@ -0,0 +1,3 @@
+.highlight {
+    -fx-effect: dropshadow(three-pass-box, red, 4, 4, 0, 0);
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/examples/package-info.java
new file mode 100644 (file)
index 0000000..13083e2
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.jfx.examples;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/package-info.java
new file mode 100644 (file)
index 0000000..975f25d
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.jfx;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/DummyTrace.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/DummyTrace.java
new file mode 100644 (file)
index 0000000..f4197a9
--- /dev/null
@@ -0,0 +1,71 @@
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+
+public class DummyTrace extends TmfTrace {
+
+//    private static final long START_TIME = 100000;
+//    private static final long END_TIME   = 200000;
+
+    private static final long START_TIME = 1332170682440133097L;
+    private static final long END_TIME   = 1332170692664579801L;
+
+    public DummyTrace() {
+        setTimeRange(TimeRange.of(START_TIME, END_TIME).toTmfTimeRange());
+    }
+
+    @Override
+    public ITmfTimestamp getInitialRangeOffset() {
+        return TmfTimestamp.fromNanos((long) ((END_TIME - START_TIME) * 0.1));
+    }
+
+    // ------------------------------------------------------------------------
+    // Stuff we don't use
+    // ------------------------------------------------------------------------
+
+    @Override
+    public @Nullable IStatus validate(@Nullable IProject project, @Nullable String path) {
+        return null;
+    }
+
+    @Override
+    public @Nullable IResource getResource() {
+        return null;
+    }
+
+    @Override
+    public @Nullable ITmfLocation getCurrentLocation() {
+        return null;
+    }
+
+    @Override
+    public double getLocationRatio(@Nullable ITmfLocation location) {
+        return 0;
+    }
+
+    @Override
+    public @Nullable ITmfContext seekEvent(@Nullable ITmfLocation location) {
+        return null;
+    }
+
+    @Override
+    public @Nullable ITmfContext seekEvent(double ratio) {
+        return null;
+    }
+
+    @Override
+    public @Nullable ITmfEvent parseEvent(@Nullable ITmfContext context) {
+        return null;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/LttngScopeApplication.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/LttngScopeApplication.java
new file mode 100644 (file)
index 0000000..1dee67b
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.NestingBoolean;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.core.timegraph.control.TimeGraphModelControl;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.application.Application;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+
+public class LttngScopeApplication extends Application {
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(@Nullable Stage primaryStage) throws Exception {
+        if (primaryStage == null) {
+            return;
+        }
+
+        /* Set up the view context */
+        ViewGroupContext viewCtx = ViewGroupContext.getCurrent();
+        ITmfTrace trace = new DummyTrace();
+
+        ITimeGraphModelProvider modelProvider = new TestModelProvider();
+        TimeGraphModelControl control = new TimeGraphModelControl(viewCtx, modelProvider);
+        TimeGraphWidget widget = new TimeGraphWidget(control, new NestingBoolean());
+        control.attachView(widget);
+        Parent root = widget.getRootNode();
+
+        primaryStage.setScene(new Scene(root));
+        primaryStage.show();
+
+        viewCtx.setCurrentTrace(trace);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider1.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider1.java
new file mode 100644 (file)
index 0000000..a02bbf5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import java.util.List;
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.TimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrow;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries.LineStyle;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import com.google.common.collect.ImmutableList;
+
+class TestModelArrowProvider1 extends TimeGraphModelArrowProvider {
+
+    public static final String SERIES_NAME = "Red";
+
+    private static final TimeGraphArrowSeries ARROW_SERIES = new TimeGraphArrowSeries(
+            SERIES_NAME,
+            new ColorDefinition(255, 0, 0),
+            LineStyle.FULL);
+
+    public TestModelArrowProvider1() {
+        super(ARROW_SERIES);
+    }
+
+    @Override
+    public TimeGraphArrowRender getArrowRender(TimeGraphTreeRender treeRender, TimeRange timeRange, @Nullable FutureTask<?> task) {
+        TimeGraphArrowSeries series = getArrowSeries();
+        List<TimeGraphTreeElement> treeElems = treeRender.getAllTreeElements();
+
+        /* Draw 3 arrows total */
+        TimeGraphEvent startEvent = new TimeGraphEvent(ts(timeRange, 0.1), treeElems.get(0));
+        TimeGraphEvent endEvent = new TimeGraphEvent(ts(timeRange, 0.3), treeElems.get(5));
+        TimeGraphArrow arrow1 = new TimeGraphArrow(startEvent, endEvent, series);
+
+        startEvent = new TimeGraphEvent(ts(timeRange, 0.2), treeElems.get(3));
+        endEvent = new TimeGraphEvent(ts(timeRange, 0.5), treeElems.get(12));
+        TimeGraphArrow arrow2 = new TimeGraphArrow(startEvent, endEvent, series);
+
+        startEvent = new TimeGraphEvent(ts(timeRange, 0.6), treeElems.get(15));
+        endEvent = new TimeGraphEvent(ts(timeRange, 0.8), treeElems.get(2));
+        TimeGraphArrow arrow3 = new TimeGraphArrow(startEvent, endEvent, series);
+
+        List<TimeGraphArrow> arrows = ImmutableList.of(arrow1, arrow2, arrow3);
+        return new TimeGraphArrowRender(timeRange, arrows);
+    }
+
+    private static long ts(TimeRange range, double ratio) {
+        return (long) (range.getDuration() * ratio + range.getStart());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider2.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelArrowProvider2.java
new file mode 100644 (file)
index 0000000..d383c00
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import java.util.List;
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.TimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrow;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries.LineStyle;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import com.google.common.collect.ImmutableList;
+
+class TestModelArrowProvider2 extends TimeGraphModelArrowProvider {
+
+    public static final String SERIES_NAME = "Green";
+
+    private static final TimeGraphArrowSeries ARROW_SERIES = new TimeGraphArrowSeries(
+            SERIES_NAME,
+            new ColorDefinition(0, 255, 0),
+            LineStyle.FULL);
+
+    public TestModelArrowProvider2() {
+        super(ARROW_SERIES);
+    }
+
+    @Override
+    public TimeGraphArrowRender getArrowRender(TimeGraphTreeRender treeRender, TimeRange timeRange, @Nullable FutureTask<?> task) {
+        TimeGraphArrowSeries series = getArrowSeries();
+        List<TimeGraphTreeElement> treeElems = treeRender.getAllTreeElements();
+
+        /* Draw 2 arrows total */
+        TimeGraphEvent startEvent = new TimeGraphEvent(ts(timeRange, 0.3), treeElems.get(6));
+        TimeGraphEvent endEvent = new TimeGraphEvent(ts(timeRange, 0.8), treeElems.get(4));
+        TimeGraphArrow arrow1 = new TimeGraphArrow(startEvent, endEvent, series);
+
+        startEvent = new TimeGraphEvent(ts(timeRange, 0.5), treeElems.get(10));
+        endEvent = new TimeGraphEvent(ts(timeRange, 0.6), treeElems.get(7));
+        TimeGraphArrow arrow2 = new TimeGraphArrow(startEvent, endEvent, series);
+
+        List<TimeGraphArrow> arrows = ImmutableList.of(arrow1, arrow2);
+        return new TimeGraphArrowRender(timeRange, arrows);
+    }
+
+    private static long ts(TimeRange range, double ratio) {
+        return (long) (range.getDuration() * ratio + range.getStart());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelProvider.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelProvider.java
new file mode 100644 (file)
index 0000000..bef8c6d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.TimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import com.google.common.collect.ImmutableList;
+
+class TestModelProvider extends TimeGraphModelProvider {
+
+    public static final String ENTRY_NAME_PREFIX = "Entry #";
+
+    private static final int NB_ENTRIES = 20;
+
+    private static final TimeGraphTreeRender TREE_RENDER;
+
+    static {
+        List<TimeGraphTreeElement> treeElements = IntStream.range(1, NB_ENTRIES)
+            .mapToObj(i -> new TimeGraphTreeElement(ENTRY_NAME_PREFIX + i, Collections.emptyList()))
+            .collect(Collectors.toList());
+        TimeGraphTreeElement rootElement = new TimeGraphTreeElement("Test", treeElements);
+        TREE_RENDER = new TimeGraphTreeRender(rootElement);
+    }
+
+    protected TestModelProvider() {
+        super("Test",
+                /* Sorting modes */
+                null,
+                /* Filter modes */
+                null,
+                /* State provider */
+                new TestModelStateProvider(),
+                /* Arrow providers */
+                ImmutableList.of(new TestModelArrowProvider1(), new TestModelArrowProvider2()));
+    }
+
+    @Override
+    public TimeGraphTreeRender getTreeRender() {
+        return TREE_RENDER;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelStateProvider.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/TestModelStateProvider.java
new file mode 100644 (file)
index 0000000..11c5e7c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016-2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.FutureTask;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.TimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.BasicTimeGraphStateInterval;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateInterval;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
+
+class TestModelStateProvider extends TimeGraphModelStateProvider {
+
+    /**
+     * The duration of each state is equal to its tree element index, multiplied
+     * by this factor.
+     */
+    public static final long DURATION_FACTOR = 100000000L;
+
+    public TestModelStateProvider() {
+        super(ImmutableList.of());
+    }
+
+    @Override
+    public TimeGraphStateRender getStateRender(TimeGraphTreeElement treeElement,
+            TimeRange timeRange, long resolution, @Nullable FutureTask<?> task) {
+
+        int entryIndex = Integer.valueOf(treeElement.getName().substring(TestModelProvider.ENTRY_NAME_PREFIX.length()));
+        long stateLength = entryIndex * DURATION_FACTOR;
+
+        List<TimeGraphStateInterval> intervals = LongStream.iterate(timeRange.getStart(), i -> i + stateLength)
+                .limit((timeRange.getDuration() / stateLength) + 1)
+                .mapToObj(startTime -> {
+                    long endTime = startTime + stateLength - 1;
+                    String name = getNextStateName();
+                    ConfigOption<ColorDefinition> color = getnextStateColor();
+                    return new BasicTimeGraphStateInterval(startTime, endTime, treeElement, name, name, color, LINE_THICKNESS, Collections.emptyMap());
+                })
+                .collect(Collectors.toList());
+
+        return new TimeGraphStateRender(timeRange, treeElement, intervals);
+    }
+
+    private static final ConfigOption<LineThickness> LINE_THICKNESS = new ConfigOption<>(LineThickness.NORMAL);
+    private static final Iterator<String> STATE_NAMES = Iterators.cycle("State 1", "State 2");
+    private static final Iterator<ConfigOption<ColorDefinition>> STATE_COLORS = Iterators.cycle(
+            new ConfigOption<>(new ColorDefinition(128, 0, 0)),
+            new ConfigOption<>(new ColorDefinition(0, 0, 128)));
+
+    private static synchronized String getNextStateName() {
+        return STATE_NAMES.next();
+    }
+
+    private static synchronized ConfigOption<ColorDefinition> getnextStateColor() {
+        return STATE_COLORS.next();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp.java
new file mode 100644 (file)
index 0000000..ee708f7
--- /dev/null
@@ -0,0 +1,157 @@
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.ScrollPane.ScrollBarPolicy;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Line;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeLineCap;
+import javafx.stage.Stage;
+
+public class UiModelApp extends Application {
+
+    /* Value where a raw Pane starts breaking down */
+    private static final double PANE_WIDTH = 1000000000.0;
+    /* Maximum pane width (roughly a 1-year trace at 0.01 nanos/pixel) */
+//    private static final double PANE_WIDTH = 1e16;
+
+    private static final double MAX_WIDTH = 1000000.0;
+
+    private static final double ENTRY_HEIGHT = 20;
+    private static final Color BACKGROUD_LINES_COLOR = requireNonNull(Color.LIGHTBLUE);
+    private static final String BACKGROUND_STYLE = "-fx-background-color: rgba(255, 255, 255, 255);"; //$NON-NLS-1$
+
+    private static final double SELECTION_STROKE_WIDTH = 1;
+    private static final Color SELECTION_STROKE_COLOR = requireNonNull(Color.BLUE);
+    private static final Color SELECTION_FILL_COLOR = requireNonNull(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.4));
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(@Nullable Stage primaryStage) throws Exception {
+        if (primaryStage == null) {
+            return;
+        }
+
+        /* Layers */
+        Group backgroundLayer = new Group();
+        Group statesLayer = new Group();
+        Group selectionLayer = new Group();
+
+        /* Top-level */
+        Pane contentPane = new Pane(backgroundLayer, statesLayer, selectionLayer);
+        contentPane.minWidthProperty().bind(contentPane.prefWidthProperty());
+        contentPane.maxWidthProperty().bind(contentPane.prefWidthProperty());
+        contentPane.setStyle(BACKGROUND_STYLE);
+
+        ScrollPane scrollPane = new ScrollPane(contentPane);
+        scrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
+        scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
+        scrollPane.setFitToHeight(true);
+        scrollPane.setFitToWidth(true);
+        scrollPane.setPannable(true);
+
+        BorderPane borderPane = new BorderPane(scrollPane);
+
+        primaryStage.setScene(new Scene(borderPane));
+        primaryStage.show();
+        primaryStage.setHeight(500);
+        primaryStage.setWidth(500);
+
+        contentPane.setPrefHeight(200);
+        contentPane.setPrefWidth(PANE_WIDTH);
+
+        drawBackground(backgroundLayer, contentPane);
+        drawRectangles(statesLayer);
+        drawSelection(selectionLayer, contentPane);
+    }
+
+    private static void drawBackground(Group parent, Pane content) {
+        DoubleStream.iterate(ENTRY_HEIGHT / 2, i -> i + ENTRY_HEIGHT).limit(10)
+                .mapToObj(y -> {
+                    Line line = new Line();
+                    line.setStartX(0);
+                    line.endXProperty().bind(content.widthProperty());
+//                    line.setEndX(MAX_WIDTH);
+                    line.setStartY(y);
+                    line.setEndY(y);
+
+                    line.setStroke(BACKGROUD_LINES_COLOR);
+                    line.setStrokeWidth(1.0);
+                    return line;
+                })
+                .forEach(parent.getChildren()::add);
+    }
+
+    private static void drawRectangles(Group target) {
+        DrawnRectangle rectangle1 = new DrawnRectangle(1, PANE_WIDTH - 20, 2);
+        rectangle1.setFill(Color.GREEN);
+
+        DrawnRectangle rectangle2 = new DrawnRectangle(20, 2000, 5);
+        rectangle2.setFill(Color.RED);
+
+        DrawnRectangle rectangle3 = new DrawnRectangle(PANE_WIDTH - 2000, PANE_WIDTH - 100, 6);
+        rectangle3.setFill(Color.ORANGE);
+
+        target.getChildren().addAll(rectangle1, rectangle2, rectangle3);
+    }
+
+
+    private static class DrawnRectangle extends Rectangle {
+
+        private static final double THICKNESS = 10;
+
+        public DrawnRectangle(double startX, double endX, int entryIndex) {
+            double y0 = entryIndex * ENTRY_HEIGHT;
+            double yOffset = (ENTRY_HEIGHT - THICKNESS) / 2;
+
+//            double width = endX - startX;
+            double width = Math.min(endX - startX, MAX_WIDTH);
+
+            setX(startX);
+            setY(y0 + yOffset);
+            setWidth(width);
+            setHeight(THICKNESS);
+        }
+    }
+
+    private static void drawSelection(Group parent, Pane content) {
+        Rectangle selection1 = new Rectangle();
+        Rectangle selection2 = new Rectangle();
+
+        Stream.of(selection1, selection2).forEach(rect -> {
+            rect.setMouseTransparent(true);
+
+            rect.setStroke(SELECTION_STROKE_COLOR);
+            rect.setStrokeWidth(SELECTION_STROKE_WIDTH);
+            rect.setStrokeLineCap(StrokeLineCap.ROUND);
+            rect.setFill(SELECTION_FILL_COLOR);
+
+            rect.yProperty().bind(JfxUtils.ZERO_PROPERTY);
+            rect.heightProperty().bind(content.heightProperty());
+        });
+
+        selection1.setX(200);
+        selection1.setWidth(100);
+
+        selection2.setX(PANE_WIDTH - 1000);
+        selection2.setWidth(500);
+
+        parent.getChildren().addAll(selection1, selection2);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp2.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/UiModelApp2.java
new file mode 100644 (file)
index 0000000..bdc5472
--- /dev/null
@@ -0,0 +1,175 @@
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.stream.DoubleStream;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+
+import javafx.application.Application;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.ScrollPane.ScrollBarPolicy;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Line;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeLineCap;
+import javafx.stage.Stage;
+
+public class UiModelApp2 extends Application {
+
+    /* Value where a raw Pane starts breaking down */
+//    private static final double PANE_WIDTH = 1000000000.0;
+    /* Maximum supportable width due to double precision */
+    private static final double PANE_WIDTH = 1e16;
+
+    private static final double MAX_WIDTH = 1000000.0;
+
+    private static final double ENTRY_HEIGHT = 20;
+    private static final Color BACKGROUD_LINES_COLOR = requireNonNull(Color.LIGHTBLUE);
+    private static final String BACKGROUND_STYLE = "-fx-background-color: rgba(255, 255, 255, 255);"; //$NON-NLS-1$
+
+    private static final double SELECTION_STROKE_WIDTH = 1;
+    private static final Color SELECTION_STROKE_COLOR = requireNonNull(Color.BLUE);
+    private static final Color SELECTION_FILL_COLOR = requireNonNull(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.4));
+
+    public static void main(String[] args) {
+        launch(args);
+    }
+
+    @Override
+    public void start(@Nullable Stage primaryStage) throws Exception {
+        if (primaryStage == null) {
+            return;
+        }
+
+        /* Layers */
+        Group backgroundLayer = new Group();
+        Group statesLayer = new Group();
+        Group selectionLayer = new Group();
+
+        Pane paintingPane = new Pane(backgroundLayer, statesLayer, selectionLayer);
+        paintingPane.setStyle(BACKGROUND_STYLE);
+
+        /* Top-level */
+        Pane contentPane = new Pane(paintingPane);
+        contentPane.minWidthProperty().bind(contentPane.prefWidthProperty());
+        contentPane.maxWidthProperty().bind(contentPane.prefWidthProperty());
+
+        ScrollPane scrollPane = new ScrollPane(contentPane);
+        scrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
+        scrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
+        scrollPane.setFitToHeight(true);
+        scrollPane.setFitToWidth(true);
+        scrollPane.setPannable(true);
+
+        BorderPane borderPane = new BorderPane(scrollPane);
+
+        primaryStage.setScene(new Scene(borderPane));
+        primaryStage.show();
+        primaryStage.setHeight(500);
+        primaryStage.setWidth(500);
+
+        contentPane.setPrefHeight(200);
+        contentPane.setPrefWidth(PANE_WIDTH);
+
+        /* Bind painting pane's height to the content pane */
+        paintingPane.minHeightProperty().bind(contentPane.minHeightProperty());
+        paintingPane.prefHeightProperty().bind(contentPane.prefHeightProperty());
+        paintingPane.maxHeightProperty().bind(contentPane.maxHeightProperty());
+
+        /* We set painting's pane width programmatically */
+        paintingPane.minWidthProperty().bind(paintingPane.prefWidthProperty());
+        paintingPane.maxWidthProperty().bind(paintingPane.prefWidthProperty());
+
+        paintingPane.setPrefWidth(2000);
+        double x = PANE_WIDTH - 2000;
+        System.out.println(x);
+        paintingPane.relocate(x, 0);
+
+        drawBackground(backgroundLayer, paintingPane);
+        drawRectangles(statesLayer);
+        drawSelection(selectionLayer, paintingPane);
+    }
+
+    private static void drawBackground(Group parent, Pane parentPane) {
+        DoubleStream.iterate(ENTRY_HEIGHT / 2, i -> i + ENTRY_HEIGHT).limit(10)
+                .mapToObj(y -> {
+                    Line line = new Line();
+                    line.setStartX(0);
+                    line.endXProperty().bind(parentPane.widthProperty());
+//                    line.setEndX(MAX_WIDTH);
+                    line.setStartY(y);
+                    line.setEndY(y);
+
+                    line.setStroke(BACKGROUD_LINES_COLOR);
+                    line.setStrokeWidth(1.0);
+                    return line;
+                })
+                .forEach(parent.getChildren()::add);
+    }
+
+    private static void drawRectangles(Group target) {
+//        DrawnRectangle rectangle1 = new DrawnRectangle(1, PANE_WIDTH - 20, 2);
+//        rectangle1.setFill(Color.GREEN);
+//
+//        DrawnRectangle rectangle2 = new DrawnRectangle(20, 2000, 5);
+//        rectangle2.setFill(Color.RED);
+//
+//        DrawnRectangle rectangle3 = new DrawnRectangle(PANE_WIDTH - 2000, PANE_WIDTH - 100, 6);
+        DrawnRectangle rectangle3 = new DrawnRectangle(100, 1900, 6);
+        rectangle3.setFill(Color.ORANGE);
+
+//        target.getChildren().addAll(rectangle1, rectangle2, rectangle3);
+        target.getChildren().addAll(rectangle3);
+    }
+
+
+    private static class DrawnRectangle extends Rectangle {
+
+        private static final double THICKNESS = 10;
+
+        public DrawnRectangle(double startX, double endX, int entryIndex) {
+            double y0 = entryIndex * ENTRY_HEIGHT;
+            double yOffset = (ENTRY_HEIGHT - THICKNESS) / 2;
+
+//            double width = endX - startX;
+            double width = Math.min(endX - startX, MAX_WIDTH);
+
+            setX(startX);
+            setY(y0 + yOffset);
+            setWidth(width);
+            setHeight(THICKNESS);
+        }
+    }
+
+    private static void drawSelection(Group parent, Pane content) {
+//        Rectangle selection1 = new Rectangle();
+        Rectangle selection2 = new Rectangle();
+
+        Stream.of(selection2).forEach(rect -> {
+            rect.setMouseTransparent(true);
+
+            rect.setStroke(SELECTION_STROKE_COLOR);
+            rect.setStrokeWidth(SELECTION_STROKE_WIDTH);
+            rect.setStrokeLineCap(StrokeLineCap.ROUND);
+            rect.setFill(SELECTION_FILL_COLOR);
+
+            rect.yProperty().bind(JfxUtils.ZERO_PROPERTY);
+            rect.heightProperty().bind(content.heightProperty());
+        });
+//
+//        selection1.setX(200);
+//        selection1.setWidth(100);
+
+        selection2.setX(500);
+        selection2.setWidth(500);
+
+        parent.getChildren().addAll(selection2);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/jfx/testapp/package-info.java
new file mode 100644 (file)
index 0000000..62ed206
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.jfx.testapp;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/DebugOptions.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/DebugOptions.java
new file mode 100644 (file)
index 0000000..a4fa48a
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline;
+
+import static java.util.Objects.requireNonNull;
+
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.paint.Color;
+import javafx.scene.paint.CycleMethod;
+import javafx.scene.paint.LinearGradient;
+import javafx.scene.paint.Paint;
+import javafx.scene.paint.Stop;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+
+/**
+ * Debug options for the {@link TimeGraphWidget}. Advanced users or unit
+ * tests might want to modify these.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class DebugOptions {
+
+    /**
+     * Constructor using the default options
+     */
+    public DebugOptions() {
+        recomputeEllipsisWidth();
+    }
+
+    // ------------------------------------------------------------------------
+    // General options
+    // ------------------------------------------------------------------------
+
+    /**
+     * Painting flag. Indicates if automatic redrawing of the view is enabled
+     */
+    public final ConfigOption<Boolean> isPaintingEnabled = new ConfigOption<>(true);
+
+    /**
+     * Entry padding. Number of tree elements to print above *and* below the
+     * visible range
+     */
+    public final ConfigOption<Integer> entryPadding = new ConfigOption<>(5);
+
+    /**
+     * How much "padding" around the current visible window, on the left and
+     * right, should be pre-rendered. Expressed as a fraction of the current
+     * window (for example, 1.0 would render one "page" on each side).
+     */
+    public final ConfigOption<Double> renderRangePadding = new ConfigOption<>(0.1);
+
+    /**
+     * Time between UI updates, in milliseconds
+     */
+    public final ConfigOption<Integer> uiUpdateDelay = new ConfigOption<>(250);
+
+    /**
+     * Whether the view should respond to vertical or horizontal scrolling
+     * actions.
+     */
+    public final ConfigOption<Boolean> isScrollingListenersEnabled = new ConfigOption<>(true);
+
+    // ------------------------------------------------------------------------
+    // Loading overlay
+    // ------------------------------------------------------------------------
+
+    public final ConfigOption<Boolean> isLoadingOverlayEnabled = new ConfigOption<>(true);
+
+    public final ConfigOption<Color> loadingOverlayColor = new ConfigOption<>(requireNonNull(Color.GRAY));
+
+    public final ConfigOption<Double> loadingOverlayFullOpacity = new ConfigOption<>(0.3);
+    public final ConfigOption<Double> loadingOverlayTransparentOpacity = new ConfigOption<>(0.0);
+
+    public final ConfigOption<Double> loadingOverlayFadeInDuration = new ConfigOption<>(1000.0);
+    public final ConfigOption<Double> loadingOverlayFadeOutDuration = new ConfigOption<>(100.0);
+
+    // ------------------------------------------------------------------------
+    // Zoom animation
+    // ------------------------------------------------------------------------
+
+    /**
+     * The zoom animation duration, which is the amount of milliseconds it takes
+     * to complete the zoom animation (smaller number means a faster animation).
+     */
+    public final ConfigOption<Integer> zoomAnimationDuration = new ConfigOption<>(50);
+
+    /**
+     * Each zoom action (typically, one mouse-scroll == one zoom action) will
+     * increase or decrease the current visible time range by this factor.
+     */
+    public final ConfigOption<Double> zoomStep = new ConfigOption<>(0.08);
+
+    /**
+     * Each zoom action will be centered on the center of the selection if it's
+     * currently visible.
+     */
+    public final ConfigOption<Boolean> zoomPivotOnSelection = new ConfigOption<>(true);
+
+    /**
+     * Each zoom action will be centered on the current mouse position if the
+     * zoom action originates from a mouse event. If zoomPivotOnSelection is
+     * enabled, it has priority.
+     */
+    public final ConfigOption<Boolean> zoomPivotOnMousePosition = new ConfigOption<>(true);
+
+    // ------------------------------------------------------------------------
+    // State rectangles
+    // ------------------------------------------------------------------------
+
+    public final ConfigOption<Double> stateIntervalOpacity = new ConfigOption<>(1.0);
+
+    public final ConfigOption<Paint> multiStatePaint;
+    {
+        Stop[] stops = new Stop[] { new Stop(0, Color.BLACK), new Stop(1, Color.WHITE) };
+        LinearGradient lg = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, stops);
+        multiStatePaint = new ConfigOption<>(lg);
+    }
+
+    // ------------------------------------------------------------------------
+    // State labels
+    // ------------------------------------------------------------------------
+
+    public final ConfigOption<Font> stateLabelFont = new ConfigOption<Font>(requireNonNull(new Text().getFont())) {
+        @Override
+        public void set(Font value) {
+            super.set(value);
+            recomputeEllipsisWidth();
+        }
+    };
+
+    public static final String ELLIPSIS_STRING = "..."; //$NON-NLS-1$
+
+    private transient double fEllipsisWidth;
+
+    public double getEllipsisWidth() {
+        return fEllipsisWidth;
+    }
+
+    private synchronized void recomputeEllipsisWidth() {
+        Text text = new Text(ELLIPSIS_STRING);
+        text.setFont(stateLabelFont.get());
+        text.applyCss();
+        fEllipsisWidth = text.getLayoutBounds().getWidth();
+    }
+
+    // ------------------------------------------------------------------------
+    // Tooltips
+    // ------------------------------------------------------------------------
+
+    public final ConfigOption<Font> toolTipFont = new ConfigOption<>(Font.font(14));
+
+    public final ConfigOption<Color> toolTipFontFill = new ConfigOption<>(requireNonNull(Color.WHITE));
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/ITimelineWidget.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/ITimelineWidget.java
new file mode 100644 (file)
index 0000000..3885c67
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.scene.Parent;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.SplitPane;
+import javafx.scene.shape.Rectangle;
+
+public interface ITimelineWidget {
+
+    String getName();
+
+    Parent getRootNode();
+
+    void dispose();
+
+    /**
+     * Many widgets will use a SplitPane to separate a tree or info pane on the
+     * left, and a time-based pane on the right. This method is used to return
+     * this pane so the manager can apply common operations on them.
+     *
+     * @return The horizontal split pane, or 'null' if the widget doesn't use
+     *         one and uses the full horizontal width of the view.
+     */
+    @Nullable SplitPane getSplitPane();
+
+    @Nullable ScrollPane getTimeBasedScrollPane();
+
+    @Nullable Rectangle getSelectionRectangle();
+
+    @Nullable Rectangle getOngoingSelectionRectangle();
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/Messages.java
new file mode 100644 (file)
index 0000000..992682f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@SuppressWarnings("javadoc")
+@NonNullByDefault({})
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String timelineViewName;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineManager.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineManager.java
new file mode 100644 (file)
index 0000000..956ce4f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.lttng.scope.tmf2.views.core.NestingBoolean;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.core.timegraph.control.TimeGraphModelControl;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProviderFactory;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.TimeGraphModelProviderManager;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.TimeGraphModelProviderManager.TimeGraphOutput;
+import org.lttng.scope.tmf2.views.core.timegraph.view.TimeGraphModelView;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.SplitPane;
+import javafx.scene.shape.Rectangle;
+
+public class TimelineManager implements TimeGraphOutput {
+
+    private static final double INITIAL_DIVIDER_POSITION = 0.2;
+
+    private final TimelineView fView;
+    private final ViewGroupContext fViewContext;
+    private final Set<ITimelineWidget> fWidgets = new HashSet<>();
+
+    private final NestingBoolean fHScrollListenerStatus = new NestingBoolean();
+
+    private final DoubleProperty fDividerPosition = new SimpleDoubleProperty(INITIAL_DIVIDER_POSITION);
+    private final DoubleProperty fHScrollValue = new SimpleDoubleProperty(0);
+
+    /* Properties to sync ongoing selection rectangles */
+    private final BooleanProperty fSelectionVisible = new SimpleBooleanProperty(true);
+    private final DoubleProperty fOngoingSelectionX = new SimpleDoubleProperty();
+    private final DoubleProperty fOngoingSelectionWidth = new SimpleDoubleProperty();
+    private final BooleanProperty fOngoingSelectionVisible = new SimpleBooleanProperty(false);
+
+    public TimelineManager(TimelineView view, ViewGroupContext viewContext) {
+        fView = view;
+        fViewContext = viewContext;
+        TimeGraphModelProviderManager.instance().registerOutput(this);
+    }
+
+    @Override
+    public void providerRegistered(ITimeGraphModelProviderFactory factory) {
+        /* Instantiate a widget for this provider type */
+        ITimeGraphModelProvider provider = factory.get();
+        TimeGraphModelControl control = new TimeGraphModelControl(fViewContext, provider);
+        TimeGraphWidget viewer = new TimeGraphWidget(control, fHScrollListenerStatus);
+        control.attachView(viewer);
+
+        /*
+         * Bind properties in a runLater() statement, so that the UI views have
+         * already been initialized. The divider position, for instance, only
+         * has effect after the view is visible.
+         */
+        Platform.runLater(() -> {
+            /* Bind divider position, if applicable */
+            SplitPane splitPane = viewer.getSplitPane();
+            splitPane.getDividers().get(0).positionProperty().bindBidirectional(fDividerPosition);
+
+            /* Bind h-scrollbar position */
+            ScrollPane scrollPane = viewer.getTimeBasedScrollPane();
+            scrollPane.hvalueProperty().bindBidirectional(fHScrollValue);
+
+            /* Bind the selection rectangles together */
+            Rectangle selectionRect = viewer.getSelectionRectangle();
+            if (selectionRect != null) {
+                selectionRect.visibleProperty().bindBidirectional(fSelectionVisible);
+            }
+            Rectangle ongoingSelectionRect = viewer.getOngoingSelectionRectangle();
+            if (ongoingSelectionRect != null) {
+                ongoingSelectionRect.layoutXProperty().bindBidirectional(fOngoingSelectionX);
+                ongoingSelectionRect.widthProperty().bindBidirectional(fOngoingSelectionWidth);
+                ongoingSelectionRect.visibleProperty().bindBidirectional(fOngoingSelectionVisible);
+            }
+        });
+
+        fWidgets.add(viewer);
+        fView.addWidget(viewer.getRootNode());
+
+    }
+
+    public void dispose() {
+        TimeGraphModelProviderManager.instance().unregisterOutput(this);
+
+        fWidgets.forEach(w -> {
+            if (w instanceof TimeGraphModelView) {
+                /*
+                 * TimeGraphModelView's are disposed via their control
+                 *
+                 * FIXME Do this better.
+                 */
+                ((TimeGraphModelView) w).getControl().dispose();
+            } else {
+                w.dispose();
+            }
+        });
+        fWidgets.clear();
+    }
+
+    void resetInitialSeparatorPosition() {
+        fDividerPosition.set(INITIAL_DIVIDER_POSITION);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineView.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/TimelineView.java
new file mode 100644 (file)
index 0000000..0a074ba
--- /dev/null
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.ui.timeline;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tracecompass.tmf.ui.views.TmfView;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+
+import javafx.embed.swt.FXCanvas;
+import javafx.geometry.Orientation;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.SplitPane;
+
+public class TimelineView extends TmfView {
+
+    public static final String VIEW_ID = "org.lttng.scope.views.timeline"; //$NON-NLS-1$
+
+    private static final String VIEW_NAME = requireNonNull(Messages.timelineViewName);
+
+    private @Nullable TimelineManager fManager;
+    private @Nullable SplitPane fSplitPane;
+
+    public TimelineView() {
+        super(VIEW_NAME);
+    }
+
+    @Override
+    public void createPartControl(@Nullable Composite parent) {
+        if (parent == null) {
+            return;
+        }
+
+        FXCanvas fxCanvas = new FXCanvas(parent, SWT.NONE);
+        SplitPane sp = new SplitPane();
+        sp.setOrientation(Orientation.VERTICAL);
+        fSplitPane = sp;
+
+        TimelineManager manager = new TimelineManager(this, ViewGroupContext.getCurrent());
+        fManager = manager;
+
+        fxCanvas.setScene(new Scene(sp));
+
+        /*
+         * Set the initial divider positions. Has to be done *after* the
+         * Stage/Scene is initialized.
+         */
+        manager.resetInitialSeparatorPosition();
+    }
+
+    void addWidget(Node node) {
+        SplitPane sp = requireNonNull(fSplitPane);
+        JfxUtils.runOnMainThread(() -> sp.getItems().add(node));
+    }
+
+    @Override
+    public void dispose() {
+        if (fManager != null) {
+            fManager.dispose();
+        }
+        if (fSplitPane != null) {
+            fSplitPane.getItems().clear();
+        }
+    }
+
+    @Override
+    public void setFocus() {
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/messages.properties
new file mode 100644 (file)
index 0000000..dd76d91
--- /dev/null
@@ -0,0 +1,10 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+timelineViewName = Timeline
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/package-info.java
new file mode 100644 (file)
index 0000000..e5f2c74
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LatestTaskExecutor.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LatestTaskExecutor.java
new file mode 100644 (file)
index 0000000..fc3d264
--- /dev/null
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import javafx.concurrent.Task;
+
+public class LatestTaskExecutor {
+
+    private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(2);
+//    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
+
+    /**
+     * The latest job that was schedule in this queue.
+     */
+    private WeakReference<@Nullable Task<?>> fLatestTask = new WeakReference<>(null);
+
+    public LatestTaskExecutor() {
+    }
+
+    public synchronized void schedule(Task<?> newTask) {
+        Task<?> latestJob = fLatestTask.get();
+        if (latestJob != null) {
+            /*
+             * Cancel the existing task. Here's hoping it cooperates and ends
+             * quickly!
+             */
+            latestJob.cancel(false);
+        }
+
+        /* Start the new job */
+        fLatestTask = new WeakReference<>(newTask);
+        EXECUTOR.submit(newTask);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LoadingOverlay.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/LoadingOverlay.java
new file mode 100644 (file)
index 0000000..8191cb8
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+
+import javafx.animation.FadeTransition;
+import javafx.scene.shape.Rectangle;
+import javafx.util.Duration;
+
+class LoadingOverlay extends Rectangle {
+
+    private final DebugOptions fOpts;
+
+    private @Nullable FadeTransition fCurrentFadeIn;
+    private @Nullable FadeTransition fCurrentFadeOut;
+
+    public LoadingOverlay(DebugOptions opts) {
+        fOpts = opts;
+
+        /*
+         * Set the fill (color) by binding the property to the corresponding
+         * config option. That way if the use changes the configured value, this
+         * overlay will follow.
+         */
+        fillProperty().bind(fOpts.loadingOverlayColor);
+
+        /*
+         * The opacity property on the other hand will change through normal
+         * operation of the overlay. We are just setting the initial value here,
+         * no permanent bind.
+         */
+        setOpacity(fOpts.loadingOverlayTransparentOpacity.get());
+
+        /*
+         * The overlay should not catch mouse events. Note we could use
+         * .setPickOnBounds(false) if we wanted to handle events but also allow
+         * them to go "through".
+         */
+        setMouseTransparent(true);
+    }
+
+    public synchronized void fadeIn() {
+        if (fCurrentFadeIn != null) {
+            /* We're already fading in, let it continue. */
+            return;
+        }
+        if (fCurrentFadeOut != null) {
+            /*
+             * Don't use stop() because that would revert to the initial opacity
+             * right away.
+             */
+            fCurrentFadeOut.pause();
+            fCurrentFadeOut = null;
+        }
+
+        double fullOpacity = fOpts.loadingOverlayFullOpacity.get();
+        double fullFadeInDuration = fOpts.loadingOverlayFadeInDuration.get();
+        double startOpacity = getOpacity();
+
+        /* Do a rule-of-three to determine the duration of fade-in we need. */
+        double neededDuration = ((fullOpacity - startOpacity) / fullOpacity) * fullFadeInDuration;
+        FadeTransition fadeIn = new FadeTransition(new Duration(neededDuration), this);
+        fadeIn.setFromValue(startOpacity);
+        fadeIn.setToValue(fullOpacity);
+        fadeIn.play();
+        fCurrentFadeIn = fadeIn;
+    }
+
+    public synchronized void fadeOut() {
+        if (fCurrentFadeOut != null) {
+            /* We're already fading out, let it continue. */
+            return;
+        }
+        if (fCurrentFadeIn != null) {
+            fCurrentFadeIn.pause();
+            fCurrentFadeIn = null;
+        }
+
+        double fullOpacity = fOpts.loadingOverlayFullOpacity.get();
+        double transparentOpacity = fOpts.loadingOverlayTransparentOpacity.get();
+        double fullFadeOutDuration = fOpts.loadingOverlayFadeOutDuration.get();
+        double startOpacity = getOpacity();
+
+        /* Do a rule-of-three to determine the duration of fade-in we need. */
+        double neededDuration = (startOpacity / fullOpacity) * fullFadeOutDuration;
+        FadeTransition fadeOut = new FadeTransition(new Duration(neededDuration), this);
+        fadeOut.setFromValue(startOpacity);
+        fadeOut.setToValue(transparentOpacity);
+        fadeOut.play();
+        fCurrentFadeOut = fadeOut;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/Messages.java
new file mode 100644 (file)
index 0000000..ebbdffb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@SuppressWarnings("javadoc")
+@NonNullByDefault({})
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String statePropertyElement;
+    public static String statePropertyStateName;
+    public static String statePropertyStartTime;
+    public static String statePropertyEndTime;
+    public static String statePropertyDuration;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/PeriodicRedrawTask.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/PeriodicRedrawTask.java
new file mode 100644 (file)
index 0000000..5cc5894
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+
+/**
+ * It was quickly determined that having mouse listeners start repaint tasks
+ * directly is not a good solution, since simply enqueuing a job can potentially
+ * take time, and thus makes scrolling sluggish and annoying to use.
+ *
+ * Instead, a separate thread can look periodically if the current view's
+ * position has changed since its last check, and queue UI updates as needed.
+ *
+ * This class implements such thread, as a {@link TimerTask}.
+ *
+ * @author Alexandre Montplaisir
+ */
+class PeriodicRedrawTask extends TimerTask {
+
+    /**
+     * Sequence number attached to each redraw operation. Can be used for
+     * tracing/analysis.
+     */
+    private final AtomicLong fTaskSeq = new AtomicLong();
+    private final TimeGraphWidget fViewer;
+
+    private TimeRange fPreviousHorizontalPos = ViewGroupContext.UNINITIALIZED_RANGE;
+    private VerticalPosition fPreviousVerticalPosition = VerticalPosition.UNINITIALIZED_VP;
+
+    private volatile boolean fForceRedraw = false;
+
+    public PeriodicRedrawTask(TimeGraphWidget viewer) {
+        fViewer = viewer;
+    }
+
+    @Override
+    public void run() {
+        if (!fViewer.getDebugOptions().isPaintingEnabled.get()) {
+            return;
+        }
+
+        TimeRange currentHorizontalPos = fViewer.getControl().getViewContext().getCurrentVisibleTimeRange();
+        VerticalPosition currentVerticalPos = fViewer.getCurrentVerticalPosition();
+
+        boolean movedHorizontally = !currentHorizontalPos.equals(fPreviousHorizontalPos);
+        boolean movedVertically = !currentVerticalPos.equals(fPreviousVerticalPosition);
+
+        if (fForceRedraw) {
+            fForceRedraw = false;
+            /* Then skip the next checks */
+        } else {
+            /*
+             * Skip painting if the previous position is the exact same as last
+             * time. Also skip if were not yet initialized.
+             */
+            if (!movedHorizontally && !movedVertically) {
+                return;
+            }
+            if (currentHorizontalPos.equals(ViewGroupContext.UNINITIALIZED_RANGE)
+                    || currentVerticalPos.equals(VerticalPosition.UNINITIALIZED_VP)) {
+                return;
+            }
+        }
+
+        fPreviousHorizontalPos = currentHorizontalPos;
+        fPreviousVerticalPosition = currentVerticalPos;
+
+        fViewer.paintArea(currentHorizontalPos, currentVerticalPos,
+                movedHorizontally, movedVertically,
+                fTaskSeq.getAndIncrement());
+    }
+
+    public void forceRedraw() {
+        fForceRedraw = true;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/StateRectangle.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/StateRectangle.java
new file mode 100644 (file)
index 0000000..8e5be37
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateInterval;
+import org.lttng.scope.tmf2.views.ui.jfx.CountingGridPane;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+
+import com.google.common.base.MoreObjects;
+
+import javafx.application.Platform;
+import javafx.geometry.Point2D;
+import javafx.scene.Node;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseButton;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.Text;
+
+/**
+ * {@link Rectangle} object used to draw states in the timegraph. It attaches
+ * the {@link TimeGraphStateInterval} that represents this state.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class StateRectangle extends Rectangle {
+
+    private final TimeGraphWidget fWidget;
+    private final int fEntryIndex;
+    private final TimeGraphStateInterval fInterval;
+
+    private @Nullable transient Paint fBaseColor;
+    private @Nullable transient Paint fSelectedColor;
+
+    private @Nullable Tooltip fTooltip = null;
+
+    /**
+     * Constructor
+     *
+     * @param viewer
+     *            The viewer in which the rectangle will be placed
+     * @param interval
+     *            The source interval model object
+     * @param entryIndex
+     *            The index of the entry to which this state belongs.
+     */
+    public StateRectangle(TimeGraphWidget viewer, TimeGraphStateInterval interval, int entryIndex) {
+        fWidget = viewer;
+        fEntryIndex = entryIndex;
+        fInterval = interval;
+
+        /*
+         * It is possible, especially when re-opening already-indexed traces,
+         * that the indexer and the state system do not report the same
+         * start/end times. Make sure to clamp the interval's bounds to the
+         * valid values.
+         */
+        TimeRange traceRange = viewer.getControl().getViewContext().getCurrentTraceFullRange();
+        long traceStart = traceRange.getStart();
+        long intervalStart = interval.getStartTime();
+        double xStart = viewer.timestampToPaneXPos(Math.max(traceStart, intervalStart));
+
+        long traceEnd = traceRange.getEnd();
+        long intervalEndTime = interval.getEndTime();
+        double xEnd = viewer.timestampToPaneXPos(Math.min(traceEnd, intervalEndTime));
+
+        double width = Math.max(1.0, xEnd - xStart) + 1.0;
+        double height = getHeightFromThickness(interval.getLineThickness().get());
+        double y = computeY(height);
+
+        setX(0);
+        setLayoutX(xStart);
+        setY(y);
+        setWidth(width);
+        setHeight(height);
+
+        double opacity = viewer.getDebugOptions().stateIntervalOpacity.get();
+        setOpacity(opacity);
+
+        updatePaint();
+
+        /* Set initial selection state and selection listener. */
+        if (this.equals(viewer.getSelectedState())) {
+            setSelected(true);
+            viewer.setSelectedState(this, false);
+        } else {
+            setSelected(false);
+        }
+
+        setOnMouseClicked(e -> {
+            if (e.getButton() != MouseButton.PRIMARY) {
+                return;
+            }
+            viewer.setSelectedState(this, true);
+        });
+
+        /*
+         * Initialize the tooltip when the mouse enters the rectangle, if it was
+         * not done previously.
+         */
+        setOnMouseEntered(e -> generateTooltip());
+    }
+
+    private void generateTooltip() {
+        if (fTooltip != null) {
+            return;
+        }
+        TooltipContents ttContents = new TooltipContents(fWidget.getDebugOptions());
+        ttContents.addTooltipRow(Messages.statePropertyElement, fInterval.getTreeElement().getName());
+        ttContents.addTooltipRow(Messages.statePropertyStateName, fInterval.getStateName());
+        ttContents.addTooltipRow(Messages.statePropertyStartTime, fInterval.getStartTime());
+        ttContents.addTooltipRow(Messages.statePropertyEndTime, fInterval.getEndTime());
+        ttContents.addTooltipRow(Messages.statePropertyDuration, fInterval.getDuration() + " ns"); //$NON-NLS-1$
+        /* Add rows corresponding to the properties from the interval */
+        Map<String, String> properties = fInterval.getProperties();
+        properties.forEach((k, v) -> ttContents.addTooltipRow(k, v));
+
+        Tooltip tt = new Tooltip();
+        tt.setGraphic(ttContents);
+        Tooltip.install(this, tt);
+        fTooltip = tt;
+    }
+
+    /**
+     * Return the model interval representing this state
+     *
+     * @return The interval model object
+     */
+    public TimeGraphStateInterval getStateInterval() {
+        return fInterval;
+    }
+
+    public void updatePaint() {
+        /* Update the color */
+        /* Set a special paint for multi-state intervals */
+        if (fInterval.isMultiState()) {
+            Paint multiStatePaint = fWidget.getDebugOptions().multiStatePaint.get();
+            fBaseColor = multiStatePaint;
+            fSelectedColor = multiStatePaint;
+        } else {
+            fBaseColor = JfxColorFactory.getColorFromDef(fInterval.getColorDefinition().get());
+            fSelectedColor = JfxColorFactory.getDerivedColorFromDef(fInterval.getColorDefinition().get());
+        }
+        setFill(fBaseColor);
+
+        /* Update the line thickness */
+        LineThickness lt = fInterval.getLineThickness().get();
+        double height = getHeightFromThickness(lt);
+        setHeight(height);
+        /* We need to adjust the y position too */
+        setY(computeY(height));
+    }
+
+    /**
+     * Compute the Y property (the Y position of the *top* of the rectangle)
+     * this rectangle should have on its pane. This takes into consideration the
+     * entry it belongs to, as well as its target height.
+     *
+     * For example, if the line thickness of the rectangle changes, its Y has to
+     * be recomputed so that the rectangle remains centered on its entry line.
+     *
+     * This method does not change the yProperty of the rectangle.
+     */
+    private double computeY(double height) {
+        double yOffset = (TimeGraphWidget.ENTRY_HEIGHT - height) / 2;
+        double y = fEntryIndex * TimeGraphWidget.ENTRY_HEIGHT + yOffset;
+        return y;
+    }
+
+    public void setSelected(boolean isSelected) {
+        if (isSelected) {
+            setFill(fSelectedColor);
+        } else {
+            setFill(fBaseColor);
+            hideTooltip();
+        }
+    }
+
+    public void showTooltip(boolean beginning) {
+        generateTooltip();
+        Tooltip tt = requireNonNull(fTooltip);
+
+        /*
+         * Show the tooltip first, then move it to the correct location. It
+         * needs to be shown for its getWidth() etc. to be populated.
+         */
+        tt.show(this, 0, 0);
+
+        Point2D position;
+        if (beginning) {
+            /* Align to the bottom-left of the rectangle, left-aligned. */
+            /* Yes, it needs to be getX() here (0), not getLayoutX(). */
+            position = this.localToScreen(getX(), getY() + getHeight());
+        } else {
+            /* Align to the bottom-right of the rectangle, right-aligned */
+            position = this.localToScreen(getX() + getWidth() - tt.getWidth(), getY() + getHeight());
+        }
+
+        tt.setAnchorX(position.getX());
+        tt.setAnchorY(position.getY());
+    }
+
+    public void hideTooltip() {
+        Tooltip tt = fTooltip;
+        if (tt != null) {
+            Platform.runLater(() -> {
+                tt.hide();
+            });
+        }
+    }
+
+    @Override
+    protected void finalize() {
+        hideTooltip();
+    }
+
+    public static double getHeightFromThickness(LineThickness lt) {
+        switch (lt) {
+        case NORMAL:
+        default:
+            return TimeGraphWidget.ENTRY_HEIGHT - 4;
+        case SMALL:
+            return TimeGraphWidget.ENTRY_HEIGHT - 8;
+        case TINY:
+            return TimeGraphWidget.ENTRY_HEIGHT - 12;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fWidget, fInterval);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        StateRectangle other = (StateRectangle) obj;
+        return Objects.equals(fWidget, other.fWidget)
+                && Objects.equals(fInterval, other.fInterval);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("interval", fInterval) //$NON-NLS-1$
+                .toString();
+    }
+
+    private static class TooltipContents extends CountingGridPane {
+
+        private final DebugOptions fOpts;
+
+        public TooltipContents(DebugOptions opts) {
+            fOpts = opts;
+        }
+
+        public void addTooltipRow(Object... objects) {
+            Node[] labels = Arrays.stream(objects)
+                    .map(Object::toString)
+                    .map(Text::new)
+                    .peek(text -> {
+                        text.fontProperty().bind(fOpts.toolTipFont);
+                        text.fillProperty().bind(fOpts.toolTipFontFill);
+                    })
+                    .toArray(Node[]::new);
+            appendRow(labels);
+        }
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidget.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidget.java
new file mode 100644 (file)
index 0000000..068d17a
--- /dev/null
@@ -0,0 +1,946 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc., Alexandre Montplaisir
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Timer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.NestingBoolean;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.control.TimeGraphModelControl;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.core.timegraph.view.TimeGraphModelView;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+import org.lttng.scope.tmf2.views.ui.timeline.ITimelineWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.TimelineView;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphArrowLayer;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphBackgroundLayer;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphDrawnEventLayer;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphSelectionLayer;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphStateLayer;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.ViewerToolBar;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import javafx.application.Platform;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.concurrent.Task;
+import javafx.event.EventHandler;
+import javafx.geometry.Orientation;
+import javafx.scene.Group;
+import javafx.scene.Parent;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.ScrollPane.ScrollBarPolicy;
+import javafx.scene.control.SplitPane;
+import javafx.scene.control.ToolBar;
+import javafx.scene.input.InputEvent;
+import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+
+/**
+ * Viewer for the {@link TimelineView}, encapsulating all the view's
+ * controls.
+ *
+ * Both ScrolledPanes's vertical scrollbars are bound together, so that they
+ * scroll together.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphWidget extends TimeGraphModelView implements ITimelineWidget {
+
+    // ------------------------------------------------------------------------
+    // Style definitions
+    // (Could eventually be moved to separate .css file?)
+    // ------------------------------------------------------------------------
+
+    public static final Color BACKGROUD_LINES_COLOR = requireNonNull(Color.LIGHTBLUE);
+
+    private static final String BACKGROUND_STYLE = "-fx-background-color: rgba(255, 255, 255, 255);"; //$NON-NLS-1$
+
+    private static final int LABEL_SIDE_MARGIN = 10;
+
+    /**
+     * Height of individual entries (text + states), including padding.
+     *
+     * TODO Make this configurable (vertical zoom feature)
+     */
+    public static final double ENTRY_HEIGHT = 20;
+
+    /** Minimum allowed zoom level, in nanos per pixel */
+    private static final double ZOOM_LIMIT = 1.0;
+
+    // ------------------------------------------------------------------------
+    // Instance fields
+    // ------------------------------------------------------------------------
+
+    private final DebugOptions fDebugOptions = new DebugOptions();
+
+    private final ScrollingContext fScrollingCtx = new ScrollingContext();
+    private final ZoomActions fZoomActions = new ZoomActions();
+
+    /*
+     * Children of the time graph pane are split into groups, so we can easily
+     * redraw or add only some of them.
+     */
+    // TODO Layer for bookmarks
+    private final TimeGraphBackgroundLayer fBackgroundLayer;
+    private final TimeGraphStateLayer fStateLayer;
+    private final TimeGraphArrowLayer fArrowLayer;
+    private final TimeGraphDrawnEventLayer fDrawnEventLayer;
+    private final TimeGraphSelectionLayer fSelectionLayer;
+    private final Group fTimeGraphLoadingOverlayGroup;
+
+    private final LatestTaskExecutor fTaskExecutor = new LatestTaskExecutor();
+
+    private final NestingBoolean fHScrollListenerStatus;
+
+    private final BorderPane fBasePane;
+    private final ToolBar fToolBar;
+    private final SplitPane fSplitPane;
+
+    private final TimeGraphWidgetTreeArea fTreeArea;
+
+    private final Pane fTimeGraphPane;
+    private final ScrollPane fTimeGraphScrollPane;
+
+    private final LoadingOverlay fTimeGraphLoadingOverlay;
+
+    private final Timer fUiUpdateTimer = new Timer();
+    private final PeriodicRedrawTask fUiUpdateTimerTask = new PeriodicRedrawTask(this);
+
+    private volatile TimeGraphTreeRender fLatestTreeRender = TimeGraphTreeRender.EMPTY_RENDER;
+
+    /** Current zoom level */
+    private final DoubleProperty fNanosPerPixel = new SimpleDoubleProperty(1.0);
+
+    /**
+     * Constructor
+     *
+     * @param control
+     *            The control for this widget. See
+     *            {@link TimeGraphModelControl}.
+     * @param hScrollListenerStatus
+     *            If the hscroll property of this widget's scrollpane is bound
+     *            with others (possibly through the
+     *            {@link ITimelineWidget#getTimeBasedScrollPane()} method), then
+     *            a common {@link NestingBoolean} should be used to track
+     *            requests to disable the hscroll listener.
+     *            <p>
+     *            If the widget is to be used stand-alone, then you can pass a "
+     *            <code>new NestingBoolean()</code> " that only this view will
+     *            use.
+     */
+    public TimeGraphWidget(TimeGraphModelControl control, NestingBoolean hScrollListenerStatus) {
+        super(control);
+        fHScrollListenerStatus = hScrollListenerStatus;
+
+        // --------------------------------------------------------------------
+        // Prepare the tree part's scene graph
+        // --------------------------------------------------------------------
+
+        fTreeArea = new TimeGraphWidgetTreeArea(ENTRY_HEIGHT, getControl().getModelRenderProvider().traceProperty());
+
+        // --------------------------------------------------------------------
+        // Prepare the time graph's part scene graph
+        // --------------------------------------------------------------------
+
+        fTimeGraphLoadingOverlay = new LoadingOverlay(fDebugOptions);
+        fTimeGraphLoadingOverlayGroup = new Group(fTimeGraphLoadingOverlay);
+
+        fTimeGraphPane = new Pane();
+        fBackgroundLayer = new TimeGraphBackgroundLayer(this, new Group());
+        fStateLayer = new TimeGraphStateLayer(this, new Group());
+        fArrowLayer = new TimeGraphArrowLayer(this, new Group());
+        fDrawnEventLayer = new TimeGraphDrawnEventLayer(this, new Group());
+        fSelectionLayer = new TimeGraphSelectionLayer(this, new Group());
+
+        /*
+         * The order of the layers is important here, it will go from back to
+         * front.
+         */
+        fTimeGraphPane.getChildren().addAll(fBackgroundLayer.getParentGroup(),
+                fStateLayer.getParentGroup(),
+                fStateLayer.getLabelGroup(),
+                fArrowLayer.getParentGroup(),
+                fDrawnEventLayer.getParentGroup(),
+                fSelectionLayer.getParentGroup(),
+                fTimeGraphLoadingOverlayGroup);
+
+        fTimeGraphPane.setStyle(BACKGROUND_STYLE);
+
+        /*
+         * We control the width of the time graph pane programmatically, so
+         * ensure that calls to setPrefWidth set the actual width right away.
+         */
+        fTimeGraphPane.minWidthProperty().bind(fTimeGraphPane.prefWidthProperty());
+        fTimeGraphPane.maxWidthProperty().bind(fTimeGraphPane.prefWidthProperty());
+
+        /*
+         * Ensure the time graph pane is always exactly the same vertical size
+         * as the tree pane, so they remain aligned.
+         */
+
+        fTimeGraphPane.minHeightProperty().bind(fTreeArea.currentHeightProperty());
+        fTimeGraphPane.prefHeightProperty().bind(fTreeArea.currentHeightProperty());
+        fTimeGraphPane.maxHeightProperty().bind(fTreeArea.currentHeightProperty());
+
+        /*
+         * Setup clipping on the timegraph pane, meaning its children outside of its
+         * actual boundary should not be rendered. For example, when the tree gets
+         * collapsed, data for hidden entries should be hidden too.
+         */
+        Rectangle clipRect = new Rectangle();
+        clipRect.setX(0);
+        clipRect.setY(0);
+        clipRect.widthProperty().bind(fTimeGraphPane.widthProperty());
+        clipRect.heightProperty().bind(fTimeGraphPane.heightProperty());
+        fTimeGraphPane.setClip(clipRect);
+
+        /*
+         * Set the loading overlay's size to always follow the size of the pane.
+         */
+        fTimeGraphLoadingOverlay.widthProperty().bind(fTimeGraphPane.widthProperty());
+        fTimeGraphLoadingOverlay.heightProperty().bind(fTimeGraphPane.heightProperty());
+
+        fTimeGraphScrollPane = new ScrollPane(fTimeGraphPane);
+        fTimeGraphScrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
+        fTimeGraphScrollPane.setHbarPolicy(ScrollBarPolicy.ALWAYS);
+        fTimeGraphScrollPane.setFitToHeight(true);
+        fTimeGraphScrollPane.setFitToWidth(true);
+        fTimeGraphScrollPane.setPannable(true);
+
+        /*
+         * Attach the scrollbar listener
+         *
+         * TODO Move this to the timeline ?
+         */
+        fTimeGraphScrollPane.hvalueProperty().addListener(fScrollingCtx.fHScrollChangeListener);
+
+        /*
+         * Mouse scroll handlers (for zooming) are attached to the time graph
+         * itself: events let through will be used by the scrollpane as normal
+         * scroll actions.
+         */
+        fTimeGraphPane.setOnScroll(fMouseScrollListener);
+
+        /*
+         * Upon reception of any mouse/keyboard event, if there's still a drawn
+         * tooltip it should be hidden.
+         */
+        fTimeGraphPane.addEventFilter(InputEvent.ANY, e -> {
+            StateRectangle selectedState = fSelectedState;
+            if (selectedState != null) {
+                selectedState.hideTooltip();
+            }
+            /* We must not consume the event here */
+        });
+
+        /* Synchronize the two scrollpanes' vertical scroll bars together */
+        fTreeArea.getVerticalScrollBar().valueProperty().bindBidirectional(fTimeGraphScrollPane.vvalueProperty());
+
+        // --------------------------------------------------------------------
+        // Prepare the top-level area
+        // --------------------------------------------------------------------
+
+        fToolBar = new ViewerToolBar(this);
+
+        fSplitPane = new SplitPane(fTreeArea, fTimeGraphScrollPane);
+        fSplitPane.setOrientation(Orientation.HORIZONTAL);
+
+        fBasePane = new BorderPane();
+        fBasePane.setCenter(fSplitPane);
+        fBasePane.setTop(fToolBar);
+
+        /* Start the periodic redraw thread */
+        long delay = fDebugOptions.uiUpdateDelay.get();
+        fUiUpdateTimer.schedule(fUiUpdateTimerTask, delay, delay);
+    }
+
+    public TimeGraphTreeRender getLatestTreeRender() {
+        return fLatestTreeRender;
+    }
+
+    // ------------------------------------------------------------------------
+    // ITimelineWidget
+    // ------------------------------------------------------------------------
+
+    @Override
+    public String getName() {
+        return getControl().getModelRenderProvider().getName();
+    }
+
+    @Override
+    public Parent getRootNode() {
+        return fBasePane;
+    }
+
+    @Override
+    public @NonNull SplitPane getSplitPane() {
+        return fSplitPane;
+    }
+
+    @Override
+    public @NonNull ScrollPane getTimeBasedScrollPane() {
+        return fTimeGraphScrollPane;
+    }
+
+    @Override
+    public @Nullable Rectangle getSelectionRectangle() {
+        return fSelectionLayer.getSelectionRectangle();
+    }
+
+    @Override
+    public @Nullable Rectangle getOngoingSelectionRectangle() {
+        return fSelectionLayer.getOngoingSelectionRectangle();
+    }
+
+    // ------------------------------------------------------------------------
+    // Operations
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void disposeImpl() {
+        /* Stop/cleanup the redraw thread */
+        fUiUpdateTimer.cancel();
+        fUiUpdateTimer.purge();
+    }
+
+    @Override
+    public void clear() {
+        Platform.runLater(() -> {
+            /*
+             * Clear the generated children of the various groups so they go
+             * back to their initial (post-constructor) state.
+             */
+            fTreeArea.clear();
+
+            fBackgroundLayer.clear();
+            fStateLayer.clear();
+            fArrowLayer.clear();
+            fDrawnEventLayer.clear();
+
+            /* Also clear whatever cached objects the viewer currently has. */
+            fLatestTreeRender = TimeGraphTreeRender.EMPTY_RENDER;
+            fUiUpdateTimerTask.forceRedraw();
+        });
+    }
+
+    @Override
+    public void seekVisibleRange(TimeRange newVisibleRange) {
+        final TimeRange fullTimeGraphRange = getViewContext().getCurrentTraceFullRange();
+
+        /* Update the zoom level */
+        long windowTimeRange = newVisibleRange.getDuration();
+        double timeGraphVisibleWidth = fTimeGraphScrollPane.getViewportBounds().getWidth();
+        /* Clamp the width to 1 px (0 is reported if the view is not visible) */
+        timeGraphVisibleWidth = Math.max(1, timeGraphVisibleWidth);
+        fNanosPerPixel.set(windowTimeRange / timeGraphVisibleWidth);
+
+        double oldTotalWidth = fTimeGraphPane.getLayoutBounds().getWidth();
+        double newTotalWidth = timestampToPaneXPos(fullTimeGraphRange.getEnd()) - timestampToPaneXPos(fullTimeGraphRange.getStart());
+        if (newTotalWidth < 1.0) {
+            // FIXME
+            return;
+        }
+
+        double newValue;
+        if (newVisibleRange.getStart() == fullTimeGraphRange.getStart()) {
+            newValue = fTimeGraphScrollPane.getHmin();
+        } else if (newVisibleRange.getEnd() == fullTimeGraphRange.getEnd()) {
+            newValue = fTimeGraphScrollPane.getHmax();
+        } else {
+            /*
+             * The "hvalue" is in reference to the beginning of the pane, not
+             * the middle point as one could think.
+             *
+             * Also note that the "scrollable distance" is not simply
+             * "timeGraphTotalWidth", it's
+             * "timeGraphTotalWidth - timeGraphVisibleWidth". The view does not
+             * allow scrolling the start and end edges up to the middle point
+             * for example.
+             *
+             * See http://stackoverflow.com/a/23518314/4227853 for a great
+             * explanation.
+             */
+            double startPos = timestampToPaneXPos(newVisibleRange.getStart());
+            newValue = startPos / (newTotalWidth - timeGraphVisibleWidth);
+        }
+
+        fHScrollListenerStatus.disable();
+        try {
+
+            /*
+             * If the zoom level changed, resize the pane and relocate its
+             * current contents. That way the "intermediate" display before the
+             * next repaint will continue showing correct data.
+             */
+            if (Math.abs(newTotalWidth - oldTotalWidth) > 0.5) {
+
+                /* Resize/reposition the state rectangles */
+                double factor = (newTotalWidth / oldTotalWidth);
+                fStateLayer.getRenderedStateRectangles().forEach(rect -> {
+                    rect.setLayoutX(rect.getLayoutX() * factor);
+                    rect.setWidth(rect.getWidth() * factor);
+                });
+
+                /* Reposition the text labels (don't stretch them!) */
+                fStateLayer.getRenderedStateLabels().forEach(text -> {
+                    text.setX(text.getX() * factor);
+                });
+
+                /* Reposition the arrows */
+                fArrowLayer.getRenderedArrows().forEach(arrow -> {
+                    arrow.setStartX(arrow.getStartX() * factor);
+                    arrow.setEndX(arrow.getEndX() * factor);
+                });
+
+                /* Reposition the drawn events */
+                fDrawnEventLayer.getRenderedEvents().forEach(event -> {
+                    /*
+                     * Drawn events use the "translate" properties to define
+                     * their position.
+                     */
+                    event.setTranslateX(event.getTranslateX() * factor);
+                });
+
+
+                /*
+                 * Resize the pane itself. Remember min/max are bound to the
+                 * "pref" width, so this will change the actual size right away.
+                 */
+                fTimeGraphPane.setPrefWidth(newTotalWidth);
+                /*
+                 * Since we changed the size of a child of the scrollpane, it's
+                 * important to call layout() on it before setHvalue(). If we
+                 * don't, the setHvalue() will apply to the old layout, and the
+                 * upcoming pulse will simply revert our changes.
+                 */
+                fTimeGraphScrollPane.layout();
+            }
+
+            fTimeGraphScrollPane.setHvalue(newValue);
+
+        } finally {
+            fHScrollListenerStatus.enable();
+        }
+
+        /*
+         * Redraw the current selection, as it may have moved if we changed the
+         * size of the pane.
+         */
+        redrawSelection();
+    }
+
+    /**
+     *
+     * Paint the specified view area.
+     *
+     * @param windowRange
+     *            The horizontal position where the visible window currently is
+     * @param verticalPos
+     *            The vertical position where the visible window currently is
+     * @param movedHorizontally
+     *            If we have moved horizontally since the last redraw. May be
+     *            used to skip some operations. If you are not sure say "true".
+     * @param movedVertically
+     *            If we have moved vertically since the last redraw. May be used
+     *            to skip some operations. If you are not sure say "true".
+     * @param taskSeqNb
+     *            The sequence number of this task, used for logging only
+     */
+    void paintArea(TimeRange windowRange, VerticalPosition verticalPos,
+            boolean movedHorizontally, boolean movedVertically,
+            long taskSeqNb) {
+        final TimeRange fullTimeGraphRange = getViewContext().getCurrentTraceFullRange();
+
+        /*
+         * Request the needed renders and prepare the corresponding UI objects.
+         * We may ask for some padding on each side, clamped by the trace's
+         * start and end.
+         */
+        final long timeRangePadding = Math.round(windowRange.getDuration() * fDebugOptions.renderRangePadding.get());
+        final long renderingStartTime = Math.max(fullTimeGraphRange.getStart(), windowRange.getStart() - timeRangePadding);
+        final long renderingEndTime = Math.min(fullTimeGraphRange.getEnd(), windowRange.getEnd() + timeRangePadding);
+        final TimeRange renderingRange = TimeRange.of(renderingStartTime, renderingEndTime);
+
+        /*
+         * Start a new repaint, display the "loading" overlay. The next
+         * paint task to finish will put it back to non-visible.
+         */
+        if (getDebugOptions().isLoadingOverlayEnabled.get()) {
+            fTimeGraphLoadingOverlay.fadeIn();
+        }
+
+        Task<@Nullable Void> task = new Task<@Nullable Void>() {
+            @Override
+            protected @Nullable Void call() {
+                System.err.println("Starting paint task #" + taskSeqNb);
+
+                ITimeGraphModelProvider modelProvider = getControl().getModelRenderProvider();
+                TimeGraphTreeRender treeRender = modelProvider.getTreeRender();
+
+                if (isCancelled()) {
+                    return null;
+                }
+
+                /* Prepare the tree part, if needed */
+                if (!treeRender.equals(fLatestTreeRender)) {
+                    fLatestTreeRender = treeRender;
+                    fTreeArea.updateTreeContents(treeRender);
+                }
+
+                if (isCancelled()) {
+                    return null;
+                }
+
+                /* Paint the background. It's very quick so we can do it every time. */
+                fBackgroundLayer.drawContents(treeRender, renderingRange, verticalPos, this);
+
+                /*
+                 * The state rectangles should be redrawn as soon as we move,
+                 * either horizontally or vertically.
+                 */
+                fStateLayer.setWindowRange(windowRange);
+                fStateLayer.drawContents(treeRender, renderingRange, verticalPos, this);
+
+                if (isCancelled()) {
+                    return null;
+                }
+
+                /*
+                 * Arrows and drawn events are drawn for the full vertical
+                 * range. Only refetch/repaint them if we moved horizontally.
+                 */
+                if (movedHorizontally) {
+                    fArrowLayer.drawContents(treeRender, renderingRange, verticalPos, this);
+                    fDrawnEventLayer.drawContents(treeRender, renderingRange, verticalPos, this);
+                }
+
+                if (isCancelled()) {
+                    return null;
+                }
+
+                /* Painting is finished, turn off the loading overlay */
+                Platform.runLater(() -> {
+                    System.err.println("fading out overlay");
+                    fTimeGraphLoadingOverlay.fadeOut();
+                    if (fRepaintLatch != null) {
+                        fRepaintLatch.countDown();
+                    }
+                });
+
+                return null;
+            }
+        };
+
+        System.err.println("Queueing task #" + taskSeqNb);
+
+        /*
+         * Attach a listener to the task to receive exceptions thrown within the
+         * task.
+         */
+        task.exceptionProperty().addListener((obs, oldVal, newVal) -> {
+            if (newVal != null) {
+                newVal.printStackTrace();
+            }
+        });
+
+        fTaskExecutor.schedule(task);
+    }
+
+    @Override
+    public void drawSelection(TimeRange selectionRange) {
+        fSelectionLayer.drawSelection(selectionRange);
+    }
+
+    private void redrawSelection() {
+        TimeRange selectionRange = getViewContext().getCurrentSelectionTimeRange();
+        drawSelection(selectionRange);
+    }
+
+    private @Nullable StateRectangle fSelectedState = null;
+
+    /**
+     * Set the selected state rectangle
+     *
+     * @param state
+     *            The new selected state. It should ideally be one that's
+     *            present in the scenegraph.
+     * @param deselectPrevious
+     *            If the previously selected interval should be unmarked as
+     *            selected.
+     */
+    public void setSelectedState(StateRectangle state, boolean deselectPrevious) {
+        @Nullable StateRectangle previousSelectedState = fSelectedState;
+        if (previousSelectedState != null) {
+            previousSelectedState.hideTooltip();
+            if (deselectPrevious) {
+                previousSelectedState.setSelected(false);
+            }
+        }
+
+        state.setSelected(true);
+        fSelectedState = state;
+    }
+
+    /**
+     * Get the currently selected state interval
+     *
+     * @return The current selected state
+     */
+    public @Nullable StateRectangle getSelectedState() {
+        return fSelectedState;
+    }
+
+    /**
+     * Return all state rectangles currently present in the timegraph.
+     *
+     * @return The rendered state rectangles
+     */
+    public Collection<StateRectangle> getRenderedStateRectangles() {
+        return fStateLayer.getRenderedStateRectangles();
+    }
+
+    // ------------------------------------------------------------------------
+    // Mouse event listeners
+    // ------------------------------------------------------------------------
+
+    /**
+     * Class encapsulating the scrolling operations of the time graph pane.
+     *
+     * The mouse entered/exited handlers ensure only the scrollpane being
+     * interacted by the user is the one sending the synchronization signals.
+     */
+    private class ScrollingContext {
+
+        /**
+         * Listener for the horizontal scrollbar changes
+         */
+        private final ChangeListener<Number> fHScrollChangeListener = (observable, oldValue, newValue) -> {
+            if (!fDebugOptions.isScrollingListenersEnabled.get()) {
+                System.out.println("HScroll event ignored due to debug option");
+                return;
+            }
+            if (!fHScrollListenerStatus.enabledProperty().get()) {
+                System.out.println("HScroll listener triggered but inactive");
+                return;
+            }
+
+            System.out.println("HScroll change listener triggered, oldval=" + oldValue.toString() + ", newval=" + newValue.toString());
+
+            /* We need to specify the new value here, or else the old one will be used */
+            TimeRange range = getTimeGraphEdgeTimestamps(newValue.doubleValue());
+
+            System.out.println("Sending visible range update: " + range.toString());
+
+            getControl().updateVisibleTimeRange(range, false);
+
+            /*
+             * We ask the control to not send this signal back to us (to avoid
+             * jitter while scrolling), but the next UI update should refresh
+             * the view accordingly.
+             *
+             * It is not our responsibility to update to this
+             * HorizontalPosition. The control will update accordingly upon
+             * managing the signal we just sent.
+             */
+        };
+    }
+
+    /**
+     * Event handler attached to the *time graph pane*, to execute zooming
+     * operations when the control key is down (otherwise, it just lets the even
+     * bubble to the ScrollPane, which will do a standard scroll).
+     */
+    private final EventHandler<ScrollEvent> fMouseScrollListener = e -> {
+        boolean forceUseMousePosition = false;
+
+        if (!e.isControlDown()) {
+            return;
+        }
+
+        if (e.isShiftDown()) {
+            forceUseMousePosition = true;
+        }
+        e.consume();
+
+        double delta = e.getDeltaY();
+        boolean zoomIn = (delta > 0.0); // false means a zoom-out
+
+        /*
+         * getX() corresponds to the X position of the mouse on the time graph.
+         * This is seriously awesome.
+         */
+        fZoomActions.zoom(zoomIn, forceUseMousePosition, e.getX());
+
+    };
+
+    // ------------------------------------------------------------------------
+    // View-specific actions
+    // These do not come from the control, but from the view itself
+    // ------------------------------------------------------------------------
+
+    /**
+     * Utils class encapsulating zoom operations
+     */
+    public class ZoomActions {
+
+        public void zoom(boolean zoomIn, boolean forceUseMousePosition, @Nullable Double mouseX) {
+            final double zoomStep = fDebugOptions.zoomStep.get();
+
+            double newScaleFactor = (zoomIn ? 1.0 * (1 + zoomStep) : 1.0 * (1 / (1 + zoomStep)));
+
+            /* Send a corresponding window-range signal to the control */
+            TimeGraphModelControl control = getControl();
+            TimeRange visibleRange = getViewContext().getCurrentVisibleTimeRange();
+
+            TimeRange currentSelection = getViewContext().getCurrentSelectionTimeRange();
+            long currentSelectionCenter = ((currentSelection.getDuration() / 2) + currentSelection.getStart());
+            boolean currentSelectionCenterIsVisible = visibleRange.contains(currentSelectionCenter);
+
+            long zoomPivot;
+            if (fDebugOptions.zoomPivotOnMousePosition.get() && mouseX != null && forceUseMousePosition) {
+                /* Pivot on mouse position */
+                zoomPivot = paneXPosToTimestamp(mouseX);
+            } else if (fDebugOptions.zoomPivotOnSelection.get() && currentSelectionCenterIsVisible) {
+                /* Pivot on current selection center */
+                zoomPivot = currentSelectionCenter;
+            } else if (fDebugOptions.zoomPivotOnMousePosition.get() && mouseX != null) {
+                /* Pivot on mouse position */
+                zoomPivot = paneXPosToTimestamp(mouseX);
+            } else {
+                /* Pivot on center of visible range */
+                zoomPivot = visibleRange.getStart() + (visibleRange.getDuration() / 2);
+            }
+
+            /* Prevent going closer than the zoom limit */
+            double timeGraphVisibleWidth = Math.max(1, fTimeGraphScrollPane.getViewportBounds().getWidth());
+            double minDuration = ZOOM_LIMIT * timeGraphVisibleWidth;
+
+            double newDuration = visibleRange.getDuration() * (1.0 / newScaleFactor);
+            newDuration = Math.max(minDuration, newDuration);
+            double durationDelta = newDuration - visibleRange.getDuration();
+            double zoomPivotRatio = (double) (zoomPivot - visibleRange.getStart()) / (double) (visibleRange.getDuration());
+
+            long newStart = visibleRange.getStart() - Math.round(durationDelta * zoomPivotRatio);
+            long newEnd = visibleRange.getEnd() + Math.round(durationDelta - (durationDelta * zoomPivotRatio));
+
+            /* Clamp newStart and newEnd to the full trace's range */
+            TimeRange fullRange = control.getViewContext().getCurrentTraceFullRange();
+            long traceStart = fullRange.getStart();
+            long traceEnd = fullRange.getEnd();
+            newStart = Math.max(newStart, traceStart);
+            newEnd = Math.min(newEnd, traceEnd);
+
+            control.updateVisibleTimeRange(TimeRange.of(newStart, newEnd), true);
+        }
+
+    }
+
+    /**
+     * Get the viewer's zoom actions
+     *
+     * @return The zoom actions
+     */
+    public ZoomActions getZoomActions() {
+        return fZoomActions;
+    }
+
+    // ------------------------------------------------------------------------
+    // Common utils
+    // ------------------------------------------------------------------------
+
+    /**
+     * Determine the timestamps currently represented by the left and right
+     * edges of the time graph pane. In other words, the current "visible range"
+     * the view is showing.
+     *
+     * Note that this method gets its information from UI objects only, so there
+     * might be discrepancies between this and the results of
+     * {@link TimeGraphModelControl#getVisibleTimeRange()}.
+     *
+     * @param newHValue
+     *            The "hvalue" property of the horizontal scrollbar to use. If
+     *            null, the current value will be retrieved from the scenegraph
+     *            object. For example, a scrolling listener might want to pass
+     *            its newValue here, since the scenegraph object will not have
+     *            been updated yet.
+     * @return The corresponding time range
+     */
+    TimeRange getTimeGraphEdgeTimestamps(@Nullable Double newHValue) {
+        double hvalue = (newHValue == null ? fTimeGraphScrollPane.getHvalue() : newHValue.doubleValue());
+
+        /*
+         * Determine the X positions represented by the edges.
+         */
+        double hmin = fTimeGraphScrollPane.getHmin();
+        double hmax = fTimeGraphScrollPane.getHmax();
+        double contentWidth = fTimeGraphPane.getLayoutBounds().getWidth();
+        double viewportWidth = fTimeGraphScrollPane.getViewportBounds().getWidth();
+        double hoffset = Math.max(0, contentWidth - viewportWidth) * (hvalue - hmin) / (hmax - hmin);
+
+        /*
+         * Convert the positions of the left and right edges to timestamps.
+         */
+        long tsStart = paneXPosToTimestamp(hoffset);
+        long tsEnd = paneXPosToTimestamp(hoffset + viewportWidth);
+
+        return TimeRange.of(tsStart, tsEnd);
+    }
+
+    public double timestampToPaneXPos(long timestamp) {
+        TimeRange fullTimeGraphRange = getViewContext().getCurrentTraceFullRange();
+        return timestampToPaneXPos(timestamp, fullTimeGraphRange, fNanosPerPixel.get());
+    }
+
+    @VisibleForTesting
+    static double timestampToPaneXPos(long timestamp, TimeRange fullTimeGraphRange, double nanosPerPixel) {
+        long start = fullTimeGraphRange.getStart();
+        long end = fullTimeGraphRange.getEnd();
+
+        if (timestamp < start) {
+            throw new IllegalArgumentException(timestamp + " is smaller than trace start time " + start); //$NON-NLS-1$
+        }
+        if (timestamp > end) {
+            throw new IllegalArgumentException(timestamp + " is greater than trace end time " + end); //$NON-NLS-1$
+        }
+
+        double traceDuration = fullTimeGraphRange.getDuration();
+        double timeStampRatio = (timestamp - start) / traceDuration;
+
+        long fullTraceWidthInPixels = (long) (traceDuration / nanosPerPixel);
+        double xPos = fullTraceWidthInPixels * timeStampRatio;
+        return Math.round(xPos);
+    }
+
+    public long paneXPosToTimestamp(double x) {
+        long fullTimeGraphStartTime = getViewContext().getCurrentTraceFullRange().getStart();
+        return paneXPosToTimestamp(x, fTimeGraphPane.getWidth(), fullTimeGraphStartTime, fNanosPerPixel.get());
+    }
+
+    @VisibleForTesting
+    static long paneXPosToTimestamp(double x, double totalWidth, long startTimestamp, double nanosPerPixel) {
+        if (x < 0.0 || totalWidth < 1.0 || x > totalWidth) {
+            throw new IllegalArgumentException("Invalid position arguments: pos=" + x + ", width=" + totalWidth);
+        }
+
+        long ts = Math.round(x * nanosPerPixel);
+        return ts + startTimestamp;
+    }
+
+    /**
+     * Get the current vertical position of the timegraph.
+     *
+     * @return The corresponding VerticalPosition
+     */
+    VerticalPosition getCurrentVerticalPosition() {
+        double vvalue = fTimeGraphScrollPane.getVvalue();
+
+        /* Get the Y position of the top/bottom edges of the pane */
+        double vmin = fTimeGraphScrollPane.getVmin();
+        double vmax = fTimeGraphScrollPane.getVmax();
+        double contentHeight = fTimeGraphPane.getLayoutBounds().getHeight();
+        double viewportHeight = fTimeGraphScrollPane.getViewportBounds().getHeight();
+
+        double vtop = Math.max(0, contentHeight - viewportHeight) * (vvalue - vmin) / (vmax - vmin);
+        double vbottom = vtop + viewportHeight;
+
+        return new VerticalPosition(vtop, vbottom);
+    }
+
+    public static int paneYPosToEntryListIndex(double yPos, double entryHeight) {
+        if (yPos < 0.0 || entryHeight < 0.0) {
+            throw new IllegalArgumentException();
+        }
+
+        return (int) (yPos / entryHeight);
+    }
+
+    // ------------------------------------------------------------------------
+    // Test accessors
+    // ------------------------------------------------------------------------
+
+    private volatile @Nullable CountDownLatch fRepaintLatch = null;
+
+    @VisibleForTesting
+    void prepareWaitForRepaint() {
+        if (fRepaintLatch != null) {
+            throw new IllegalStateException("Do not call this method concurrently!"); //$NON-NLS-1$
+        }
+        fRepaintLatch = new CountDownLatch(1);
+    }
+
+    @VisibleForTesting
+    boolean waitForRepaint() {
+        CountDownLatch latch = fRepaintLatch;
+        boolean done = false;
+        if (latch == null) {
+            throw new IllegalStateException("Do not call this method concurrently!"); //$NON-NLS-1$
+        }
+        try {
+            done = latch.await(100, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+        }
+        if (done) {
+            fRepaintLatch = null;
+        }
+        return done;
+    }
+
+    /**
+     * Bypass the redraw thread and do a manual redraw of the current location.
+     */
+    @VisibleForTesting
+    void paintCurrentLocation() {
+        TimeRange currentHorizontalPos = getViewContext().getCurrentVisibleTimeRange();
+        VerticalPosition currentVerticalPos = getCurrentVerticalPosition();
+        paintArea(currentHorizontalPos, currentVerticalPos, true, true, 0);
+    }
+
+    // could eventually be exposed to the user, as "advanced preferences"
+    public DebugOptions getDebugOptions() {
+        return fDebugOptions;
+    }
+
+    public double getCurrentNanosPerPixel() {
+        return fNanosPerPixel.get();
+    }
+
+    public Pane getTimeGraphPane() {
+        return fTimeGraphPane;
+    }
+
+    @VisibleForTesting
+    ScrollPane getTimeGraphScrollPane() {
+        return fTimeGraphScrollPane;
+    }
+
+    @VisibleForTesting
+    TimeGraphArrowLayer getArrowLayer() {
+        return fArrowLayer;
+    }
+
+    @VisibleForTesting
+    TimeGraphDrawnEventLayer getDrawnEventLayer() {
+        return fDrawnEventLayer;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidgetTreeArea.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/TimeGraphWidgetTreeArea.java
new file mode 100644 (file)
index 0000000..0ed8321
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+
+import com.sun.javafx.scene.control.skin.TreeViewSkin;
+import com.sun.javafx.scene.control.skin.VirtualFlow;
+
+import javafx.application.Platform;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.geometry.Insets;
+import javafx.geometry.Orientation;
+import javafx.scene.control.ScrollBar;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TreeCell;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeView;
+import javafx.scene.layout.BorderPane;
+
+/**
+ * Extension of {@link TreeView} which exposes some of its components, like
+ * Scrollbars.
+ *
+ * It also supports specifying a static visibility modifier for the scrollbars,
+ * similar to a {@link ScrollPane}'s bar policy.
+ *
+ * This class makes us of some internal, non-API JavaFX objects and methods and
+ * thus, might break once the codebase moves to a newer version.
+ *
+ * @author Alexandre Montplaisir
+ */
+@SuppressWarnings("restriction")
+public class TimeGraphWidgetTreeArea extends BorderPane {
+
+    private final double fEntryHeight;
+
+    private final TreeAreaTreeView fTreeView;
+
+    /**
+     * Constructor
+     */
+    public TimeGraphWidgetTreeArea(double entryHeight, ObjectProperty<@Nullable ITmfTrace> targetTraceProperty) {
+        TreeItem<String> treeRoot = new TreeItem<>();
+        treeRoot.setExpanded(true);
+
+        TreeAreaTreeView treeView = new TreeAreaTreeView(treeRoot);
+        treeView.setFixedCellSize(entryHeight);
+
+        /*
+         * Instead of using the TreeView's scrollbar, whose visibility and size are
+         * managed internally by the super-class, we will instantiate our own and will
+         * bind its important properties to the "real" one.
+         */
+        ScrollBar dummyScrollBar = new ScrollBar();
+        dummyScrollBar.setOrientation(Orientation.HORIZONTAL);
+        ScrollBar realHScrollBar = treeView.getHBar();
+        dummyScrollBar.valueProperty().bindBidirectional(realHScrollBar.valueProperty());
+        dummyScrollBar.visibleAmountProperty().bind(treeView.widthProperty().divide(2));
+
+        /* Add the "children" to this BorderPane */
+        setCenter(treeView);
+        setBottom(dummyScrollBar);
+
+        treeView.setCellFactory(view -> new TreeAreaTreeViewCell());
+
+        fEntryHeight = entryHeight;
+        fTreeView = treeView;
+    }
+
+    public void clear() {
+        fTreeView.setRoot(null);
+    }
+
+    public ScrollBar getVerticalScrollBar() {
+        return fTreeView.getVBar();
+    }
+
+    public DoubleBinding currentHeightProperty() {
+        return fTreeView.expandedItemCountProperty().multiply((fEntryHeight));
+    }
+
+    public void updateTreeContents(TimeGraphTreeRender treeRender) {
+        TreeItem<String> newRoot = getTreeItemForElement(treeRender.getRootElement());
+        newRoot.setExpanded(true);
+        Platform.runLater(() -> {
+            fTreeView.setRoot(newRoot);
+        });
+    }
+
+    private static TreeItem<String> getTreeItemForElement(TimeGraphTreeElement element) {
+        TreeItem<String> treeItem = new TreeItem<>(element.getName());
+        List<TreeItem<String>> childItems = element.getChildElements().stream()
+                .map(treeElem -> getTreeItemForElement(treeElem))
+                .collect(Collectors.toList());
+        if (!childItems.isEmpty()) {
+            treeItem.getChildren().addAll(childItems);
+        }
+
+        // TODO Correctly manage sub-trees being expanded. Disallow it for now.
+        treeItem.setExpanded(true);
+        treeItem.addEventHandler(TreeItem.branchCollapsedEvent(),
+                event -> event.getTreeItem().setExpanded(true));
+
+        return treeItem;
+    }
+
+    // ------------------------------------------------------------------------
+    // Methods related to the Tree area
+    // ------------------------------------------------------------------------
+
+    private static List<TreeItem<String>> getTreeItemNames(TimeGraphTreeRender treeRender) {
+        return treeRender.getAllTreeElements().stream()
+                .map(elem -> new TreeItem<>(elem.getName()))
+                .collect(Collectors.toList());
+    }
+
+    // TODO Background could be re-added by salvaging this
+//    private static Node prepareTreeContents(TimeGraphTreeRender treeRender, ReadOnlyDoubleProperty widthProperty) {
+//        /* Prepare the tree element objects */
+//        List<Label> treeElements = treeRender.getAllTreeElements().stream()
+//                // TODO Put as a real tree. TreeView ?
+//                .map(elem -> new Label(elem.getName()))
+//                .peek(label -> {
+//                    label.setPrefHeight(ENTRY_HEIGHT);
+//                    label.setPadding(new Insets(0, LABEL_SIDE_MARGIN, 0, LABEL_SIDE_MARGIN));
+//                    /*
+//                     * Re-set the solid background for the labels, so we do not
+//                     * see the background lines through.
+//                     */
+//                    label.setStyle(BACKGROUND_STYLE);
+//                })
+//                .collect(Collectors.toList());
+//
+//        VBox treeElemsBox = new VBox(); // Change to TreeView eventually ?
+//        treeElemsBox.getChildren().addAll(treeElements);
+//
+//        /* Prepare the background layer with the horizontal alignment lines */
+//        List<Line> lines = DoubleStream.iterate((ENTRY_HEIGHT / 2), y -> y + ENTRY_HEIGHT)
+//                .limit(treeElements.size())
+//                .mapToObj(y -> {
+//                    Line line = new Line();
+//                    line.startXProperty().bind(JfxUtils.ZERO_PROPERTY);
+//                    line.endXProperty().bind(widthProperty);
+//                    line.setStartY(y);
+//                    line.setEndY(y);
+//
+//                    line.setStroke(BACKGROUD_LINES_COLOR);
+//                    line.setStrokeWidth(1.0);
+//                    return line;
+//                })
+//                .collect(Collectors.toList());
+//        Pane background = new Pane();
+//        background.getChildren().addAll(lines);
+//
+//        /* Put the background layer and the Tree View into their containers */
+//        StackPane stackPane = new StackPane(background, treeElemsBox);
+//        stackPane.setStyle(BACKGROUND_STYLE);
+//        return stackPane;
+//    }
+
+
+    // ------------------------------------------------------------------------
+    // Helper inner classes
+    // ------------------------------------------------------------------------
+
+    private static class TreeAreaTreeViewCell extends TreeCell<String> {
+
+        public TreeAreaTreeViewCell() {
+            // TODO Font size etc. could be managed here. Might be needed on ENTRY_HEIGHT
+            // becomes variable
+            setPadding(Insets.EMPTY);
+        }
+
+        @Override
+        public void updateItem(String item, boolean empty) {
+            super.updateItem(item, empty);
+            setGraphic(null);
+            if (empty) {
+                setText(null);
+            } else {
+                setText(item);
+            }
+        }
+    }
+
+    private static class TreeAreaTreeView extends TreeView<String> {
+
+        private TreeAreaTreeViewSkin fSkin = new TreeAreaTreeViewSkin(this);
+
+        public TreeAreaTreeView(TreeItem<String> root) {
+            super(root);
+
+            /*
+             * Completely hide both scrollbars. This works much better than trying to set
+             * their 'visible' property, which are constantly being changed by the
+             * superclass.
+             */
+            getHBar().minHeightProperty().bind(JfxUtils.ZERO_PROPERTY);
+            getHBar().prefHeightProperty().bind(JfxUtils.ZERO_PROPERTY);
+            getHBar().maxHeightProperty().bind(JfxUtils.ZERO_PROPERTY);
+
+            getVBar().minWidthProperty().bind(JfxUtils.ZERO_PROPERTY);
+            getVBar().prefWidthProperty().bind(JfxUtils.ZERO_PROPERTY);
+            getVBar().maxWidthProperty().bind(JfxUtils.ZERO_PROPERTY);
+
+            lookupAll(".scroll-bar").forEach(node -> node.setStyle("-fx-base: transparent;")); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+
+        @Override
+        protected TreeAreaTreeViewSkin createDefaultSkin() {
+            return fSkin;
+        }
+
+        private TreeAreaTreeViewSkin retrieveSkin() {
+            return fSkin;
+        }
+
+        /**
+         * Retrieve the horizontal scrollbar
+         *
+         * @return The horizontal scrollbar
+         */
+        public ScrollBar getHBar() {
+            return retrieveSkin().getFlow().retrieveHbar();
+        }
+
+        /**
+         * Retrieve the vertical scrollbar
+         *
+         * @return The vertical scrollbar
+         */
+        public ScrollBar getVBar() {
+            return retrieveSkin().getFlow().retrieveVbar();
+        }
+    }
+
+    private static class TreeAreaTreeViewSkin extends TreeViewSkin<String> {
+
+        public TreeAreaTreeViewSkin(TreeAreaTreeView treeView) {
+            super(treeView);
+        }
+
+        @Override
+        protected TreeAreaVirtualFlow createVirtualFlow() {
+            return new TreeAreaVirtualFlow();
+        }
+
+        public TreeAreaVirtualFlow getFlow() {
+            return (TreeAreaVirtualFlow) requireNonNull(flow);
+        }
+
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static class TreeAreaVirtualFlow extends VirtualFlow {
+
+        public ScrollBar retrieveHbar() {
+            return requireNonNull(getHbar());
+        }
+
+        public ScrollBar retrieveVbar() {
+            return requireNonNull(getVbar());
+        }
+
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/VerticalPosition.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/VerticalPosition.java
new file mode 100644 (file)
index 0000000..ef658c2
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
+
+import java.util.Objects;
+
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.common.base.MoreObjects;
+
+public class VerticalPosition {
+
+    /**
+     * Placeholder for uninitialized vertical positions.
+     */
+    public static final VerticalPosition UNINITIALIZED_VP = new VerticalPosition(0.0, 0.0);
+
+    private static final double EPSILON = 0.00001;
+
+    public final double fTopPos;
+    public final double fBottomPos;
+
+    public VerticalPosition(double topPos, double bottomPos) {
+        fTopPos = topPos;
+        fBottomPos = bottomPos;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(fTopPos, fBottomPos);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        VerticalPosition other = (VerticalPosition) obj;
+        return (doubleEquals(fTopPos, other.fTopPos)
+                && doubleEquals(fBottomPos, other.fBottomPos));
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("fTopPos", fTopPos) //$NON-NLS-1$
+                .add("fBottomPos", fBottomPos) //$NON-NLS-1$
+                .toString();
+    }
+
+    private static boolean doubleEquals(double d1, double d2) {
+        return (Math.abs(d1 - d2) < EPSILON);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphArrowLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphArrowLayer.java
new file mode 100644 (file)
index 0000000..061e19d
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.FutureTask;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.ITimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.Arrow;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import com.google.common.collect.ImmutableMap;
+
+import javafx.application.Platform;
+import javafx.scene.Group;
+import javafx.scene.paint.Paint;
+
+public class TimeGraphArrowLayer extends TimeGraphLayer {
+
+    private final Map<ITimeGraphModelArrowProvider, ArrowConfig> fArrowProvidersConfig;
+
+    public TimeGraphArrowLayer(TimeGraphWidget widget, Group parentGroup) {
+        super(widget, parentGroup);
+
+        Collection<ITimeGraphModelArrowProvider> arrowProviders =
+                widget.getControl().getModelRenderProvider().getArrowProviders();
+
+        fArrowProvidersConfig = arrowProviders.stream()
+                .collect(ImmutableMap.toImmutableMap(
+                        Function.identity(),
+                        ap -> {
+                            Group group = new Group();
+                            ColorDefinition colorDef = ap.getArrowSeries().getColor();
+                            Paint stroke = JfxColorFactory.getColorFromDef(colorDef);
+                            return new ArrowConfig(group, stroke);
+                        }));
+
+        fArrowProvidersConfig.values().stream()
+                .map(ArrowConfig::getGroup)
+                .forEach(parentGroup.getChildren()::add);
+
+        /*
+         * Add listeners to the registered arrow providers. If providers become
+         * enabled or disabled, we must repaint or hide the arrows from this
+         * provider's series.
+         */
+        arrowProviders.forEach(ap -> {
+            ap.enabledProperty().addListener((obs, oldValue, newValue) -> {
+                if (newValue) {
+                    /*
+                     * The provider is now enabled, we must fetch and display
+                     * its arrows
+                     */
+                    TimeRange timeRange = getWidget().getViewContext().getCurrentVisibleTimeRange();
+                    TimeGraphTreeRender treeRender = getWidget().getLatestTreeRender();
+                    // FIXME Not using a task here, so this might end up running
+                    // on the UI thread  and not being cancellable...
+                    paintArrowsOfProvider(treeRender, timeRange, ap, null);
+                } else {
+                    /*
+                     * The provider is now disabled, we must remove the existing
+                     * arrows from this provider.
+                     */
+                    ArrowConfig config = fArrowProvidersConfig.get(ap);
+                    if (config == null) {
+                        return;
+                    }
+                    Platform.runLater(() -> {
+                        config.getGroup().getChildren().clear();
+                    });
+                }
+            });
+        });
+    }
+
+    @Override
+    public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task) {
+        fArrowProvidersConfig.keySet().stream()
+                .filter(arrowProvider -> arrowProvider.enabledProperty().get())
+                .forEach(arrowProvider -> paintArrowsOfProvider(treeRender, timeRange, arrowProvider, task));
+    }
+
+    @Override
+    public void clear() {
+        /*
+         * Only clear the children's children, not our direct children which
+         * could still be valid.
+         */
+        fArrowProvidersConfig.values().stream()
+                .map(ArrowConfig::getGroup)
+                .forEach(group -> group.getChildren().clear());
+    }
+
+    private void paintArrowsOfProvider(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            ITimeGraphModelArrowProvider arrowProvider, @Nullable FutureTask<?> task) {
+        ArrowConfig config = fArrowProvidersConfig.get(arrowProvider);
+        if (config == null) {
+            /* Should not happen... */
+            return;
+        }
+
+        TimeGraphArrowRender arrowRender = arrowProvider.getArrowRender(treeRender, timeRange, task);
+        Collection<Arrow> arrows = prepareArrows(treeRender, arrowRender, config.getStroke());
+
+        Platform.runLater(() -> {
+            config.getGroup().getChildren().clear();
+            config.getGroup().getChildren().addAll(arrows);
+        });
+    }
+
+    private Collection<Arrow> prepareArrows(TimeGraphTreeRender treeRender,
+            TimeGraphArrowRender arrowRender, Paint arrowStroke) {
+        final double entryHeight = TimeGraphWidget.ENTRY_HEIGHT;
+
+        Collection<Arrow> arrows = arrowRender.getArrows().stream()
+            .map(timeGraphArrow -> {
+                TimeGraphTreeElement startTreeElem = timeGraphArrow.getStartEvent().getTreeElement();
+                TimeGraphTreeElement endTreeElem = timeGraphArrow.getEndEvent().getTreeElement();
+                long startTimestamp = timeGraphArrow.getStartEvent().getTimestamp();
+                long endTimestamp = timeGraphArrow.getEndEvent().getTimestamp();
+                // FIXME Build and use a hashmap instead for indexes
+                int startIndex = treeRender.getAllTreeElements().indexOf(startTreeElem);
+                int endIndex = treeRender.getAllTreeElements().indexOf(endTreeElem);
+                if (startIndex == -1 || endIndex == -1) {
+                    /* We shouldn't have received this... */
+                    return null;
+                }
+
+                double startX = getWidget().timestampToPaneXPos(startTimestamp);
+                double endX = getWidget().timestampToPaneXPos(endTimestamp);
+                double startY = startIndex * entryHeight + entryHeight / 2;
+                double endY = endIndex * entryHeight + entryHeight / 2;
+
+                Arrow arrow = new Arrow(startX, startY, endX, endY);
+                arrow.setStroke(arrowStroke);
+                return arrow;
+            })
+            .filter(Objects::nonNull)
+            .collect(Collectors.toList());
+        return arrows;
+    }
+
+    public synchronized Collection<Arrow> getRenderedArrows() {
+        /*
+         * Retrieve the rendered arrows of each group, and flatten them into a
+         * single collection.
+         */
+        return fArrowProvidersConfig.values().stream()
+                .map(ArrowConfig::getGroup)
+                .map(Group::getChildren)
+                .flatMap(Collection::stream)
+                .map(node -> (Arrow) node)
+                .collect(Collectors.toList());
+    }
+
+    private static class ArrowConfig {
+
+        private final Group fGroup;
+        private final Paint fStroke;
+
+        public ArrowConfig(Group group, Paint stroke) {
+            fGroup = group;
+            fStroke = stroke;
+        }
+
+        public Group getGroup() {
+            return fGroup;
+        }
+
+        public Paint getStroke() {
+            return fStroke;
+        }
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphBackgroundLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphBackgroundLayer.java
new file mode 100644 (file)
index 0000000..d74177f
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import java.util.LinkedList;
+import java.util.concurrent.FutureTask;
+import java.util.stream.DoubleStream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import javafx.application.Platform;
+import javafx.scene.Group;
+import javafx.scene.shape.Line;
+
+/**
+ * Sub-control of the time graph widget taking care of simply drawing the
+ * background lines in the area of the current visible window.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphBackgroundLayer extends TimeGraphLayer {
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            Main widget which this control supports
+     * @param parentGroup
+     *            The group to which this layer should add its children
+     */
+    public TimeGraphBackgroundLayer(TimeGraphWidget widget, Group parentGroup) {
+        super(widget, parentGroup);
+    }
+
+    @Override
+    public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task) {
+        final double entryHeight = TimeGraphWidget.ENTRY_HEIGHT;
+
+        final int entriesToPrefetch = getWidget().getDebugOptions().entryPadding.get();
+        int totalNbEntries = treeRender.getAllTreeElements().size();
+
+        final double timeGraphWidth = getWidget().getTimeGraphPane().getWidth();
+        final double paintTopPos = Math.max(0.0, vPos.fTopPos - entriesToPrefetch * entryHeight);
+        final double paintBottomPos = Math.min(vPos.fBottomPos + entriesToPrefetch * entryHeight,
+                /*
+                 * If there are less tree elements than can fill the window,
+                 * stop at the end of the real tree elements.
+                 */
+                totalNbEntries * entryHeight);
+
+        LinkedList<Line> lines = new LinkedList<>();
+        DoubleStream.iterate((entryHeight / 2), y -> y + entryHeight)
+                // TODO Java 9 will allow using dropWhile()/takeWhile()/collect
+                .filter(y -> y > paintTopPos)
+                .peek(y -> {
+                    Line line = new Line(0, y, timeGraphWidth, y);
+                    line.setStroke(TimeGraphWidget.BACKGROUD_LINES_COLOR);
+                    line.setStrokeWidth(1.0);
+
+                    lines.add(line);
+                })
+                .allMatch(y -> y < paintBottomPos);
+        // The list contains the first element that didn't match the predicate,
+        // we don't want it.
+        if (!lines.isEmpty()) {
+            lines.removeLast();
+        }
+
+        Platform.runLater(() -> {
+            getParentGroup().getChildren().clear();
+            getParentGroup().getChildren().addAll(lines);
+        });
+    }
+
+    @Override
+    public void clear() {
+        JfxUtils.runOnMainThread(() -> getParentGroup().getChildren().clear());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphDrawnEventLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphDrawnEventLayer.java
new file mode 100644 (file)
index 0000000..1d5db52
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.FutureTask;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents.ITimeGraphDrawnEventProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents.TimeGraphDrawnEventProviderManager;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries.SymbolStyle;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import javafx.application.Platform;
+import javafx.collections.ObservableSet;
+import javafx.collections.SetChangeListener;
+import javafx.scene.Group;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.Polygon;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.SVGPath;
+import javafx.scene.shape.Shape;
+
+public class TimeGraphDrawnEventLayer extends TimeGraphLayer {
+
+    private final Map<ITimeGraphDrawnEventProvider, Group> fEventProviders = new HashMap<>();
+
+    public TimeGraphDrawnEventLayer(TimeGraphWidget widget, Group parentGroup) {
+        super(widget, parentGroup);
+
+        ObservableSet<ITimeGraphDrawnEventProvider> providers = TimeGraphDrawnEventProviderManager.instance().getRegisteredProviders();
+        /* Populate with the initial values */
+        providers.forEach(this::trackEventProvider);
+
+        /* Add listeners to track registered/deregistered providers */
+        providers.addListener((SetChangeListener<ITimeGraphDrawnEventProvider>) change -> {
+            if (change == null) {
+                return;
+            }
+            ITimeGraphDrawnEventProvider addedProvider = change.getElementAdded();
+            if (addedProvider != null) {
+                trackEventProvider(addedProvider);
+            }
+
+            ITimeGraphDrawnEventProvider removedProvider = change.getElementRemoved();
+            if (removedProvider != null) {
+                untrackEventProvider(removedProvider);
+            }
+        });
+    }
+
+    private void trackEventProvider(ITimeGraphDrawnEventProvider provider) {
+        Group newGroup = new Group();
+        Group oldGroup = fEventProviders.put(provider, newGroup);
+        if (oldGroup == null) {
+            Platform.runLater(() -> {
+                getParentGroup().getChildren().add(newGroup);
+            });
+        } else {
+            /* Remove the old group in case there was already one. */
+            Platform.runLater(() -> {
+                getParentGroup().getChildren().remove(oldGroup);
+                getParentGroup().getChildren().add(newGroup);
+            });
+        }
+
+        /*
+         * Add a listener to this provider's "enabled" property, so that when it
+         * changes from enabled to disabled and vice versa, we update the view
+         * accordingly.
+         */
+        provider.enabledProperty().addListener((obs, oldValue, newValue) -> {
+            if (newValue) {
+                /* The provider was just enabled */
+                TimeRange timeRange = getWidget().getViewContext().getCurrentVisibleTimeRange();
+                TimeGraphTreeRender treeRender = getWidget().getLatestTreeRender();
+                // FIXME Use a Task?
+                paintEventsOfProvider(treeRender, timeRange, provider, null);
+            } else {
+                /* Provider was disabled. Clear the children of its group. */
+                Group group = fEventProviders.get(provider);
+                if (group == null) {
+                    return;
+                }
+                Platform.runLater(() -> group.getChildren().clear());
+            }
+        });
+
+    }
+
+    private void untrackEventProvider(ITimeGraphDrawnEventProvider provider) {
+        Group group = fEventProviders.remove(provider);
+        if (group != null) {
+            Platform.runLater(() -> {
+                getParentGroup().getChildren().remove(group);
+            });
+        }
+        /*
+         * Note, we do not need to explicitly remove the ChangeListener we added
+         * above, the weak reference will simply lose it at some point.
+         */
+    }
+
+    @Override
+    public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task) {
+        fEventProviders.keySet().stream()
+                .filter(provider -> provider.enabledProperty().get())
+                .forEach(provider -> paintEventsOfProvider(treeRender, timeRange, provider, task));
+    }
+
+    @Override
+    public void clear() {
+        fEventProviders.values().forEach(group -> group.getChildren().clear());
+    }
+
+    private void paintEventsOfProvider(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            ITimeGraphDrawnEventProvider eventsProvider, @Nullable FutureTask<?> task) {
+
+        TimeGraphDrawnEventRender eventRender = eventsProvider.getEventRender(treeRender, timeRange, task);
+        Collection<Shape> drawnEvents = prepareDrawnEvents(treeRender, eventRender);
+
+        Group paintGroup = requireNonNull(fEventProviders.get(eventsProvider));
+        Platform.runLater(() -> {
+            paintGroup.getChildren().clear();
+            paintGroup.getChildren().addAll(drawnEvents);
+        });
+    }
+
+    private Collection<Shape> prepareDrawnEvents(TimeGraphTreeRender treeRender, TimeGraphDrawnEventRender eventRender) {
+        final double entryHeight = TimeGraphWidget.ENTRY_HEIGHT;
+
+        Collection<Shape> shapes = eventRender.getEvents().stream()
+                .map(event -> {
+                    TimeGraphEvent tgEvent = event.getEvent();
+                    double x = getWidget().timestampToPaneXPos(tgEvent.getTimestamp());
+
+                    int treeIndex = treeRender.getAllTreeElements().indexOf(tgEvent.getTreeElement());
+                    if (treeIndex == -1) {
+                        return null;
+                    }
+                    double y = treeIndex * entryHeight + entryHeight / 2;
+
+                    Shape shape = getShapeFromEvent(event);
+                    /*
+                     * Some symbols already use the layout* properties for
+                     * adjusting their center. Use translate* properties for
+                     * their positioning on the timegraph.
+                     */
+                    shape.setTranslateX(x);
+                    shape.setTranslateY(y);
+                    return shape;
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        return shapes;
+    }
+
+    private static Shape getShapeFromEvent(TimeGraphDrawnEvent event) {
+        Color color = JfxColorFactory.getColorFromDef(event.getEventSeries().getColor().get());
+        SymbolStyle symbol = event.getEventSeries().getSymbolStyle().get();
+        Shape shape = getShapeFromSymbol(symbol);
+        shape.setFill(color);
+        return shape;
+    }
+
+    public static Shape getShapeFromSymbol(SymbolStyle symbol) {
+        Shape shape;
+        switch (symbol) {
+        case CIRCLE:
+            shape = new Circle(5);
+            break;
+
+        case DIAMOND: {
+            shape = new Polygon(5.0, 0.0,
+                    10.0, 5.0,
+                    5.0, 10.0,
+                    0.0, 5.0);
+            shape.relocate(-5.0, -5.0);
+        }
+            break;
+
+        case SQUARE:
+            shape = new Rectangle(-5, -5, 10, 10);
+            break;
+
+        case STAR:
+            // FIXME bigger?
+            shape = new Polygon(4.0, 0.0,
+                    5.0, 4.0,
+                    8.0, 4.0,
+                    6.0, 6.0,
+                    7.0, 9.0,
+                    4.0, 7.0,
+                    1.0, 9.0,
+                    2.0, 6.0,
+                    0.0, 4.0,
+                    3.0, 4.0);
+            shape.relocate(-4, -4.5);
+            break;
+
+        case TRIANGLE: {
+            SVGPath path = new SVGPath();
+            path.setContent("M5,0 L10,8 L0,8 Z"); //$NON-NLS-1$
+            path.relocate(-5, -2);
+            shape = path;
+        }
+            break;
+
+        case CROSS:
+        default: {
+            SVGPath path = new SVGPath();
+            path.setContent("M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2, 10 L0,10 L0,8 L4,5 L0,2 L0,0 Z"); //$NON-NLS-1$
+            path.relocate(-5, -5);
+            shape = path;
+        }
+            break;
+
+        }
+
+        shape.setStroke(Color.BLACK);
+        return shape;
+    }
+
+    public synchronized Collection<Shape> getRenderedEvents() {
+        /*
+         * Retrieve the rendered events of each group, and flatten them into a
+         * single collection.
+         */
+        return fEventProviders.values().stream()
+                .map(Group::getChildren)
+                .flatMap(Collection::stream)
+                .map(node -> (Shape) node)
+                .collect(Collectors.toList());
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphLayer.java
new file mode 100644 (file)
index 0000000..1bff408
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import java.util.concurrent.FutureTask;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import javafx.scene.Group;
+
+/**
+ * Base class for layers of the timegraph, providing one particular data aspect.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class TimeGraphLayer {
+
+    private final TimeGraphWidget fWidget;
+    private final Group fParentGroup;
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            The widget to which this layer belongs
+     * @param parentGroup
+     *            The group to which this layer should add its children
+     */
+    public TimeGraphLayer(TimeGraphWidget widget, Group parentGroup) {
+        fWidget = widget;
+        fParentGroup = parentGroup;
+    }
+
+    /**
+     * Get the layer's widget.
+     *
+     * @return The widget
+     */
+    protected TimeGraphWidget getWidget() {
+        return fWidget;
+    }
+
+    /**
+     * Get this layer's parent group, in which all its generated children should
+     * be added.
+     *
+     * @return The parent group
+     */
+    public Group getParentGroup() {
+        return fParentGroup;
+    }
+
+    /**
+     * Ask the layer to populate its scenegraph. This is usually done by having
+     * the Layer add children Nodes to itself.
+     *
+     * @param treeRender
+     *            The current tree render of the time graph. Can be used to get
+     *            the number of visible entries, etc.
+     * @param timeRange
+     *            The time range, or "horizontal position" of the current
+     *            visible window
+     * @param vPos
+     *            The vertical position of the current visible window
+     * @param task
+     *            An optional Task which is calling this method. Long-running
+     *            implementations are encouraged to check the 'cancelled' status
+     *            of this Task periodically (if supplied), and early exit in
+     *            case of cancellation.
+     */
+    public abstract void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task);
+
+    /**
+     * Remove from the scenegraph the Nodes generated by this layer.
+     */
+    public abstract void clear();
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphSelectionLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphSelectionLayer.java
new file mode 100644 (file)
index 0000000..0d19fc1
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.concurrent.FutureTask;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import javafx.event.EventHandler;
+import javafx.scene.Group;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.Pane;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeLineCap;
+
+/**
+ * Sub-control of the time graph widget to handle the selection layer, which
+ * displays the current and ongoing selections.
+ *
+ * It also sends the corresponding selection update whenever a new selection is
+ * made.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphSelectionLayer extends TimeGraphLayer {
+
+    /* Style settings. TODO Move to debug options? */
+    private static final double SELECTION_STROKE_WIDTH = 1;
+    private static final Color SELECTION_STROKE_COLOR = requireNonNull(Color.BLUE);
+    private static final Color SELECTION_FILL_COLOR = requireNonNull(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.4));
+
+    /**
+     * These events are to be ignored by the time graph pane, they should
+     * "bubble up" to the scrollpane to be used for panning.
+     */
+    private static final Predicate<MouseEvent> MOUSE_EVENT_IGNORED = e -> {
+        return (e.getButton() == MouseButton.SECONDARY
+                || e.getButton() == MouseButton.MIDDLE
+                || e.isControlDown());
+    };
+
+    private final Rectangle fSelectionRect = new Rectangle();
+    private final Rectangle fOngoingSelectionRect = new Rectangle();
+
+    private final SelectionContext fSelectionCtx = new SelectionContext();
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            The corresponding time graph widget
+     * @param parentGroup
+     *            The group to which this layer should add its children
+     */
+    public TimeGraphSelectionLayer(TimeGraphWidget widget, Group parentGroup) {
+        super(widget, parentGroup);
+
+        final Pane timeGraphPane = getWidget().getTimeGraphPane();
+
+        fSelectionRect.setStroke(SELECTION_STROKE_COLOR);
+        fSelectionRect.setStrokeWidth(SELECTION_STROKE_WIDTH);
+        fSelectionRect.setStrokeLineCap(StrokeLineCap.ROUND);
+
+        Stream.of(fSelectionRect, fOngoingSelectionRect).forEach(rect -> {
+            rect.setMouseTransparent(true);
+            rect.setFill(SELECTION_FILL_COLOR);
+
+            /*
+             * We keep the 'x' property at 0, and we'll use 'layoutX' to set the
+             * start position of the rectangles.
+             *
+             * See https://github.com/lttng/lttng-scope/issues/25
+             */
+            rect.xProperty().bind(JfxUtils.ZERO_PROPERTY);
+            rect.yProperty().bind(JfxUtils.ZERO_PROPERTY);
+            rect.heightProperty().bind(timeGraphPane.heightProperty());
+        });
+
+        /*
+         * Note, unlike most other controls, we will not add/remove children to
+         * the target group, we will add them once then toggle their 'visible'
+         * property.
+         */
+        fSelectionRect.setVisible(true);
+        fOngoingSelectionRect.setVisible(false);
+        getParentGroup().getChildren().addAll(fSelectionRect, fOngoingSelectionRect);
+
+        timeGraphPane.addEventHandler(MouseEvent.MOUSE_PRESSED, fSelectionCtx.fMousePressedEventHandler);
+        timeGraphPane.addEventHandler(MouseEvent.MOUSE_DRAGGED, fSelectionCtx.fMouseDraggedEventHandler);
+        timeGraphPane.addEventHandler(MouseEvent.MOUSE_RELEASED, fSelectionCtx.fMouseReleasedEventHandler);
+    }
+
+    /**
+     * Get the rectangle object representing the current selection range.
+     *
+     * @return The current selection rectangle
+     */
+    public Rectangle getSelectionRectangle() {
+        return fSelectionRect;
+    }
+
+    /**
+     * Get the rectangle object representing the ongoing selection. It is
+     * displayed while the user holds the mouse down and drags to make a
+     * selection, but before the mouse is released. The "real" selection is only
+     * applied on mouse release.
+     *
+     * @return The ongoing selection rectangle
+     */
+    public Rectangle getOngoingSelectionRectangle() {
+        return fOngoingSelectionRect;
+    }
+
+    @Override
+    public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task) {
+        drawSelection(timeRange);
+    }
+
+    @Override
+    public void clear() {
+        /* We don't have to clear anything */
+    }
+
+    /**
+     * Draw a new "current" selection. For times where the selection is updated
+     * elsewhere in the framework.
+     *
+     * @param timeRange
+     *            The time range of the new selection
+     */
+    public void drawSelection(TimeRange timeRange) {
+        double xStart = getWidget().timestampToPaneXPos(timeRange.getStart());
+        double xEnd = getWidget().timestampToPaneXPos(timeRange.getEnd());
+        double xWidth = xEnd - xStart;
+
+        fSelectionRect.setLayoutX(xStart);
+        fSelectionRect.setWidth(xWidth);
+
+        fSelectionRect.setVisible(true);
+    }
+
+    /**
+     * Class encapsulating the time range selection, related drawing and
+     * listeners.
+     */
+    private class SelectionContext {
+
+        private boolean fOngoingSelection;
+        private double fMouseOriginX;
+
+        public final EventHandler<MouseEvent> fMousePressedEventHandler = e -> {
+            if (MOUSE_EVENT_IGNORED.test(e)) {
+                return;
+            }
+            e.consume();
+
+            if (fOngoingSelection) {
+                return;
+            }
+
+            /* Remove the current selection, if there is one */
+            fSelectionRect.setVisible(false);
+
+            fMouseOriginX = e.getX();
+
+            fOngoingSelectionRect.setLayoutX(fMouseOriginX);
+            fOngoingSelectionRect.setWidth(0);
+            fOngoingSelectionRect.setVisible(true);
+
+            fOngoingSelection = true;
+        };
+
+        public final EventHandler<MouseEvent> fMouseDraggedEventHandler = e -> {
+            if (MOUSE_EVENT_IGNORED.test(e)) {
+                return;
+            }
+            e.consume();
+
+            double newX = e.getX();
+            double offsetX = newX - fMouseOriginX;
+
+            if (offsetX > 0) {
+                fOngoingSelectionRect.setLayoutX(fMouseOriginX);
+                fOngoingSelectionRect.setWidth(offsetX);
+            } else {
+                fOngoingSelectionRect.setLayoutX(newX);
+                fOngoingSelectionRect.setWidth(-offsetX);
+            }
+
+        };
+
+        public final EventHandler<MouseEvent> fMouseReleasedEventHandler = e -> {
+            if (MOUSE_EVENT_IGNORED.test(e)) {
+                return;
+            }
+            e.consume();
+
+            fOngoingSelectionRect.setVisible(false);
+
+            /* Send a time range selection signal for the currently selected time range */
+            double startX = Math.max(0, fOngoingSelectionRect.getLayoutX());
+            // FIXME Possible glitch when selecting backwards outside of the window
+            double endX = Math.min(getWidget().getTimeGraphPane().getWidth(), startX + fOngoingSelectionRect.getWidth());
+            long tsStart = getWidget().paneXPosToTimestamp(startX);
+            long tsEnd = getWidget().paneXPosToTimestamp(endX);
+
+            getWidget().getControl().updateTimeRangeSelection(TimeRange.of(tsStart, tsEnd));
+
+            fOngoingSelection = false;
+        };
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphStateLayer.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/TimeGraphStateLayer.java
new file mode 100644 (file)
index 0000000..28ffe61
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.FutureTask;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.ITimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.TimeGraphStateRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.StateRectangle;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.VerticalPosition;
+
+import javafx.application.Platform;
+import javafx.scene.Group;
+import javafx.scene.Node;
+import javafx.scene.control.OverrunStyle;
+import javafx.scene.paint.Color;
+import javafx.scene.text.Font;
+import javafx.scene.text.Text;
+
+/**
+ * Time graph layer taking care of drawing the state intervals and their labels.
+ *
+ * Intervals and labels are part of separate Groups, which the time graph widget
+ * can stack in the correct order. This ensures that the labels are always shown
+ * on top of the states.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class TimeGraphStateLayer extends TimeGraphLayer {
+
+    private final Group fLabelGroup = new Group();
+    private final ITimeGraphModelStateProvider fStateProvider;
+
+    private TimeRange fWindowRange;
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            Time graph widget to which this layer belongs
+     * @param parentGroup
+     *            The group to which this layer should add its children
+     */
+    public TimeGraphStateLayer(TimeGraphWidget widget, Group parentGroup) {
+        super(widget, parentGroup);
+
+        fStateProvider = widget.getControl().getModelRenderProvider().getStateProvider();
+        /*
+         * Initially we'll set our window range to the one from the view
+         * context, afterwards we'll let the widget update it.
+         */
+        fWindowRange = widget.getControl().getViewContext().getCurrentVisibleTimeRange();
+    }
+
+    /**
+     * Return the separate Group used to hold the state labels.
+     *
+     * @return The group of state labels
+     */
+    public Group getLabelGroup() {
+        return fLabelGroup;
+    }
+
+    /**
+     * Update the window range tracked by this layer. This will be used to
+     * "clamp" the state labels to the current visible window in case the state
+     * intervals are larger than it.
+     *
+     * It may or may not be the same as the current UI visible range.
+     *
+     * @param windowRange
+     *            The new window range
+     */
+    public void setWindowRange(TimeRange windowRange) {
+        fWindowRange = windowRange;
+    }
+
+    @Override
+    public void drawContents(TimeGraphTreeRender treeRender, TimeRange timeRange,
+            VerticalPosition vPos, @Nullable FutureTask<?> task) {
+
+        final long resolution = Math.max(1, Math.round(getWidget().getCurrentNanosPerPixel()));
+        final List<TimeGraphTreeElement> allTreeElements = treeRender.getAllTreeElements();
+        final int nbElements = allTreeElements.size();
+        final int entriesToPrefetch = getWidget().getDebugOptions().entryPadding.get();
+        final int topEntry = Math.max(0,
+                TimeGraphWidget.paneYPosToEntryListIndex(vPos.fTopPos, TimeGraphWidget.ENTRY_HEIGHT) - entriesToPrefetch);
+        final int bottomEntry = Math.min(nbElements,
+                TimeGraphWidget.paneYPosToEntryListIndex(vPos.fBottomPos, TimeGraphWidget.ENTRY_HEIGHT) + entriesToPrefetch);
+
+        System.out.println("topEntry=" + topEntry +", bottomEntry=" + bottomEntry);
+
+        List<TimeGraphStateRender> stateRenders = allTreeElements.subList(topEntry, bottomEntry).stream()
+                .map(treeElem -> fStateProvider.getStateRender(treeElem, timeRange, resolution, task))
+                .collect(Collectors.toList());
+
+        if (task != null && task.isCancelled()) {
+            return;
+        }
+
+        Collection<StateRectangle> stateRectangles = prepareStateRectangles(stateRenders, topEntry);
+        Node statesLayerContents = prepareTimeGraphStatesContents(stateRectangles);
+        Node labelsLayerContents = prepareTimeGrahLabelsContents(stateRectangles, fWindowRange);
+
+        /*
+         * Go over all state rectangles, and bring the "multi-state"
+         * ones to the front, to be sure they show on top of the others.
+         * Note we cannot do the forEach() as part of the stream, that
+         * would throw a ConcurrentModificationException.
+         */
+        ((Group) statesLayerContents).getChildren().stream()
+                .map(node -> (StateRectangle) node)
+                .filter(rect -> (rect.getStateInterval().isMultiState()))
+                .collect(Collectors.toList())
+                .forEach(Node::toFront);
+
+        Platform.runLater(() -> {
+            getParentGroup().getChildren().clear();
+            getLabelGroup().getChildren().clear();
+
+            getParentGroup().getChildren().add(statesLayerContents);
+            getLabelGroup().getChildren().add(labelsLayerContents);
+        });
+    }
+
+    @Override
+    public void clear() {
+        JfxUtils.runOnMainThread(() -> {
+            getParentGroup().getChildren().clear();
+            getLabelGroup().getChildren().clear();
+        });
+    }
+
+    private Collection<StateRectangle> prepareStateRectangles(
+            List<TimeGraphStateRender> stateRenders, int topEntry) {
+        /* Prepare the colored state rectangles */
+        Collection<StateRectangle> rectangles = IntStream.range(0, stateRenders.size()).parallel()
+                .mapToObj(idx -> getRectanglesForStateRender(stateRenders.get(idx), idx + topEntry))
+                .flatMap(Function.identity())
+                .collect(Collectors.toSet());
+        return rectangles;
+    }
+
+    private Stream<StateRectangle> getRectanglesForStateRender(TimeGraphStateRender stateRender, int entryIndex) {
+        return stateRender.getStateIntervals().stream()
+                .map(interval -> new StateRectangle(getWidget(), interval, entryIndex));
+    }
+
+    private static Node prepareTimeGraphStatesContents(Collection<StateRectangle> stateRectangles) {
+        Group group = new Group();
+        group.getChildren().addAll(stateRectangles);
+        return group;
+    }
+
+    private Node prepareTimeGrahLabelsContents(Collection<StateRectangle> stateRectangles,
+            TimeRange windowRange) {
+        double minX = getWidget().timestampToPaneXPos(windowRange.getStart());
+
+        final String ellipsisStr = DebugOptions.ELLIPSIS_STRING;
+        final double ellipsisWidth = getWidget().getDebugOptions().getEllipsisWidth();
+        final Font textFont = getWidget().getDebugOptions().stateLabelFont.get();
+        final OverrunStyle overrunStyle = OverrunStyle.ELLIPSIS;
+        final Color textColor = Color.WHITE;
+
+        /* Requires a ~2 pixels adjustment to be centered on the states */
+        final double yOffset = TimeGraphWidget.ENTRY_HEIGHT / 2.0 + 2.0;
+        Collection<Node> texts = stateRectangles.stream()
+                /* Only try to annotate rectangles that are large enough */
+                .filter(stateRect -> stateRect.getWidth() > ellipsisWidth)
+                .filter(stateRect -> stateRect.getStateInterval().getLabel() != null)
+                .map(stateRect -> {
+                    String labelText = requireNonNull(stateRect.getStateInterval().getLabel());
+                    /* A small offset looks better here */
+                    double textX = Math.max(minX, stateRect.getX()) + 4.0;
+                    double textY = stateRect.getY() + yOffset;
+
+                    double rectEndX = stateRect.getX() + stateRect.getWidth();
+                    double minWidth = rectEndX - textX;
+
+                    String ellipsedText = JfxUtils.computeClippedText(textFont,
+                            labelText,
+                            minWidth,
+                            overrunStyle,
+                            ellipsisStr);
+
+                    if (ellipsedText.equals(ellipsisStr)) {
+                        return null;
+                    }
+
+                    Text text = new Text(textX, textY, ellipsedText);
+                    text.setFont(textFont);
+                    text.setFill(textColor);
+                    return text;
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        return new Group(texts);
+    }
+
+    /**
+     * Retrieve the state rectangles currently present in the scenegraph. This
+     * should include all currently visible ones, but also possibly more (due to
+     * padding, prefetching, etc.)
+     *
+     * @return The state rectangles
+     */
+    public Collection<StateRectangle> getRenderedStateRectangles() {
+        if (getParentGroup().getChildren().isEmpty()) {
+            return Collections.EMPTY_LIST;
+        }
+        Collection<?> stateRectangles = ((Group) getParentGroup().getChildren().get(0)).getChildren();
+        @SuppressWarnings("unchecked")
+        Collection<StateRectangle> ret = (@NonNull Collection<StateRectangle>) stateRectangles;
+        return ret;
+    }
+
+    /**
+     * Retrieve the state labels current drawn by this layer.
+     *
+     * @return The state labels
+     */
+    public Collection<Text> getRenderedStateLabels() {
+        if (fLabelGroup.getChildren().isEmpty()) {
+            return Collections.EMPTY_LIST;
+        }
+        /* The labels are wrapped in a group */
+        Collection<?> texts = ((Group) fLabelGroup.getChildren().get(0)).getChildren();
+        @SuppressWarnings("unchecked")
+        Collection<Text> ret = (@NonNull Collection<Text>) texts;
+        return ret;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/layer/package-info.java
new file mode 100644 (file)
index 0000000..9a77e2b
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/messages.properties
new file mode 100644 (file)
index 0000000..1b627ea
--- /dev/null
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+statePropertyElement = Element
+statePropertyStateName = State
+statePropertyStartTime = Start Time
+statePropertyEndTime = End Time
+statePropertyDuration = Duration
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/package-info.java
new file mode 100644 (file)
index 0000000..51b5655
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2016 EfficiOS Inc. and others
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ArrowSeriesMenuButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ArrowSeriesMenuButton.java
new file mode 100644 (file)
index 0000000..6d4b806
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.arrows.ITimeGraphModelArrowProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.arrows.TimeGraphArrowSeries;
+import org.lttng.scope.tmf2.views.ui.jfx.Arrow;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.CheckMenuItem;
+import javafx.scene.control.MenuButton;
+import javafx.scene.paint.Color;
+
+/**
+ * Menu-button for listing the available arrow series.
+ *
+ * The available arrow series come from the time graph model. More than one mode
+ * (or none) can be active at the same time, so we are using CheckMenuItems for
+ * the menu items.
+ *
+ * @author Alexandre Montplaisir
+ */
+class ArrowSeriesMenuButton extends MenuButton {
+
+    private static final double ARROW_GRAPHIC_LENGTH = 10;
+
+    public ArrowSeriesMenuButton(TimeGraphWidget widget) {
+        ITimeGraphModelProvider modelProvider = widget.getControl().getModelRenderProvider();
+        Collection<ITimeGraphModelArrowProvider> arrowProviders = modelProvider.getArrowProviders();
+
+        Collection<CheckMenuItem> arrowSeriesItems = arrowProviders.stream()
+                .map(arrowProvider -> {
+                    TimeGraphArrowSeries series = arrowProvider.getArrowSeries();
+                    String name = series.getSeriesName();
+                    Arrow graphic = getArrowGraphicForSeries(series);
+                    CheckMenuItem cmi = new CheckMenuItem(name, graphic);
+                    cmi.selectedProperty().bindBidirectional(arrowProvider.enabledProperty());
+                    return cmi;
+                })
+                .collect(Collectors.toList());
+
+        setText(Messages.arrowSeriesMenuButtonName);
+        getItems().addAll(arrowSeriesItems);
+
+        if (arrowSeriesItems.isEmpty()) {
+            setDisable(true);
+        }
+    }
+
+    private static Arrow getArrowGraphicForSeries(TimeGraphArrowSeries series) {
+        Color color = JfxColorFactory.getColorFromDef(series.getColor());
+        Arrow arrow = new Arrow(0, 0, ARROW_GRAPHIC_LENGTH, 0);
+        arrow.setStroke(color);
+        return arrow;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/FilterModeMenuButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/FilterModeMenuButton.java
new file mode 100644 (file)
index 0000000..d7208b7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import java.util.Collection;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider.FilterMode;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.CheckMenuItem;
+import javafx.scene.control.MenuButton;
+
+/**
+ * Menu-button for listing the filter modes.
+ *
+ * The available filter modes come from the time graph model. More than one mode
+ * (or none) can be active at the same time, so we are using CheckMenuItems for
+ * the menu items.
+ *
+ * @author Alexandre Montplaisir
+ */
+class FilterModeMenuButton extends MenuButton {
+
+    public FilterModeMenuButton(TimeGraphWidget viewer) {
+        ITimeGraphModelProvider provider = viewer.getControl().getModelRenderProvider();
+
+        Collection<CheckMenuItem> filterModeItems = IntStream.range(0, provider.getFilterModes().size())
+                .mapToObj(index -> {
+                    FilterMode fm = provider.getFilterModes().get(index);
+                    CheckMenuItem cmi = new CheckMenuItem(fm.getName());
+                    cmi.setOnAction(e -> {
+                        if (cmi.isSelected()) {
+                            /* Mode was enabled */
+                            provider.enableFilterMode(index);
+                        } else {
+                            /* Mode was disabled */
+                            provider.disableFilterMode(index);
+                        }
+                        viewer.getControl().repaintCurrentArea();
+                    });
+                    return cmi;
+                })
+                .collect(Collectors.toList());
+
+        setText(Messages.sfFilterModeMenuButtonName);
+        getItems().addAll(filterModeItems);
+
+        if (filterModeItems.isEmpty()) {
+            setDisable(true);
+        }
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/Messages.java
new file mode 100644 (file)
index 0000000..6bbda25
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String arrowSeriesMenuButtonName;
+
+    public static String sfFilterModeMenuButtonName;
+    public static String sfSortingModeMenuButtonName;
+
+    public static String sfZoomInActionDescription;
+    public static String sfZoomOutActionDescription;
+    public static String sfZoomToFullRangeActionDescription;
+    public static String sfZoomToSelectionActionDescription;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/SortingModeMenuButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/SortingModeMenuButton.java
new file mode 100644 (file)
index 0000000..68652f9
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider.SortingMode;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.MenuButton;
+import javafx.scene.control.RadioMenuItem;
+import javafx.scene.control.ToggleGroup;
+
+/**
+ * Menu button for selecting the sorting mode of tree entries.
+ *
+ * The available modes are defined by the time graph model. Only one mode can be
+ * active at a time, so we are using RadioMenuItems for the menu items.
+ *
+ * @author Alexandre Montplaisir
+ */
+class SortingModeMenuButton extends MenuButton {
+
+    public SortingModeMenuButton(TimeGraphWidget viewer) {
+        ITimeGraphModelProvider provider = viewer.getControl().getModelRenderProvider();
+
+        ToggleGroup tg = new ToggleGroup();
+        List<RadioMenuItem> sortingModeItems = IntStream.range(0, provider.getSortingModes().size())
+                .mapToObj(index -> {
+                    SortingMode sm = provider.getSortingModes().get(index);
+                    RadioMenuItem rmi = new RadioMenuItem(sm.getName());
+                    rmi.setToggleGroup(tg);
+                    rmi.setOnAction(e -> {
+                        provider.setCurrentSortingMode(index);
+                        viewer.getControl().repaintCurrentArea();
+                    });
+                    return rmi;
+                })
+                .collect(Collectors.toList());
+
+        if (!sortingModeItems.isEmpty()) {
+            /*
+             * Initialize the first mode to be selected, which is what the model
+             * does. This should not trigger the event handler.
+             */
+            sortingModeItems.get(0).setSelected(true);
+        }
+
+        setText(Messages.sfSortingModeMenuButtonName);
+        getItems().addAll(sortingModeItems);
+
+        /*
+         * There is at minimum the "default" sorting mode. Don't show the list
+         * if there is only that one.
+         */
+        if (sortingModeItems.size() <= 1) {
+            setDisable(true);
+        }
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ViewerToolBar.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ViewerToolBar.java
new file mode 100644 (file)
index 0000000..3b1af1c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.StateRectangle;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.debugopts.DebugOptionsButton;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents.EventSeriesMenuButton;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.modelconfig.ModelConfigButton;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav.NavigationButtons;
+
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.Separator;
+import javafx.scene.control.TextField;
+import javafx.scene.control.ToolBar;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+
+/**
+ * Toolbar for the time graph viewer.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ViewerToolBar extends ToolBar {
+
+    private static final String HELP_ICON_PATH = "/icons/toolbar/help.gif"; //$NON-NLS-1$
+
+    /**
+     * Constructor
+     *
+     * @param viewer
+     *            The time graph viewer to which this toolbar belongs.
+     */
+    public ViewerToolBar(TimeGraphWidget viewer) {
+        super();
+
+        NavigationButtons navButtons = new NavigationButtons(viewer);
+
+        getItems().addAll(
+                new Label(viewer.getName()),
+                new Separator(),
+
+                new ZoomInButton(viewer),
+                new ZoomOutButton(viewer),
+                new ZoomToSelectionButton(viewer),
+                new ZoomToFullRangeButton(viewer),
+                new Separator(),
+
+                new HBox(
+                    navButtons.getBackButton(),
+                    navButtons.getForwardButton(),
+                    navButtons.getMenuButton()
+                ),
+                getStateInfoButton(viewer),
+                new Separator(),
+
+                new ModelConfigButton(viewer),
+                new ArrowSeriesMenuButton(viewer),
+                new EventSeriesMenuButton(viewer),
+                new SortingModeMenuButton(viewer),
+                new FilterModeMenuButton(viewer),
+                new Separator(),
+
+                new DebugOptionsButton(viewer));
+    }
+
+    // FIXME Temporary, should be moved to tooltip
+    private Button getStateInfoButton(TimeGraphWidget viewer) {
+        Button button = new Button();
+        Image helpIcon = JfxImageFactory.instance().getImageFromResource(HELP_ICON_PATH);
+        button.setGraphic(new ImageView(helpIcon));
+        button.setTooltip(new Tooltip("Get State Info")); //$NON-NLS-1$
+        button.setOnAction(e -> {
+            StateRectangle state = viewer.getSelectedState();
+            if (state == null) {
+                return;
+            }
+            Alert alert = new Alert(AlertType.INFORMATION);
+            /* Use a read-only TextField so the text can be copy-pasted */
+            TextField content = new TextField(state.toString());
+            content.setEditable(false);
+            content.setPrefWidth(1000.0);
+            alert.getDialogPane().setContent(content);
+            alert.setResizable(true);
+            alert.show();
+
+            JfxUtils.centerDialogOnScreen(alert, this);
+        });
+        return button;
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomInButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomInButton.java
new file mode 100644 (file)
index 0000000..179cff3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button for zooming in. It should do the same action as one ctrl+mouse-scroll.
+ *
+ * @author Alexandre Montplaisir
+ */
+class ZoomInButton extends Button {
+
+    private static final String ZOOM_IN_ICON_PATH = "/icons/toolbar/zoom_in.gif"; //$NON-NLS-1$
+
+    public ZoomInButton(TimeGraphWidget viewer) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(ZOOM_IN_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.sfZoomInActionDescription));
+        setOnAction(e -> {
+            viewer.getZoomActions().zoom(true, false, null);
+        });
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomOutButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomOutButton.java
new file mode 100644 (file)
index 0000000..56f5803
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button for zooming out. It should do the same action as one
+ * ctrl+mouse-scroll.
+ *
+ * @author Alexandre Montplaisir
+ */
+class ZoomOutButton extends Button {
+
+    private static final String ZOOM_OUT_ICON_PATH = "/icons/toolbar/zoom_out.gif"; //$NON-NLS-1$
+
+    public ZoomOutButton(TimeGraphWidget viewer) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(ZOOM_OUT_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.sfZoomOutActionDescription));
+        setOnAction(e -> {
+            viewer.getZoomActions().zoom(false, false, null);
+        });
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToFullRangeButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToFullRangeButton.java
new file mode 100644 (file)
index 0000000..4bc61da
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button to zoom (out) to the full trace's range.
+ *
+ * @author Alexandre Montplaisir
+ */
+class ZoomToFullRangeButton extends Button {
+
+    private static final String ZOOM_TO_FULL_RANGE_ICON_PATH = "/icons/toolbar/zoom_full.gif"; //$NON-NLS-1$
+
+    public ZoomToFullRangeButton(TimeGraphWidget viewer) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(ZOOM_TO_FULL_RANGE_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.sfZoomToFullRangeActionDescription));
+        setOnAction(e -> {
+            TimeRange fullRange = viewer.getControl().getViewContext().getCurrentTraceFullRange();
+            viewer.getControl().updateVisibleTimeRange(fullRange, true);
+        });
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToSelectionButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/ZoomToSelectionButton.java
new file mode 100644 (file)
index 0000000..9afa98f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
+
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button to zoom into the current selection, if there is one.
+ *
+ * @author Alexandre Montplaisir
+ */
+class ZoomToSelectionButton extends Button {
+
+    // TODO Get an icon!
+    private static final String ZOOM_TO_SELECTION_ICON_PATH = "/icons/toolbar/zoom_in.gif"; //$NON-NLS-1$
+
+    public ZoomToSelectionButton(TimeGraphWidget viewer) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(ZOOM_TO_SELECTION_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.sfZoomToSelectionActionDescription));
+        setOnAction(e -> {
+            TmfTimeRange range = TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange();
+            TimeRange timeRange = TimeRange.fromTmfTimeRange(range);
+            /*
+             * Only actually zoom if the selection is a time range, not a single
+             * timestamp.
+             */
+            if (timeRange.getDuration() > 0) {
+                viewer.getControl().updateVisibleTimeRange(timeRange, true);
+            }
+        });
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsButton.java
new file mode 100644 (file)
index 0000000..3fcc182
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.debugopts;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.scene.control.Button;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button to open the debug options dialog.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class DebugOptionsButton extends Button {
+
+    private static final String CONFIG_ICON_PATH = "/icons/toolbar/config.gif"; //$NON-NLS-1$
+
+    /*
+     * Since the button is more persistent than the dialog, track state like the
+     * last-selected tab in here.
+     */
+    private final IntegerProperty fLastSelectedDialogTab = new SimpleIntegerProperty(0);
+
+    private final DebugOptions fOpts;
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            The time graph widget to which this toolbar button is
+     *            associated.
+     */
+    public DebugOptionsButton(TimeGraphWidget widget) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(CONFIG_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.debugOptionsDialogName));
+
+        fOpts = widget.getDebugOptions();
+
+        setOnAction(e -> {
+            Dialog<@Nullable Void> dialog = new DebugOptionsDialog(this);
+            dialog.show();
+            JfxUtils.centerDialogOnScreen(dialog, DebugOptionsButton.this);
+        });
+    }
+
+    DebugOptions getDebugOptions() {
+        return fOpts;
+    }
+
+    IntegerProperty lastSelectedDialogProperty() {
+        return fLastSelectedDialogTab;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsDialog.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/DebugOptionsDialog.java
new file mode 100644 (file)
index 0000000..1513cac
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.debugopts;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.ui.timeline.DebugOptions;
+
+import com.google.common.primitives.Doubles;
+import com.google.common.primitives.Ints;
+
+import javafx.event.ActionEvent;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ColorPicker;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.Label;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+
+/**
+ * Dialog to configure the debug options at runtime.
+ *
+ * @author Alexandre Montplaisir
+ */
+class DebugOptionsDialog extends Dialog<@Nullable Void> {
+
+    private static final Insets PADDING = new Insets(20.0);
+    private static final double SPACING = 10.0;
+
+    private final DebugOptions fOpts;
+    private final TabPane fTabPane;
+
+    public DebugOptionsDialog(DebugOptionsButton button) {
+        fOpts = button.getDebugOptions();
+
+        setTitle(Messages.debugOptionsDialogTitle);
+        setHeaderText(Messages.debugOptionsDialogName);
+
+        ButtonType resetToDefaultButtonType = new ButtonType(Messages.resetDefaultsButtonLabel, ButtonData.LEFT);
+        getDialogPane().getButtonTypes().addAll(resetToDefaultButtonType, ButtonType.CANCEL, ButtonType.OK);
+
+        fTabPane = new TabPane(getGeneralTab(),
+                getLoadingOverlayTab(),
+                getZoomTab(),
+                getStateIntervalsTab(),
+                getTooltipTab());
+        getDialogPane().setContent(fTabPane);
+
+        /*
+         * Restore the last-selected tab (that state is saved in the button),
+         * and re-bind on the new dialog so that the property continues getting
+         * updated.
+         */
+        fTabPane.getSelectionModel().select(button.lastSelectedDialogProperty().get());
+        button.lastSelectedDialogProperty().bind(fTabPane.getSelectionModel().selectedIndexProperty());
+
+        /* What to do when the dialog is closed */
+        setResultConverter(dialogButton -> {
+            fTabPane.getSelectionModel().getSelectedIndex();
+
+            if (dialogButton != ButtonType.OK) {
+                return null;
+            }
+            /*
+             * Set the debug options according to the current contents of the
+             * dialog.
+             */
+            getAllPropertySetters().forEach(PropertySetter::save);
+
+            return null;
+        });
+
+        /* Define how to "Reset Defaults" button works */
+        getDialogPane().lookupButton(resetToDefaultButtonType).addEventFilter(ActionEvent.ACTION, e -> {
+            /*
+             * This button should not close the dialog. Consuming the event here
+             * will prevent the dialog from closing.
+             */
+            e.consume();
+
+            getAllPropertySetters().forEach(ps -> {
+                ConfigOption<?> option = ps.getOption();
+                option.resetToDefault();
+                ps.load();
+            });
+
+        });
+
+    }
+
+    private Stream<PropertySetter> getAllPropertySetters() {
+        return fTabPane.getTabs().stream()
+                .flatMap(tab -> ((VBox) tab.getContent()).getChildren().stream())
+                .map(e -> requireNonNull((PropertySetter) e));
+    }
+
+    // ------------------------------------------------------------------------
+    // Tab classes
+    // ------------------------------------------------------------------------
+
+    private static class DebugOptionsDialogTab extends Tab {
+
+        public DebugOptionsDialogTab(@Nullable String name, Node... contents) {
+            VBox page = new VBox(contents);
+            page.setPadding(PADDING);
+            page.setSpacing(SPACING);
+
+            setClosable(false);
+            setText(name);
+            setContent(page);
+        }
+
+    }
+
+    private Tab getGeneralTab() {
+        return new DebugOptionsDialogTab(Messages.tabNameGeneral,
+                new CheckBoxControl(Messages.controlPaintingEnabled, fOpts.isPaintingEnabled),
+                new IntegerTextField(Messages.controlEntryPadding, fOpts.entryPadding),
+                new DoubleTextField(Messages.controlRenderRangePadding, fOpts.renderRangePadding),
+                new IntegerTextField(Messages.controlUIUpdateDelay, fOpts.uiUpdateDelay),
+                new CheckBoxControl(Messages.controlHScrollEnabled, fOpts.isScrollingListenersEnabled));
+    }
+
+    private Tab getLoadingOverlayTab() {
+        return new DebugOptionsDialogTab(Messages.tabNameLoadingOverlay,
+                new CheckBoxControl(Messages.controlLoadingOverlayEnabled, fOpts.isLoadingOverlayEnabled),
+                new ColorControl(Messages.controlLoadingOverlayColor, fOpts.loadingOverlayColor),
+                new DoubleTextField(Messages.controlLoadingOverlayFullOpacity, fOpts.loadingOverlayFullOpacity),
+//                new DoubleTextField(Messages.controlLoadingOverlayTransparentOpacity, fOpts.loadingOverlayTransparentOpacity),
+                new DoubleTextField(Messages.controlLoadingOverlayFadeIn, fOpts.loadingOverlayFadeInDuration),
+                new DoubleTextField(Messages.controlLoadingOverlayFadeOut, fOpts.loadingOverlayFadeOutDuration));
+    }
+
+    private Tab getZoomTab() {
+        return new DebugOptionsDialogTab(Messages.tabNameZoom,
+                new IntegerTextField(Messages.controlZoomAnimationDuration + " (unused)", fOpts.zoomAnimationDuration), //$NON-NLS-1$
+                new DoubleTextField(Messages.controlZoomStep, fOpts.zoomStep),
+                new CheckBoxControl(Messages.controlZoomPivotOnSelection, fOpts.zoomPivotOnSelection),
+                new CheckBoxControl(Messages.controlZoomPivotOnMousePosition, fOpts.zoomPivotOnMousePosition));
+    }
+
+    private Tab getStateIntervalsTab() {
+        return new DebugOptionsDialogTab(Messages.tabNameIntervals,
+                new DoubleTextField(Messages.controlIntervalOpacity, fOpts.stateIntervalOpacity)
+                // multi-state Paint ?
+                // state label Font ?
+                );
+    }
+
+    private Tab getTooltipTab() {
+        return new DebugOptionsDialogTab(Messages.tabNameTooltips,
+                // Tooltip Font picker
+                new ColorControl(Messages.controlTooltipFontColor, fOpts.toolTipFontFill));
+    }
+
+    // ------------------------------------------------------------------------
+    // Property-setting controls
+    // ------------------------------------------------------------------------
+
+    private static interface PropertySetter {
+        ConfigOption<?> getOption();
+        void load();
+        void save();
+    }
+
+    private static class CheckBoxControl extends CheckBox implements PropertySetter {
+
+        private final ConfigOption<Boolean> fOption;
+
+        public CheckBoxControl(@Nullable String labelText, ConfigOption<Boolean> option) {
+            fOption = option;
+            setText(labelText);
+            load();
+        }
+
+        @Override
+        public ConfigOption<?> getOption() {
+            return fOption;
+        }
+
+        @Override
+        public void load() {
+            setSelected(fOption.get());
+        }
+
+        @Override
+        public void save() {
+            fOption.set(isSelected());
+        }
+    }
+
+    private static class ColorControl extends HBox implements PropertySetter {
+
+        private final ConfigOption<Color> fOption;
+        private final ColorPicker fColorPicker;
+
+        public ColorControl(@Nullable String labelText, ConfigOption<Color> option) {
+            fOption = option;
+
+            Label label = new Label(labelText + ":"); //$NON-NLS-1$
+            fColorPicker = new ColorPicker(option.get());
+
+            getChildren().addAll(label, fColorPicker);
+            setAlignment(Pos.CENTER_LEFT);
+            setSpacing(SPACING);
+        }
+
+        @Override
+        public ConfigOption<?> getOption() {
+            return fOption;
+        }
+
+        @Override
+        public void load() {
+            fColorPicker.setValue(fOption.get());
+        }
+
+        @Override
+        public void save() {
+            Color color = fColorPicker.getValue();
+            if (color != null) {
+                fOption.set(color);
+            }
+        }
+    }
+
+    private static abstract class TextFieldControl<T extends Number> extends HBox implements PropertySetter {
+
+        private final ConfigOption<T> fOption;
+        private final TextField fTextField = new TextField();
+
+        protected TextFieldControl(@Nullable String labelText, ConfigOption<T> option) {
+            fOption = option;
+
+            Label label = new Label(labelText + ":"); //$NON-NLS-1$
+            load();
+
+            getChildren().addAll(label, fTextField);
+            setAlignment(Pos.CENTER_LEFT);
+            setSpacing(SPACING);
+        }
+
+        @Override
+        public ConfigOption<T> getOption() {
+            return fOption;
+        }
+
+        @Override
+        public void load() {
+            fTextField.setText(fOption.get().toString());
+        }
+
+        @Override
+        public abstract void save();
+
+        protected TextField getTextField() {
+            return fTextField;
+        }
+    }
+
+    private static class IntegerTextField extends TextFieldControl<Integer> {
+
+        public IntegerTextField(@Nullable String labelText, ConfigOption<Integer> option) {
+            super(labelText, option);
+        }
+
+        @Override
+        public void save() {
+            String text = getTextField().getText();
+            Integer value = Ints.tryParse(text);
+            if (value == null) {
+                getOption().resetToDefault();
+                load();
+            } else {
+                getOption().set(value);
+            }
+        }
+    }
+
+    private static class DoubleTextField extends TextFieldControl<Double> {
+
+        public DoubleTextField(@Nullable String labelText, ConfigOption<Double> option) {
+            super(labelText, option);
+        }
+
+        @Override
+        public void save() {
+            String text = getTextField().getText();
+            Double value = Doubles.tryParse(text);
+            if (value == null) {
+                getOption().resetToDefault();
+                load();
+            } else {
+                getOption().set(value);
+            }
+        }
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/Messages.java
new file mode 100644 (file)
index 0000000..d8a9ddf
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.debugopts;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String debugOptionsDialogTitle;
+    public static String debugOptionsDialogName;
+
+    public static String resetDefaultsButtonLabel;
+
+    public static String tabNameGeneral;
+    public static String controlPaintingEnabled;
+    public static String controlEntryPadding;
+    public static String controlRenderRangePadding;
+    public static String controlUIUpdateDelay;
+    public static String controlHScrollEnabled;
+
+    public static String tabNameLoadingOverlay;
+    public static String controlLoadingOverlayEnabled;
+    public static String controlLoadingOverlayColor;
+    public static String controlLoadingOverlayFullOpacity;
+    public static String controlLoadingOverlayTransparentOpacity;
+    public static String controlLoadingOverlayFadeIn;
+    public static String controlLoadingOverlayFadeOut;
+
+    public static String tabNameZoom;
+    public static String controlZoomAnimationDuration;
+    public static String controlZoomStep;
+    public static String controlZoomPivotOnSelection;
+    public static String controlZoomPivotOnMousePosition;
+
+    public static String tabNameIntervals;
+    public static String controlIntervalOpacity;
+
+    public static String tabNameTooltips;
+    public static String controlTooltipFontColor;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/messages.properties
new file mode 100644 (file)
index 0000000..c4b61d9
--- /dev/null
@@ -0,0 +1,40 @@
+###############################################################################
+# Copyright (c) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+debugOptionsDialogTitle = Bikeshedding
+debugOptionsDialogName = Advanced View Configuration
+
+resetDefaultsButtonLabel = Reset Defaults
+
+tabNameGeneral = General
+controlPaintingEnabled = Painting enabled
+controlEntryPadding = Entry Padding
+controlRenderRangePadding = Render time range padding
+controlUIUpdateDelay = UI Update Delay (ms)
+controlHScrollEnabled = HScrolling listener enabled
+
+tabNameLoadingOverlay = Loading Overlay
+controlLoadingOverlayEnabled = Loading overlay enabled
+controlLoadingOverlayColor = Overlay color
+controlLoadingOverlayFullOpacity = Full Opacity
+controlLoadingOverlayTransparentOpacity = Transparent Opacity
+controlLoadingOverlayFadeIn = Fade-in duration (ms)
+controlLoadingOverlayFadeOut = Fade-out duration (ms)
+
+tabNameZoom = Zoom
+controlZoomAnimationDuration = Zoom animation duration (ms)
+controlZoomStep = Zoom step
+controlZoomPivotOnSelection = Zoom pivot on selection
+controlZoomPivotOnMousePosition = Zoom pivot on mouse position
+
+tabNameIntervals = Intervals
+controlIntervalOpacity = State interval opacity
+
+tabNameTooltips = Tooltips
+controlTooltipFontColor = Font color
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/debugopts/package-info.java
new file mode 100644 (file)
index 0000000..0e21319
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.debugopts;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/CreateEventSeriesDialog.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/CreateEventSeriesDialog.java
new file mode 100644 (file)
index 0000000..8a573e7
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents;
+
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries.SymbolStyle;
+import org.lttng.scope.tmf2.views.ui.jfx.CountingGridPane;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphDrawnEventLayer;
+
+import javafx.beans.property.ReadOnlyProperty;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.ColorPicker;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.ContentDisplay;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Shape;
+import javafx.scene.text.Font;
+import javafx.scene.text.FontPosture;
+import javafx.scene.text.FontWeight;
+import javafx.util.Callback;
+
+class CreateEventSeriesDialog extends Dialog<@Nullable PredicateDrawnEventProvider> {
+
+    private static final Color DEFAULT_SYMBOL_COLOR = Color.ORANGE;
+    private static final double PADDING = 10;
+
+    private static final Font HEADER_FONT = Font.font(null, FontWeight.BOLD, FontPosture.REGULAR, -1);
+    private static final Insets HEADER_PADDING = new Insets(10, 0, 10, 0);
+
+    private final TextField fEventNameField;
+
+    private final ColorPicker fSymbolColorPicker;
+    private final ShapePicker fSymbolShapePicker;
+
+    public CreateEventSeriesDialog(ITimeGraphModelProvider modelProvider) {
+        setTitle(Messages.createEventSeriesDialogTitle);
+
+        /* Dialog buttons, standard "OK" and "Cancel" */
+        getDialogPane().getButtonTypes().addAll(ButtonType.CANCEL, ButtonType.OK);
+
+        /* Dialog contents */
+        Label filterHeader = new Label(Messages.createEventSeriesDialogSectionFilterDef);
+        filterHeader.setFont(HEADER_FONT);
+        filterHeader.setPadding(HEADER_PADDING);
+
+        fEventNameField = new TextField();
+        CountingGridPane filterGrid = new CountingGridPane();
+        filterGrid.setHgap(PADDING);
+        filterGrid.setVgap(PADDING);
+        filterGrid.appendRow(new Label(Messages.createEventSeriesDialogFieldEventName), fEventNameField);
+
+        Label symbolHeader = new Label(Messages.createEventSeriesDialogSectionSymbolDef);
+        symbolHeader.setFont(HEADER_FONT);
+        symbolHeader.setPadding(HEADER_PADDING);
+
+        fSymbolColorPicker = new ColorPicker(DEFAULT_SYMBOL_COLOR);
+        fSymbolShapePicker = new ShapePicker(fSymbolColorPicker.valueProperty());
+        CountingGridPane symbolGrid = new CountingGridPane();
+        symbolGrid.setHgap(PADDING);
+        symbolGrid.setVgap(PADDING);
+        symbolGrid.appendRow(new Label(Messages.createEventSeriesDialogFieldColor), fSymbolColorPicker);
+        symbolGrid.appendRow(new Label(Messages.createEventSeriesDialogFieldShape), fSymbolShapePicker);
+
+        VBox vbox = new VBox(filterHeader, filterGrid, symbolHeader, symbolGrid);
+        vbox.setAlignment(Pos.CENTER);
+        getDialogPane().setContent(vbox);
+
+        /*
+         * Disable the OK button until the input is valid
+         *
+         * TODO ControlsFX Validation framework might be useful when more
+         * fields/options are added.
+         */
+        final Button okButton = (Button) getDialogPane().lookupButton(ButtonType.OK);
+        okButton.setDisable(true);
+        fEventNameField.textProperty().addListener((observable, oldValue, newValue) -> {
+            okButton.setDisable(newValue.trim().length() <= 0);
+        });
+
+        /* What to do when the dialog is closed */
+        setResultConverter(dialogButton -> {
+            if (dialogButton != ButtonType.OK) {
+                return null;
+            }
+
+            String eventName = fEventNameField.getText();
+            if (eventName == null || eventName.isEmpty()) {
+                return null;
+            }
+
+            TimeGraphDrawnEventSeries series = generateEventSeries();
+            Predicate<ITmfEvent> predicate = event -> event.getName().equals(eventName);
+            return new PredicateDrawnEventProvider(series, modelProvider, predicate);
+        });
+
+    }
+
+    /**
+     * Generate an event series from the current value of the controls
+     *
+     * @return The corresponding event series
+     */
+    private TimeGraphDrawnEventSeries generateEventSeries() {
+        String seriesName = fEventNameField.getText();
+        ColorDefinition colorDef = JfxColorFactory.colorToColorDef(fSymbolColorPicker.getValue());
+        SymbolStyle style = fSymbolShapePicker.getSelectionModel().getSelectedItem();
+
+        return new TimeGraphDrawnEventSeries(
+                seriesName == null ? "" : seriesName, //$NON-NLS-1$
+                new ConfigOption<>(colorDef),
+                new ConfigOption<>(style));
+    }
+
+    private static class ShapePicker extends ComboBox<SymbolStyle> {
+
+        public ShapePicker(ReadOnlyProperty<Color> colorSource) {
+            getItems().addAll(SymbolStyle.values());
+
+            Callback<@Nullable ListView<SymbolStyle>, ListCell<SymbolStyle>> cellFactory =
+                    p -> new ListCell<SymbolStyle>() {
+                        {
+                            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
+                        }
+
+                        @Override
+                        protected void updateItem(SymbolStyle item, boolean empty) {
+                            super.updateItem(item, empty);
+                            if (empty) {
+                                setGraphic(null);
+                            } else {
+                                Node graphic = getGraphicFromSymbol(item, colorSource);
+                                setGraphic(graphic);
+                            }
+                        }
+                    };
+
+            setButtonCell(cellFactory.call(null));
+            setCellFactory(cellFactory);
+
+            /* Select the first symbol by default */
+            getSelectionModel().select(0);
+        }
+
+        private static Node getGraphicFromSymbol(SymbolStyle symbol, ReadOnlyProperty<Color> colorSource) {
+            Shape graphic = TimeGraphDrawnEventLayer.getShapeFromSymbol(symbol);
+            graphic.fillProperty().bind(colorSource);
+            return graphic;
+        }
+
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/EventSeriesMenuButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/EventSeriesMenuButton.java
new file mode 100644 (file)
index 0000000..8365c95
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents;
+
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents.ITimeGraphDrawnEventProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents.TimeGraphDrawnEventProviderManager;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.layer.TimeGraphDrawnEventLayer;
+
+import com.google.common.collect.Iterables;
+
+import javafx.application.Platform;
+import javafx.collections.ListChangeListener;
+import javafx.collections.SetChangeListener;
+import javafx.scene.control.CheckMenuItem;
+import javafx.scene.control.MenuButton;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.SeparatorMenuItem;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Shape;
+
+/**
+ * Menu button showing listing the existing drawn-event providers, with menu
+ * items to create/clear such providers.
+ *
+ * TODO This button and its related actions are independent from a time graph
+ * widget. They could be moved elsewhere in the UI at some point.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class EventSeriesMenuButton extends MenuButton {
+
+    private static final TimeGraphDrawnEventProviderManager PROVIDER_MANAGER =
+            TimeGraphDrawnEventProviderManager.instance();
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            Time graph widget to which this button refers
+     */
+    public EventSeriesMenuButton(TimeGraphWidget widget) {
+        setText(Messages.eventSeriesMenuButtonName);
+
+        MenuItem separator = new SeparatorMenuItem();
+
+        /*
+         * There are minimum 3 items in the menu. Whenever there are more (from
+         * registered providers), make the separator visible.
+         */
+        getItems().addListener((ListChangeListener<MenuItem>) change -> {
+            separator.visibleProperty().set(change.getList().size() > 3);
+        });
+
+        /* "Create New Series" menu item */
+        MenuItem addNewSeriesMenuItem = new MenuItem(Messages.newEventSeriesMenuItem);
+        addNewSeriesMenuItem.setOnAction(e -> {
+            ITimeGraphModelProvider modelProvider = widget.getControl().getModelRenderProvider();
+            CreateEventSeriesDialog dialog = new CreateEventSeriesDialog(modelProvider);
+            dialog.setOnShowing(h -> Platform.runLater(() -> JfxUtils.centerDialogOnScreen(dialog, EventSeriesMenuButton.this)));
+            Optional<@Nullable PredicateDrawnEventProvider> results = dialog.showAndWait();
+            ITimeGraphDrawnEventProvider provider = results.orElse(null);
+            if (provider != null) {
+                PROVIDER_MANAGER.getRegisteredProviders().add(provider);
+                provider.enabledProperty().set(true);
+            }
+        });
+
+        /* "Clear series" menu item */
+        MenuItem clearSeriesMenuItem = new MenuItem(Messages.clearEventSeriesMenuItem);
+        clearSeriesMenuItem.setOnAction(e -> {
+            // TODO Eventually we could track which providers were created from
+            // this button/dialog, and only clear those here.
+            PROVIDER_MANAGER.getRegisteredProviders().clear();
+        });
+
+        getItems().addAll(separator, addNewSeriesMenuItem, clearSeriesMenuItem);
+
+
+        /* Load the already-registered providers */
+        PROVIDER_MANAGER.getRegisteredProviders().forEach(this::addProviderToMenu);
+
+        /* Watch for future addition/removal of providers */
+        PROVIDER_MANAGER.getRegisteredProviders().addListener((SetChangeListener<ITimeGraphDrawnEventProvider>) change -> {
+            ITimeGraphDrawnEventProvider addedProvider = change.getElementAdded();
+            if (addedProvider != null) {
+                addProviderToMenu(addedProvider);
+            }
+            ITimeGraphDrawnEventProvider removedProvider = change.getElementRemoved();
+            if (removedProvider != null) {
+                removeProviderFromMenu(removedProvider);
+            }
+        });
+    }
+
+    private void addProviderToMenu(ITimeGraphDrawnEventProvider provider) {
+        CheckMenuItem item = new EventProviderMenuItem(provider);
+        int index = getItems().size() - 3;
+        getItems().add(index, item);
+    }
+
+    private void removeProviderFromMenu(ITimeGraphDrawnEventProvider provider) {
+        MenuItem itemToRemove = Iterables
+                .tryFind(getItems(), item -> {
+                    return (item instanceof EventProviderMenuItem
+                            && ((EventProviderMenuItem) item).getProvider().equals(provider));
+                })
+                .orNull();
+
+        if (itemToRemove != null) {
+            getItems().remove(itemToRemove);
+        }
+    }
+
+    /**
+     * Menu item that represents a particular
+     * {@link ITimeGraphDrawnEventProvider}.
+     */
+    private static class EventProviderMenuItem extends CheckMenuItem {
+
+        private final ITimeGraphDrawnEventProvider fProvider;
+
+        public EventProviderMenuItem(ITimeGraphDrawnEventProvider provider) {
+            fProvider = provider;
+
+            TimeGraphDrawnEventSeries series = provider.getEventSeries();
+
+            setMnemonicParsing(false);
+            setText(series.getSeriesName());
+            selectedProperty().bindBidirectional(provider.enabledProperty());
+
+            Shape graphic = TimeGraphDrawnEventLayer.getShapeFromSymbol(series.getSymbolStyle().get());
+            Color color = JfxColorFactory.getColorFromDef(series.getColor().get());
+            graphic.setFill(color);
+            setGraphic(graphic);
+        }
+
+        public ITimeGraphDrawnEventProvider getProvider() {
+            return fProvider;
+        }
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/Messages.java
new file mode 100644 (file)
index 0000000..74d0b99
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String eventSeriesMenuButtonName;
+
+    public static String newEventSeriesMenuItem;
+    public static String clearEventSeriesMenuItem;
+
+    public static String createEventSeriesDialogTitle;
+    public static String createEventSeriesDialogSectionFilterDef;
+    public static String createEventSeriesDialogFieldEventName;
+    public static String createEventSeriesDialogSectionSymbolDef;
+    public static String createEventSeriesDialogFieldColor;
+    public static String createEventSeriesDialogFieldShape;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/PredicateDrawnEventProvider.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/PredicateDrawnEventProvider.java
new file mode 100644 (file)
index 0000000..96f5476
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.FutureTask;
+import java.util.function.Predicate;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.request.ITmfEventRequest.ExecutionType;
+import org.eclipse.tracecompass.tmf.core.request.TmfEventRequest;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.core.context.ViewGroupContext;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.ITimeGraphModelProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.drawnevents.TimeGraphDrawnEventProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.TimeGraphEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEvent;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventRender;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.drawnevents.TimeGraphDrawnEventSeries;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeRender;
+
+import com.google.common.collect.ImmutableList;
+
+class PredicateDrawnEventProvider extends TimeGraphDrawnEventProvider {
+
+    /** Maximum number of matching events */
+    private static final int MAX = 2000;
+
+    private final ITimeGraphModelProvider fModelProvider;
+    private final Predicate<ITmfEvent> fPredicate;
+
+    public PredicateDrawnEventProvider(TimeGraphDrawnEventSeries drawnEventSeries,
+            ITimeGraphModelProvider modelProvider,
+            Predicate<ITmfEvent> predicate) {
+        super(drawnEventSeries);
+        fModelProvider = modelProvider;
+        fPredicate = predicate;
+
+        /* Just use whatever trace is currently active */
+        traceProperty().bind(ViewGroupContext.getCurrent().currentTraceProperty());
+    }
+
+    @Override
+    public TimeGraphDrawnEventRender getEventRender(TimeGraphTreeRender treeRender,
+            TimeRange timeRange, @Nullable FutureTask<?> task) {
+
+        /* Very TMF-specific */
+        ITmfTrace trace = traceProperty().get();
+        if (trace == null) {
+            return new TimeGraphDrawnEventRender(timeRange, Collections.EMPTY_LIST);
+        }
+
+        long startIndex = 0;
+
+        List<ITmfEvent> traceEvents = new LinkedList<>();
+        TmfEventRequest req = new TmfEventRequest(ITmfEvent.class,
+                timeRange.toTmfTimeRange(),
+                startIndex,
+                ITmfEventRequest.ALL_DATA,
+                ExecutionType.BACKGROUND) {
+
+            private int matches = 0;
+
+            @Override
+            public void handleData(ITmfEvent event) {
+                super.handleData(event);
+                if (task != null && task.isCancelled()) {
+                    cancel();
+                }
+
+                if (fPredicate.test(event)) {
+                    matches++;
+                    traceEvents.add(event);
+                    if (matches > MAX) {
+                        done();
+                    }
+                }
+            }
+        };
+        trace.sendRequest(req);
+        try {
+            req.waitForCompletion();
+        } catch (InterruptedException e) {
+        }
+
+        if (req.isCancelled()) {
+            return new TimeGraphDrawnEventRender(timeRange, Collections.EMPTY_LIST);
+        }
+
+        List<TimeGraphDrawnEvent> drawnEvents = traceEvents.stream()
+                /* trace event -> TimeGraphEvent */
+                .map(traceEvent -> {
+                    long timestamp = traceEvent.getTimestamp().toNanos();
+                    /*
+                     * Find the matching tree element for this trace event, if
+                     * there is one.
+                     */
+                    Optional<TimeGraphTreeElement> treeElem = treeRender.getAllTreeElements().stream()
+                            .filter(elem -> {
+                                Predicate<ITmfEvent> predicate = elem.getEventMatching();
+                                if (predicate == null) {
+                                    return false;
+                                }
+                                return predicate.test(traceEvent);
+                            })
+                            .findFirst();
+
+                    if (!treeElem.isPresent()) {
+                        return null;
+                    }
+                    return new TimeGraphEvent(timestamp, treeElem.get());
+                })
+                .filter(Objects::nonNull)
+                /* TimeGraphEvent -> TimeGraphDrawnEvent */
+                .map(tgEvent -> new TimeGraphDrawnEvent(tgEvent, getEventSeries(), null))
+                .collect(ImmutableList.toImmutableList());
+
+        return new TimeGraphDrawnEventRender(timeRange, drawnEvents);
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/messages.properties
new file mode 100644 (file)
index 0000000..421f564
--- /dev/null
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+eventSeriesMenuButtonName = Event Series
+
+newEventSeriesMenuItem = New Event Series...
+clearEventSeriesMenuItem = Clear All
+
+createEventSeriesDialogTitle = Create New Event Series
+createEventSeriesDialogSectionFilterDef = Filter Definition
+createEventSeriesDialogFieldEventName = Event Name
+createEventSeriesDialogSectionSymbolDef = Symbol
+createEventSeriesDialogFieldColor = Color
+createEventSeriesDialogFieldShape = Shape
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/drawnevents/package-info.java
new file mode 100644 (file)
index 0000000..a063272
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.drawnevents;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/messages.properties
new file mode 100644 (file)
index 0000000..096d8de
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+arrowSeriesMenuButtonName = Arrow Series
+
+sfFilterModeMenuButtonName = Filter Modes
+sfSortingModeMenuButtonName = Sorting Modes
+
+sfZoomInActionDescription = Zoom In
+sfZoomOutActionDescription = Zoom Out
+sfZoomToFullRangeActionDescription = Fit to Full Trace Range
+sfZoomToSelectionActionDescription = Fit to Selection
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/Messages.java
new file mode 100644 (file)
index 0000000..64e6671
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.modelconfig;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String modelConfigButtonName;
+    public static String modelConfigDialogTitle;
+    public static String modelConfigDialogHeader;
+
+    public static String modelConfigDialogRowHeaderState;
+    public static String modelConfigDialogRowHeaderColor;
+    public static String modelConfigDialogRowHeaderLineThickness;
+
+    public static String modelConfigDialogResetDefaultsButton;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigButton.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigButton.java
new file mode 100644 (file)
index 0000000..2521a17
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.modelconfig;
+
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+
+/**
+ * Button to open the legend mapping states to colors.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class ModelConfigButton extends Button {
+
+    private static final String LEGEND_ICON_PATH = "/icons/toolbar/legend.gif"; //$NON-NLS-1$
+
+    /**
+     * Constructor
+     *
+     * @param widget
+     *            The time graph widget to which this toolbar button is
+     *            associated.
+     */
+    public ModelConfigButton(TimeGraphWidget widget) {
+        Image icon = JfxImageFactory.instance().getImageFromResource(LEGEND_ICON_PATH);
+        setGraphic(new ImageView(icon));
+        setTooltip(new Tooltip(Messages.modelConfigButtonName));
+
+        setOnAction(e -> {
+            Dialog<?> dialog = new ModelConfigDialog(widget);
+            dialog.show();
+            JfxUtils.centerDialogOnScreen(dialog, ModelConfigButton.this);
+        });
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigDialog.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/ModelConfigDialog.java
new file mode 100644 (file)
index 0000000..6268648
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.modelconfig;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.core.config.ConfigOption;
+import org.lttng.scope.tmf2.views.core.timegraph.model.provider.states.ITimeGraphModelStateProvider;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.ColorDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.LineThickness;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.StateDefinition;
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.states.MultiStateInterval;
+import org.lttng.scope.tmf2.views.ui.jfx.CountingGridPane;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxColorFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.StateRectangle;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.beans.property.ReadOnlyProperty;
+import javafx.event.ActionEvent;
+import javafx.scene.Node;
+import javafx.scene.control.ButtonBar.ButtonData;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.ColorPicker;
+import javafx.scene.control.Dialog;
+import javafx.scene.control.Label;
+import javafx.scene.control.MenuButton;
+import javafx.scene.control.RadioMenuItem;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.Text;
+
+class ModelConfigDialog extends Dialog<@Nullable Void> {
+
+    private static final double H_GAP = 10;
+
+    public ModelConfigDialog(TimeGraphWidget widget) {
+        setTitle(Messages.modelConfigDialogTitle);
+        setHeaderText(Messages.modelConfigDialogHeader);
+
+        ButtonType resetToDefaultButtonType = new ButtonType(Messages.modelConfigDialogResetDefaultsButton, ButtonData.LEFT);
+        getDialogPane().getButtonTypes().addAll(resetToDefaultButtonType, ButtonType.CLOSE);
+
+        // TODO Allow configuring arrow, etc. providers too (different tabs?)
+        ITimeGraphModelStateProvider stateProvider = widget.getControl().getModelRenderProvider().getStateProvider();
+        List<ColorDefControl> stateControls = stateProvider.getStateDefinitions().stream()
+                .map(stateDef -> new ColorDefControl(widget, stateDef))
+                .collect(Collectors.toList());
+
+        /* Setup the GridPane which will be the contents of the dialog */
+        CountingGridPane grid = new CountingGridPane();
+        grid.setHgap(H_GAP);
+        /* Header row */
+        grid.appendRow(new Text(Messages.modelConfigDialogRowHeaderState),
+                new Text(Messages.modelConfigDialogRowHeaderColor),
+                new Text(Messages.modelConfigDialogRowHeaderLineThickness));
+
+        stateControls.forEach(setter -> grid.appendRow(setter.getNodes()));
+
+        /* Add an "empty row", then the control for multi-state intervals */
+        LineThicknessMenuButton multiStateThicknessButton = new LineThicknessMenuButton(widget,
+                MultiStateInterval.MULTI_STATE_THICKNESS_OPTION,
+                widget.getDebugOptions().multiStatePaint);
+        grid.appendRow(new Text("")); //$NON-NLS-1$
+        grid.appendRow(new Label("Multi-states"), new Text(), multiStateThicknessButton);
+
+        getDialogPane().setContent(grid);
+
+        /*
+         * We do not set the dialog's 'resultConverter', there is nothing
+         * special to do on close (all changes are immediately valid).
+         */
+
+        /* Define how to "Reset Defaults" button works */
+        getDialogPane().lookupButton(resetToDefaultButtonType).addEventFilter(ActionEvent.ACTION, e -> {
+            /*
+             * This button should not close the dialog. Consuming the event here
+             * will prevent the dialog from closing.
+             */
+            e.consume();
+
+            stateControls.forEach(sc -> {
+                sc.getOptions().forEach(ConfigOption::resetToDefault);
+                sc.load();
+            });
+            multiStateThicknessButton.getOption().resetToDefault();
+            multiStateThicknessButton.load();
+
+            repaintAllRectangles(widget);
+        });
+
+    }
+
+    // TODO Repaint the rectangles of the whole Timeline view, not just 1 widget
+    private static void repaintAllRectangles(TimeGraphWidget widget) {
+        widget.getRenderedStateRectangles().forEach(StateRectangle::updatePaint);
+    }
+
+    private static class ColorDefControl {
+
+        private final TimeGraphWidget fWidget;
+        private final StateDefinition fStateDef;
+
+        private final Label fLabel;
+        private final ColorPicker fColorPicker;
+        private final LineThicknessMenuButton fLineThicknessButton;
+
+        /**
+         * Constructor
+         *
+         * TODO Use a "PaintPicker" dialog instead, so a full Paint object can
+         * be configured from the UI. There is apparently one in SceneBuilder.
+         */
+        public ColorDefControl(TimeGraphWidget widget, StateDefinition stateDef) {
+            fWidget = widget;
+            fStateDef = stateDef;
+
+            fLabel = new Label(stateDef.getName());
+            fColorPicker = new ColorPicker();
+            fColorPicker.getStyleClass().add(ColorPicker.STYLE_CLASS_BUTTON);
+            fLineThicknessButton = new LineThicknessMenuButton(widget, stateDef.getLineThickness(), fColorPicker.valueProperty());
+            load();
+
+            /*
+             * Whenever a new color is selected in the UI, update the
+             * corresponding model color.
+             */
+            fColorPicker.setOnAction(e -> {
+                Color color = fColorPicker.getValue();
+                if (color == null) {
+                    return;
+                }
+                ColorDefinition colorDef = JfxColorFactory.colorToColorDef(color);
+                fStateDef.getColor().set(colorDef);
+
+                repaintAllRectangles(widget);
+            });
+
+        }
+
+        public Node[] getNodes() {
+            return new Node[] { fLabel, fColorPicker, fLineThicknessButton };
+        }
+
+        public Iterable<ConfigOption<?>> getOptions() {
+            return Arrays.asList(fStateDef.getColor(), fStateDef.getLineThickness());
+        }
+
+        public void load() {
+            Color color = JfxColorFactory.getColorFromDef(fStateDef.getColor().get());
+            fColorPicker.setValue(color);
+
+            fLineThicknessButton.load();
+        }
+    }
+
+    private static class LineThicknessMenuButton extends MenuButton {
+
+        private static final double RECTANGLE_WIDTH = 30;
+
+        private final ConfigOption<LineThickness> fOption;
+        private final ReadOnlyProperty<? extends Paint> fColorSource;
+
+        public LineThicknessMenuButton(TimeGraphWidget widget,
+                ConfigOption<LineThickness> option,
+                ReadOnlyProperty<? extends Paint> colorSource) {
+            fOption = option;
+            fColorSource = colorSource;
+
+            ToggleGroup tg = new ToggleGroup();
+            List<LineThicknessMenuButtonItem> items = Arrays.stream(LineThickness.values())
+                    .map(lt -> {
+                        LineThicknessMenuButtonItem rmi = new LineThicknessMenuButtonItem(lt);
+                        rmi.setGraphic(getRectangleForThickness(lt));
+                        rmi.setToggleGroup(tg);
+
+                        LineThickness currentThickness = option.get();
+                        rmi.setSelected(lt == currentThickness);
+
+                        rmi.setOnAction(e -> {
+                            option.set(lt);
+                            LineThicknessMenuButton.this.setGraphic(getRectangleForThickness(lt));
+                            repaintAllRectangles(widget);
+                        });
+                        return rmi;
+                    })
+                    .collect(Collectors.toList());
+
+            /* Initial value shown in the button */
+            setGraphic(getRectangleForThickness(option.get()));
+            getItems().addAll(items);
+        }
+
+        private Rectangle getRectangleForThickness(LineThickness lt) {
+            Rectangle rectangle = new Rectangle(RECTANGLE_WIDTH, StateRectangle.getHeightFromThickness(lt));
+            rectangle.fillProperty().bind(fColorSource);
+            return rectangle;
+        }
+
+        public ConfigOption<LineThickness> getOption() {
+            return fOption;
+        }
+
+        public void load() {
+            LineThickness lt = fOption.get();
+            setGraphic(getRectangleForThickness(lt));
+            getItems().stream()
+                    .map(item -> (LineThicknessMenuButtonItem) item)
+                    .filter(item -> item.getLineThickness() == lt)
+                    .findFirst().get().setSelected(true);
+        }
+    }
+
+    private static class LineThicknessMenuButtonItem extends RadioMenuItem {
+
+        private final LineThickness fLineThickness;
+
+        public LineThicknessMenuButtonItem(LineThickness lt) {
+            fLineThickness = lt;
+        }
+
+        public LineThickness getLineThickness() {
+            return fLineThickness;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/messages.properties
new file mode 100644 (file)
index 0000000..2c3898f
--- /dev/null
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+modelConfigButtonName = Model Configuration
+modelConfigDialogTitle = Model Configuration
+modelConfigDialogHeader = State Model Configuration
+
+modelConfigDialogRowHeaderState = State
+modelConfigDialogRowHeaderColor = Color
+modelConfigDialogRowHeaderLineThickness = Line Thickness
+
+modelConfigDialogResetDefaultsButton = Reset Defaults
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/modelconfig/package-info.java
new file mode 100644 (file)
index 0000000..dd5ffbf
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.modelconfig;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/Messages.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/Messages.java
new file mode 100644 (file)
index 0000000..8859164
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Message bundle for the package
+ *
+ * @noreference Messages class
+ */
+@NonNullByDefault({})
+@SuppressWarnings("javadoc")
+public class Messages extends NLS {
+
+    private static final String BUNDLE_NAME = Messages.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+    public static String sfFollowStateChangesNavModeName;
+    public static String sfFollowEventsNavModeName;
+    public static String sfFollowArrowsNavModeName;
+    public static String sfFollowBookmarksNavModeName;
+
+    public static String sfNextEventJobName;
+    public static String sfPreviousEventJobName;
+
+    static {
+        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+    }
+
+    private Messages() {
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavUtils.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavUtils.java
new file mode 100644 (file)
index 0000000..a925214
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.lttng.scope.tmf2.views.core.TimeRange;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+/**
+ * Common utilities for navigation actions.
+ *
+ * @author Alexandre Montplaisir
+ */
+final class NavUtils {
+
+    private NavUtils() {}
+
+    /**
+     * Move the selection to the target timestamp. Also update the visible range
+     * to be centered on that timestamp, but only if it is outside of the
+     * current visible range.
+     *
+     * This should only be called when reaching the new timestamp is caused by a
+     * user action (ie, not simply because another view sent a signal).
+     *
+     * @param viewer
+     *            The viewer on which to work
+     * @param timestamp
+     *            The timestamp to select, and potentially move to
+     */
+    public static void selectNewTimestamp(TimeGraphWidget viewer, long timestamp) {
+        /* Update the selection to the new timestamp. */
+        viewer.getControl().updateTimeRangeSelection(TimeRange.of(timestamp, timestamp));
+
+        TimeRange fullTimeGraphRange = viewer.getControl().getViewContext().getCurrentTraceFullRange();
+        TmfTimeRange windowRange = TmfTraceManager.getInstance().getCurrentTraceContext().getWindowRange();
+        long windowStart = windowRange.getStartTime().toNanos();
+        long windowEnd = windowRange.getEndTime().toNanos();
+        if (windowStart <= timestamp && timestamp <= windowEnd) {
+            /* Timestamp is still in the visible range, don't touch anything. */
+            return;
+        }
+        /* Update the visible range to the requested timestamp. */
+        /* The "span" of the window (aka zoom level) will remain constant. */
+        long windowSpan = windowEnd - windowStart;
+        if (windowSpan > fullTimeGraphRange.getDuration()) {
+            /* Should never happen, but just to be mathematically safe. */
+            windowSpan = fullTimeGraphRange.getDuration();
+        }
+
+        long newStart = timestamp - (windowSpan / 2);
+        long newEnd = newStart + windowSpan;
+
+        /* Clamp the range to the borders of the pane/trace. */
+        if (newStart < fullTimeGraphRange.getStart()) {
+            newStart = fullTimeGraphRange.getStart();
+            newEnd = newStart + windowSpan;
+        } else if (newEnd > fullTimeGraphRange.getEnd()) {
+            newEnd = fullTimeGraphRange.getEnd();
+            newStart = newEnd - windowSpan;
+        }
+
+        viewer.getControl().updateVisibleTimeRange(TimeRange.of(newStart, newEnd), true);
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationButtons.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationButtons.java
new file mode 100644 (file)
index 0000000..b1d9f5c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import com.google.common.collect.ImmutableList;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.MenuButton;
+import javafx.scene.control.RadioMenuItem;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+
+/**
+ * Class encapsulating the fowards/backwards navigation buttons, with support
+ * for different "modes" of navigation.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NavigationButtons {
+
+    // ------------------------------------------------------------------------
+    // Inner classes
+    // ------------------------------------------------------------------------
+
+    private static class BackButton extends Button {
+
+        private final TimeGraphWidget fViewer;
+
+        public BackButton(TimeGraphWidget viewer, NavigationMode initialNavMode) {
+            fViewer = viewer;
+            setNavMode(initialNavMode);
+        }
+
+        public void setNavMode(NavigationMode navMode) {
+            setGraphic(new ImageView(navMode.getBackIcon()));
+            setOnAction(e -> {
+                navMode.navigateBackwards(fViewer);
+            });
+        }
+    }
+
+    private static class ForwardButton extends Button {
+
+        private final TimeGraphWidget fViewer;
+
+        public ForwardButton(TimeGraphWidget viewer, NavigationMode initialNavMode) {
+            fViewer = viewer;
+            setNavMode(initialNavMode);
+        }
+
+        public void setNavMode(NavigationMode navMode) {
+            setGraphic(new ImageView(navMode.getForwardIcon()));
+            setOnAction(e -> {
+                navMode.navigateForwards(fViewer);
+            });
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Pre-defined navigation modes
+    // ------------------------------------------------------------------------
+
+    private static final List<NavigationMode> NAVIGATION_MODES = ImmutableList.of(
+            new NavigationModeFollowStateChanges(),
+            new NavigationModeFollowEvents(),
+            new NavigationModeFollowArrows(),
+            new NavigationModeFollowBookmarks());
+
+    // ------------------------------------------------------------------------
+    // Class components
+    // ------------------------------------------------------------------------
+
+    private final Button fBackButton;
+    private final Button fForwardButton;
+    private final MenuButton fMenuButton;
+
+    /**
+     * Constructor. This will prepare the buttons, which can then be retrieved
+     * with {@link #getBackButton()}, {@link #getForwardButton()} and
+     * {@link #getMenuButton()}.
+     *
+     * @param viewer
+     *            The viewer to which the buttons will belong
+     */
+    public NavigationButtons(TimeGraphWidget viewer) {
+        List<NavigationMode> navModes = NAVIGATION_MODES;
+        NavigationMode initialMode = navModes.get(0);
+
+        BackButton backButton = new BackButton(viewer, initialMode);
+        ForwardButton forwardButton = new ForwardButton(viewer, initialMode);
+        MenuButton menuButton = new MenuButton();
+
+        ToggleGroup tg = new ToggleGroup();
+        List<RadioMenuItem> items = NAVIGATION_MODES.stream()
+                .map(nm -> {
+                    RadioMenuItem item = new RadioMenuItem();
+                    item.setText(nm.getModeName());
+                    item.setGraphic(new HBox(new ImageView(nm.getBackIcon()), new ImageView(nm.getForwardIcon())));
+                    item.setToggleGroup(tg);
+                    item.setOnAction(e -> {
+                        backButton.setNavMode(nm);
+                        forwardButton.setNavMode(nm);
+                    });
+                    item.setDisable(!nm.isEnabled());
+                    return item;
+                })
+                .collect(Collectors.toList());
+
+        items.get(0).setSelected(true);
+        menuButton.getItems().addAll(items);
+
+        fBackButton = backButton;
+        fForwardButton = forwardButton;
+        fMenuButton = menuButton;
+    }
+
+    /**
+     * Get the "back" button.
+     *
+     * @return The back button
+     */
+    public Button getBackButton() {
+        return fBackButton;
+    }
+
+    /**
+     * Get the "forward" button.
+     *
+     * @return The forward button
+     */
+    public Button getForwardButton() {
+        return fForwardButton;
+    }
+
+    /**
+     * Get the drop-down menu button, which allows switching between the
+     * available navigation modes.
+     *
+     * @return The drop-down menu button
+     */
+    public MenuButton getMenuButton() {
+        return fMenuButton;
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationMode.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationMode.java
new file mode 100644 (file)
index 0000000..a7c80a8
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.lttng.scope.tmf2.views.ui.jfx.JfxImageFactory;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+import javafx.scene.image.Image;
+
+/**
+ * Abstract class for defining navigation (back and forward buttons) modes in
+ * the toolbar.
+ *
+ * @author Alexandre Montplaisir
+ */
+public abstract class NavigationMode {
+
+    private final String fModeName;
+    private final @Nullable Image fBackIcon;
+    private final @Nullable Image fForwardIcon;
+
+    /**
+     * Constructor
+     *
+     * @param modeName
+     *            Name of the mode, will be shown in the UI
+     * @param backIconPath
+     *            The icon to use for the "back" button
+     * @param forwardIconPath
+     *            The icon to use for the "forward" button
+     */
+    public NavigationMode(String modeName, String backIconPath, String forwardIconPath) {
+        fModeName = modeName;
+
+        JfxImageFactory factory = JfxImageFactory.instance();
+        fBackIcon = factory.getImageFromResource(backIconPath);
+        fForwardIcon = factory.getImageFromResource(forwardIconPath);
+    }
+
+    /**
+     * Get the name of this navigation mode.
+     *
+     * @return The mode name
+     */
+    public String getModeName() {
+        return fModeName;
+    }
+
+    /**
+     * Get the icon to use for the "back" button.
+     *
+     * @return The back button icon
+     */
+    public @Nullable Image getBackIcon() {
+        return fBackIcon;
+    }
+
+    /**
+     * Get the icon to use for the "forward" button.
+     *
+     * @return The forward button icon
+     */
+    public @Nullable Image getForwardIcon() {
+        return fForwardIcon;
+    }
+
+    /**
+     * If for some reason this particular mode should not be available to the
+     * user, this should be overridden to return 'false'.
+     *
+     * @return If this mode is enabled
+     */
+    public boolean isEnabled() {
+        return true;
+    }
+
+    /**
+     * What to do when the back button is invoked while in this mode.
+     *
+     * @param viewer
+     *            The viewer on which we are working
+     */
+    public abstract void navigateBackwards(TimeGraphWidget viewer);
+
+    /**
+     * What to do when the forward button is invoked while in this mode.
+     *
+     * @param viewer
+     *            The viewer on which we are working
+     */
+    public abstract void navigateForwards(TimeGraphWidget viewer);
+}
\ No newline at end of file
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowArrows.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowArrows.java
new file mode 100644 (file)
index 0000000..6cb64fe
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import static java.util.Objects.requireNonNull;
+
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+/**
+ * Navigation mode using drawn arrows.
+ *
+ * If we are currently positioned at an arrow endpoint, it follow the arrow to
+ * its other endpoint, scrolling vertically if needed.
+ *
+ * If we are not at an arrow endpoint, following the current entry backwards or
+ * forwards until we find one. If we don't find any, do nothing.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NavigationModeFollowArrows extends NavigationMode {
+
+    private static final String BACK_ICON_PATH = "/icons/toolbar/nav_arrow_back.gif"; //$NON-NLS-1$
+    private static final String FWD_ICON_PATH = "/icons/toolbar/nav_arrow_fwd.gif"; //$NON-NLS-1$
+
+    /**
+     * Constructor
+     */
+    public NavigationModeFollowArrows() {
+        super(requireNonNull(Messages.sfFollowArrowsNavModeName),
+                BACK_ICON_PATH,
+                FWD_ICON_PATH);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+
+    @Override
+    public void navigateBackwards(TimeGraphWidget viewer) {
+        // TODO NYI
+        System.out.println("Follow arrows backwards");
+    }
+
+    @Override
+    public void navigateForwards(TimeGraphWidget viewer) {
+        // TODO NYI
+        System.out.println("Follow arrows forwards");
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowBookmarks.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowBookmarks.java
new file mode 100644 (file)
index 0000000..603e7f0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import static java.util.Objects.requireNonNull;
+
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+/**
+ * Navigation mode using the current entry's events. It looks through all events
+ * in the trace belonging to the tree entry of the current selected state, and
+ * navigates through them. This allows stopping at events that may not cause a
+ * state change shown in the view.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NavigationModeFollowBookmarks extends NavigationMode {
+
+    private static final String BACK_ICON_PATH = "/icons/toolbar/nav_bookmark_back.gif"; //$NON-NLS-1$
+    private static final String FWD_ICON_PATH = "/icons/toolbar/nav_bookmark_fwd.gif"; //$NON-NLS-1$
+
+    /**
+     * Constructor
+     */
+    public NavigationModeFollowBookmarks() {
+        super(requireNonNull(Messages.sfFollowBookmarksNavModeName),
+                BACK_ICON_PATH,
+                FWD_ICON_PATH);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+
+    @Override
+    public void navigateBackwards(TimeGraphWidget viewer) {
+        // TODO NYI
+        System.out.println("Follow bookmarks backwards");
+    }
+
+    @Override
+    public void navigateForwards(TimeGraphWidget viewer) {
+        // TODO NYI
+        System.out.println("Follow bookmarks forwards");
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowEvents.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowEvents.java
new file mode 100644 (file)
index 0000000..c217dcf
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
+import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfContext;
+import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
+import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.StateRectangle;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+/**
+ * Navigation mode using the current entry's events. It looks through all events
+ * in the trace belonging to the tree entry of the current selected state, and
+ * navigates through them. This allows stopping at events that may not cause a
+ * state change shown in the view.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NavigationModeFollowEvents extends NavigationMode {
+
+    private static final String BACK_ICON_PATH = "/icons/toolbar/nav_event_back.gif"; //$NON-NLS-1$
+    private static final String FWD_ICON_PATH = "/icons/toolbar/nav_event_fwd.gif"; //$NON-NLS-1$
+
+    /**
+     * Mutex rule for search action jobs, making sure they execute sequentially
+     */
+    private final ISchedulingRule fSearchActionMutexRule = new ISchedulingRule() {
+        @Override
+        public boolean isConflicting(@Nullable ISchedulingRule rule) {
+            return (rule == this);
+        }
+
+        @Override
+        public boolean contains(@Nullable ISchedulingRule rule) {
+            return (rule == this);
+        }
+    };
+
+    /**
+     * Constructor
+     */
+    public NavigationModeFollowEvents() {
+        super(requireNonNull(Messages.sfFollowEventsNavModeName),
+                BACK_ICON_PATH,
+                FWD_ICON_PATH);
+    }
+
+    @Override
+    public void navigateBackwards(TimeGraphWidget viewer) {
+        navigate(viewer, false);
+    }
+
+    @Override
+    public void navigateForwards(TimeGraphWidget viewer) {
+        navigate(viewer, true);
+    }
+
+    private void navigate(TimeGraphWidget viewer, boolean forward) {
+        StateRectangle state = viewer.getSelectedState();
+        ITmfTrace trace = viewer.getControl().getViewContext().getCurrentTrace();
+        if (state == null || trace == null) {
+            return;
+        }
+        Predicate<ITmfEvent> predicate = state.getStateInterval().getTreeElement().getEventMatching();
+        if (predicate == null) {
+            /* The tree element does not support navigating by events. */
+            return;
+        }
+
+        String jobName = (forward ? Messages.sfNextEventJobName : Messages.sfPreviousEventJobName);
+
+        Job job = new Job(jobName) {
+            @Override
+            protected IStatus run(@Nullable IProgressMonitor monitor) {
+                long currentTime = TmfTraceManager.getInstance().getCurrentTraceContext().getSelectionRange().getStartTime().toNanos();
+                ITmfContext ctx = trace.seekEvent(TmfTimestamp.fromNanos(currentTime));
+                long rank = ctx.getRank();
+                ctx.dispose();
+
+                ITmfEvent event = (forward ?
+                        TmfTraceUtils.getNextEventMatching(trace, rank, predicate, monitor) :
+                        TmfTraceUtils.getPreviousEventMatching(trace, rank, predicate, monitor));
+                if (event != null) {
+                    NavUtils.selectNewTimestamp(viewer, event.getTimestamp().toNanos());
+                }
+                return Status.OK_STATUS;
+            }
+        };
+
+        /*
+         * Make subsequent jobs not run concurrently, but wait after one
+         * another.
+         */
+        job.setRule(fSearchActionMutexRule);
+        job.schedule();
+    }
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowStateChanges.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/NavigationModeFollowStateChanges.java
new file mode 100644 (file)
index 0000000..1439903
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.lttng.scope.tmf2.views.core.timegraph.model.render.tree.TimeGraphTreeElement;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.StateRectangle;
+import org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.TimeGraphWidget;
+
+/**
+ * Navigation mode using state changes. It goes to the end/start of the current
+ * selected state interval, or jumps to the next/previous one if we are already
+ * at a border.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class NavigationModeFollowStateChanges extends NavigationMode {
+
+    private static final String BACK_ICON_PATH = "/icons/toolbar/nav_statechange_back.gif"; //$NON-NLS-1$
+    private static final String FWD_ICON_PATH = "/icons/toolbar/nav_statechange_fwd.gif"; //$NON-NLS-1$
+
+    private static final Comparator<StateRectangle> EARLIEST_START_TIME_COMPARATOR =
+            Comparator.comparingLong(rect -> rect.getStateInterval().getStartEvent().getTimestamp());
+    private static final Comparator<StateRectangle> LATEST_END_TIME_COMPARATOR =
+            Comparator.<StateRectangle> comparingLong(rect -> rect.getStateInterval().getEndEvent().getTimestamp()).reversed();
+
+    /**
+     * Constructor
+     */
+    public NavigationModeFollowStateChanges() {
+        super(requireNonNull(Messages.sfFollowStateChangesNavModeName),
+                BACK_ICON_PATH,
+                FWD_ICON_PATH);
+    }
+
+    @Override
+    public void navigateBackwards(TimeGraphWidget viewer) {
+        navigate(viewer, false);
+    }
+
+
+    @Override
+    public void navigateForwards(TimeGraphWidget viewer) {
+        navigate(viewer, true);
+    }
+
+    private static void navigate(TimeGraphWidget viewer, boolean forward) {
+        StateRectangle state = viewer.getSelectedState();
+        if (state == null) {
+            return;
+        }
+        long stateStartTime = state.getStateInterval().getStartEvent().getTimestamp();
+        long stateEndTime = state.getStateInterval().getEndEvent().getTimestamp();
+
+        /* Aim to go to the start/end of the next/previous interval */
+        long targetTimestamp = (forward ? stateEndTime + 1 : stateStartTime - 1);
+        TimeGraphTreeElement treeElement = state.getStateInterval().getStartEvent().getTreeElement();
+        List<StateRectangle> potentialStates = getPotentialStates(viewer, targetTimestamp, treeElement, forward);
+
+        if (potentialStates.isEmpty()) {
+            /*
+             * We either reached the end of our model or an edge of the trace.
+             * Go to the end/start of the current state.
+             */
+            long bound = (forward ? stateEndTime : stateStartTime);
+            NavUtils.selectNewTimestamp(viewer, bound);
+            return;
+        }
+
+        /*
+         * Also compute the intervals that intersect the target timestamp.
+         * We will prefer those, but if there aren't any, we'll pick the
+         * best "potential" state.
+         */
+        List<StateRectangle> intersectingStates = getIntersectingStates(potentialStates, targetTimestamp, forward);
+
+        StateRectangle newState;
+        if (intersectingStates.isEmpty()) {
+            /*
+             * Let's look back into 'potentialStates' (non-intersecting)
+             * and pick the interval with the closest bound.
+             */
+            Optional<StateRectangle> optState = getBestPotentialState(potentialStates, forward);
+            if (!optState.isPresent()) {
+                /* We did our best and didn't find anything. */
+                return;
+            }
+            newState = optState.get();
+
+        } else if (intersectingStates.size() == 1) {
+            /* There is only one match, must be the right one. */
+            newState = intersectingStates.get(0);
+        } else {
+            /*
+             * There is more than one match (overlapping intervals, can
+             * happen sometimes with multi-states). Pick the one with the
+             * earliest start time (for backwards) or latest end time (for
+             * forwards), to ensure we "move out" on the next action.
+             */
+            newState = intersectingStates.stream()
+                    .sorted(forward ? LATEST_END_TIME_COMPARATOR : EARLIEST_START_TIME_COMPARATOR)
+                    .findFirst().get();
+        }
+
+        viewer.setSelectedState(newState, true);
+        newState.showTooltip(forward);
+        NavUtils.selectNewTimestamp(viewer, targetTimestamp);
+    }
+
+    /**
+     * Compute all the potentially valid states for a navigate
+     * backwards/forwards operation.
+     *
+     * This means all the states for the given time graph tree element which
+     * happen before, or after, the time given timestamp for backwards or
+     * forwards operation respectively.
+     */
+    private static List<StateRectangle> getPotentialStates(TimeGraphWidget viewer, long targetTimestamp,
+            TimeGraphTreeElement treeElement, boolean forward) {
+
+        Stream<StateRectangle> potentialStates = viewer.getRenderedStateRectangles().stream()
+                /* Keep only the intervals of the current tree element */
+                .filter(rect -> rect.getStateInterval().getStartEvent().getTreeElement().equals(treeElement));
+
+        if (forward) {
+            /*
+             * Keep only those intersecting, or happening after, the target
+             * timestamp.
+             */
+            potentialStates = potentialStates.filter(rect -> rect.getStateInterval().getEndEvent().getTimestamp() >= targetTimestamp);
+        } else {
+            /*
+             * Keep only those intersecting, or happening before, the target
+             * timestamp.
+             */
+            potentialStates = potentialStates.filter(rect -> rect.getStateInterval().getStartEvent().getTimestamp() <= targetTimestamp);
+        }
+
+        List<StateRectangle> allStates = potentialStates.collect(Collectors.toList());
+        return allStates;
+    }
+
+    /**
+     * From a list of potential states, generate the list of intersecting
+     * states. This means all state intervals that actually cross the target
+     * timestamp.
+     *
+     * Note that we've already verified one of the start/end time for back/forth
+     * navigation when generating the potential states, this method only needs
+     * to check the other bound.
+     */
+    private static List<StateRectangle> getIntersectingStates(List<StateRectangle> potentialStates,
+            long targetTimestamp, boolean forward) {
+
+        Stream<StateRectangle> intersectingStates = potentialStates.stream();
+        if (forward) {
+            intersectingStates = intersectingStates.filter(rect -> {
+                long start = rect.getStateInterval().getStartEvent().getTimestamp();
+                return (targetTimestamp >= start);
+            });
+        } else {
+            intersectingStates = intersectingStates.filter(rect -> {
+                long end = rect.getStateInterval().getEndEvent().getTimestamp();
+                return (targetTimestamp <= end);
+            });
+        }
+        return intersectingStates.collect(Collectors.toList());
+    }
+
+    private static Optional<StateRectangle> getBestPotentialState(List<StateRectangle> potentialStates, boolean forward) {
+        return potentialStates.stream()
+                .sorted(forward ?  EARLIEST_START_TIME_COMPARATOR : LATEST_END_TIME_COMPARATOR)
+                .findFirst();
+    }
+
+}
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/messages.properties b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/messages.properties
new file mode 100644 (file)
index 0000000..46321d0
--- /dev/null
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2017 EfficiOS Inc.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+###############################################################################
+
+sfFollowStateChangesNavModeName = Follow State Changes
+sfFollowEventsNavModeName = Follow Events
+sfFollowArrowsNavModeName = Follow Arrows
+sfFollowBookmarksNavModeName = Follow Bookmarks
+
+sfNextEventJobName = Searching for next matching event
+sfPreviousEventJobName = Searching for previous matching event
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/nav/package-info.java
new file mode 100644 (file)
index 0000000..fba1e87
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar.nav;
diff --git a/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/package-info.java b/tmf/org.lttng.scope.tmf2.views.ui/src/org/lttng/scope/tmf2/views/ui/timeline/widgets/timegraph/toolbar/package-info.java
new file mode 100644 (file)
index 0000000..6774a99
--- /dev/null
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) 2017 EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.lttng.scope.tmf2.views.ui.timeline.widgets.timegraph.toolbar;
This page took 0.302785 seconds and 5 git commands to generate.