Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWim Jongman2018-02-15 07:10:39 -0500
committerWim Jongman2018-02-28 05:43:23 -0500
commit855e377e4232cf456912f5d4d0088e86d157eabd (patch)
tree3792982215a3b81d3a30a9c9bc48bc1e2054d562
parenta5d490a262a6e2f6f3dfcb36372dba07b98566a7 (diff)
downloadeclipse.platform.ua-855e377e4232cf456912f5d4d0088e86d157eabd.tar.gz
eclipse.platform.ua-855e377e4232cf456912f5d4d0088e86d157eabd.tar.xz
eclipse.platform.ua-855e377e4232cf456912f5d4d0088e86d157eabd.zip
Bug 307889 - [Intro] having "tip of the day" functionalityI20180228-2000
This is the initial contribution of a Tip of the Day framework to Eclipse. It provides a dialog with tips that can be started from the Help menu. Optionally is is show at IDE or RCP application starts. Extenders can provide their own tips by implementing a TipProvider. TipProviders can be added to the tips extension point and/or can be initialized from a JSon file. TipProviders can get priority based on an enabled when expression similar to commands and handlers. For example, EMF tips may be shown when the modeling perspective is open. Tips can be created from straight up HTML, hosted on a separate web page (e.g. Eclipse wiki), created from a Json file or be a full blown SWT implementation. Tips can provide actions to enable the user to open a view, set some preferences or whatever. The framework is UI agnostic but comes with a separate SWT implementation. Examples on how to create a Tips and TipProviders are available. Documentation is on the wiki: https://wiki.eclipse.org/Tip_of_the_Day The Tips framework uses null annotations, by default all methods cannot return null, except if they annotated with @Nullable. Changes done by Lars in cooperation with Wim ==Wim 16/feb== * removed annotations in core for now * Fixed some javadoc == Lars 16/feb== Removed the TipThemeManager Remove TipProvider from the Tip API, the Tip does not need to know its provider Tip now require the TipID Tips hashcode and equals methods are based on summary, providerID and creation date Removed the special getImage64 methods and refactored getImage48 to getImage == Wim 27 feb == Removed json plugin Change-Id: Ib65e150bdeb5f3f38075d8c6432e5b476bfa064d Signed-off-by: Wim Jongman <wim.jongman@remainsoftware.com>
-rw-r--r--org.eclipse.tips.core/.classpath7
-rw-r--r--org.eclipse.tips.core/.gitignore2
-rw-r--r--org.eclipse.tips.core/.project28
-rw-r--r--org.eclipse.tips.core/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.tips.core/META-INF/MANIFEST.MF11
-rw-r--r--org.eclipse.tips.core/build.properties17
-rw-r--r--org.eclipse.tips.core/images/nomoretips.pngbin0 -> 123004 bytes
-rw-r--r--org.eclipse.tips.core/plugin.xml16
-rw-r--r--org.eclipse.tips.core/pom.xml25
-rw-r--r--org.eclipse.tips.core/schema/tips.exsd455
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/IHtmlTip.java37
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/ITipManager.java74
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/IUrlTip.java30
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/Tip.java109
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipAction.java81
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java298
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipManager.java260
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java332
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListener.java34
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListenerManager.java67
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/internal/FinalTip.java65
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/internal/ImageUtil.java50
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/internal/LogUtil.java69
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/internal/package-info.java14
-rw-r--r--org.eclipse.tips.core/src/org/eclipse/tips/core/package-info.java20
-rw-r--r--org.eclipse.tips.examples/.classpath7
-rw-r--r--org.eclipse.tips.examples/.gitignore2
-rw-r--r--org.eclipse.tips.examples/.project28
-rw-r--r--org.eclipse.tips.examples/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.tips.examples/META-INF/MANIFEST.MF16
-rw-r--r--org.eclipse.tips.examples/build.properties18
-rw-r--r--org.eclipse.tips.examples/icons/48/c++.pngbin0 -> 2255 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/ecf.pngbin0 -> 1251 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/eclipse.pngbin0 -> 2949 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/java.pngbin0 -> 3603 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/key.pngbin0 -> 1762 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/nebula.pngbin0 -> 5931 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/pin.pngbin0 -> 1575 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/plugin.pngbin0 -> 2247 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/pydev.pngbin0 -> 2574 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/swt.pngbin0 -> 2842 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/tips.pngbin0 -> 2217 bytes
-rw-r--r--org.eclipse.tips.examples/icons/48/twitter.pngbin0 -> 1961 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/c++.pngbin0 -> 2977 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/ecf.pngbin0 -> 1642 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/eclipse.pngbin0 -> 3949 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/java.pngbin0 -> 4801 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/key.pngbin0 -> 1467 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/nebula.pngbin0 -> 9126 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/pin.pngbin0 -> 1621 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/plugin.pngbin0 -> 2123 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/pydev.pngbin0 -> 2664 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/swt.pngbin0 -> 4841 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/tips.pngbin0 -> 2514 bytes
-rw-r--r--org.eclipse.tips.examples/icons/64/twitter.pngbin0 -> 2345 bytes
-rw-r--r--org.eclipse.tips.examples/icons/asterisk_yellow.pngbin0 -> 743 bytes
-rw-r--r--org.eclipse.tips.examples/icons/bug_link.pngbin0 -> 847 bytes
-rw-r--r--org.eclipse.tips.examples/icons/clock.pngbin0 -> 882 bytes
-rw-r--r--org.eclipse.tips.examples/icons/cut.pngbin0 -> 648 bytes
-rw-r--r--org.eclipse.tips.examples/icons/lightbulb.pngbin0 -> 782 bytes
-rw-r--r--org.eclipse.tips.examples/images/eclipsetips/tip1.gifbin0 -> 235329 bytes
-rw-r--r--org.eclipse.tips.examples/images/eclipsetips/tip2.pngbin0 -> 530211 bytes
-rw-r--r--org.eclipse.tips.examples/images/eclipsetips/tip3.pngbin0 -> 26780 bytes
-rw-r--r--org.eclipse.tips.examples/images/java/duke.pngbin0 -> 82983 bytes
-rw-r--r--org.eclipse.tips.examples/images/tips/navigate1.pngbin0 -> 44089 bytes
-rw-r--r--org.eclipse.tips.examples/images/tips/navigate2.pngbin0 -> 56333 bytes
-rw-r--r--org.eclipse.tips.examples/images/tips/starttip.gifbin0 -> 71600 bytes
-rw-r--r--org.eclipse.tips.examples/images/tips/tip2.gifbin0 -> 16442 bytes
-rw-r--r--org.eclipse.tips.examples/images/tips/tipsfw.pngbin0 -> 105366 bytes
-rw-r--r--org.eclipse.tips.examples/matrixrain/index.html26
-rw-r--r--org.eclipse.tips.examples/matrixrain/matrixrain.js96
-rw-r--r--org.eclipse.tips.examples/matrixrain/particle.js124
-rw-r--r--org.eclipse.tips.examples/plugin.xml96
-rw-r--r--org.eclipse.tips.examples/pom.xml25
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/DateUtil.java40
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/EclipseTipsProvider.java110
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip1.java63
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip2.java66
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip3.java58
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/TwitterTip.java48
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Java9TipProvider.java73
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Tip1.java109
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/json/JsonTipProviderPhoton.java27
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipImpl.java81
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipsProvider.java105
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider.java96
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider1.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider2.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider3.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider4.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider5.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider6.java76
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider7.java78
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/MediaWikiTip.java83
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/TwitterTip.java79
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/package-info.java15
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/GithubTip.java90
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/MatrixRainTip.java44
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate1Tip.java101
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate2Tip.java63
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/StartingTip.java62
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/TipsTipProvider.java75
-rw-r--r--org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/WelcomeTip.java48
-rw-r--r--org.eclipse.tips.feature/.gitignore1
-rw-r--r--org.eclipse.tips.feature/.project17
-rw-r--r--org.eclipse.tips.feature/build.properties14
-rw-r--r--org.eclipse.tips.feature/feature.properties156
-rw-r--r--org.eclipse.tips.feature/feature.xml53
-rw-r--r--org.eclipse.tips.feature/pom.xml26
-rw-r--r--org.eclipse.tips.ide/.classpath7
-rw-r--r--org.eclipse.tips.ide/.gitignore2
-rw-r--r--org.eclipse.tips.ide/.project28
-rw-r--r--org.eclipse.tips.ide/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.tips.ide/META-INF/MANIFEST.MF14
-rw-r--r--org.eclipse.tips.ide/build.properties16
-rw-r--r--org.eclipse.tips.ide/icons/lightbulb.pngbin0 -> 782 bytes
-rw-r--r--org.eclipse.tips.ide/plugin.xml189
-rw-r--r--org.eclipse.tips.ide/pom.xml25
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Constants.java33
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/IDETipManager.java239
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Preferences.java81
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/ProviderLoadJobChangeListener.java50
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Startup.java113
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipSourceProvider.java82
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipsHandler.java29
-rw-r--r--org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/package-info.java16
-rw-r--r--org.eclipse.tips.tests/.classpath7
-rw-r--r--org.eclipse.tips.tests/.gitignore2
-rw-r--r--org.eclipse.tips.tests/.project28
-rw-r--r--org.eclipse.tips.tests/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.tips.tests/META-INF/MANIFEST.MF17
-rw-r--r--org.eclipse.tips.tests/build.properties14
-rw-r--r--org.eclipse.tips.tests/pom.xml26
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/JsonTestProvider.java27
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTip.java47
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipManager.java73
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipProvider.java57
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageBas64Test.java108
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageURLTest.java104
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TipManagerTest.java179
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TipProviderTest.java191
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/core/TipTest.java153
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/Sleak.java306
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/SleakTipManager.java132
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/XML.java45
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/package-info.java18
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/tests/Good.java23
-rw-r--r--org.eclipse.tips.tests/src/org/eclipse/tips/util/ImageUtilTest.java142
-rw-r--r--org.eclipse.tips.ui/.classpath7
-rw-r--r--org.eclipse.tips.ui/.gitignore2
-rw-r--r--org.eclipse.tips.ui/.project28
-rw-r--r--org.eclipse.tips.ui/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--org.eclipse.tips.ui/META-INF/MANIFEST.MF13
-rw-r--r--org.eclipse.tips.ui/build.properties15
-rw-r--r--org.eclipse.tips.ui/icons/48/aleft.pngbin0 -> 915 bytes
-rw-r--r--org.eclipse.tips.ui/icons/48/aright.pngbin0 -> 945 bytes
-rw-r--r--org.eclipse.tips.ui/icons/64/aleft.pngbin0 -> 663 bytes
-rw-r--r--org.eclipse.tips.ui/icons/64/aright.pngbin0 -> 687 bytes
-rw-r--r--org.eclipse.tips.ui/icons/lightbulb.pngbin0 -> 782 bytes
-rw-r--r--org.eclipse.tips.ui/icons/popup_menu.gifbin0 -> 92 bytes
-rw-r--r--org.eclipse.tips.ui/pom.xml25
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/DefaultTipManager.java85
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/ISwtTip.java32
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/ProviderSelectionListener.java29
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/Slider.java406
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipComposite.java580
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipDialog.java59
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/package-info.java16
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/DateUtil.java40
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ImageUtil.java133
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ResourceManager.java435
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/SWTResourceManager.java488
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/package-info.java14
-rw-r--r--org.eclipse.tips.ui/src/org/eclipse/tips/ui/package-info.java15
-rw-r--r--pom.xml4
175 files changed, 9995 insertions, 0 deletions
diff --git a/org.eclipse.tips.core/.classpath b/org.eclipse.tips.core/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.eclipse.tips.core/.classpath
@@ -0,0 +1,7 @@
+<?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"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.tips.core/.gitignore b/org.eclipse.tips.core/.gitignore
new file mode 100644
index 000000000..09e3bc9b2
--- /dev/null
+++ b/org.eclipse.tips.core/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/org.eclipse.tips.core/.project b/org.eclipse.tips.core/.project
new file mode 100644
index 000000000..85a0fc8d3
--- /dev/null
+++ b/org.eclipse.tips.core/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.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>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.core/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tips.core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/org.eclipse.tips.core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.tips.core/META-INF/MANIFEST.MF b/org.eclipse.tips.core/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..201ad389f
--- /dev/null
+++ b/org.eclipse.tips.core/META-INF/MANIFEST.MF
@@ -0,0 +1,11 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Tip of the Day core plugin
+Bundle-SymbolicName: org.eclipse.tips.core;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-Vendor: Eclipse Foundation
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Export-Package: org.eclipse.tips.core,
+ org.eclipse.tips.core.internal;x-internal:=true
+Require-Bundle: org.eclipse.core.runtime;bundle-version="3.13.0"
+Automatic-Module-Name: org.eclipse.tips.core
diff --git a/org.eclipse.tips.core/build.properties b/org.eclipse.tips.core/build.properties
new file mode 100644
index 000000000..39b31f3b5
--- /dev/null
+++ b/org.eclipse.tips.core/build.properties
@@ -0,0 +1,17 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ images/
+src.includes = schema/
diff --git a/org.eclipse.tips.core/images/nomoretips.png b/org.eclipse.tips.core/images/nomoretips.png
new file mode 100644
index 000000000..d77582876
--- /dev/null
+++ b/org.eclipse.tips.core/images/nomoretips.png
Binary files differ
diff --git a/org.eclipse.tips.core/plugin.xml b/org.eclipse.tips.core/plugin.xml
new file mode 100644
index 000000000..b29e93cdb
--- /dev/null
+++ b/org.eclipse.tips.core/plugin.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<plugin>
+ <extension-point id="tips" name="Tips" schema="schema/tips.exsd"/>
+</plugin>
diff --git a/org.eclipse.tips.core/pom.xml b/org.eclipse.tips.core/pom.xml
new file mode 100644
index 000000000..173ccf367
--- /dev/null
+++ b/org.eclipse.tips.core/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.core</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/org.eclipse.tips.core/schema/tips.exsd b/org.eclipse.tips.core/schema/tips.exsd
new file mode 100644
index 000000000..93236edf6
--- /dev/null
+++ b/org.eclipse.tips.core/schema/tips.exsd
@@ -0,0 +1,455 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.tips.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.eclipse.tips.core" id="tips" name="Tips"/>
+ </appinfo>
+ <documentation>
+ &lt;p&gt;
+This extension point allows bundles to hook into the eclipse tips framework.
+&lt;/p&gt;
+@see Examples
+ </documentation>
+ </annotation>
+
+ <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <choice>
+ <element ref="provider" minOccurs="1" maxOccurs="unbounded"/>
+ <element ref="category" minOccurs="1" maxOccurs="unbounded"/>
+ </choice>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="provider">
+ <complexType>
+ <sequence>
+ <element ref="enablement" minOccurs="0" maxOccurs="1"/>
+ </sequence>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="description" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="org.eclipse.tips.core.TipProvider:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="category" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.tips.core.tips/category/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="category">
+ <annotation>
+ <documentation>
+ A category to group tips in the UI.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ a unique name that will be used to identify this category
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ a translatable name that will be used in the UI for this category
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="parentCategory" type="string">
+ <annotation>
+ <documentation>
+ an optional path composed of category IDs separated by &apos;/&apos;. This
+allows the creation of a hierarchy of categories.
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.tips.core.tips/category/@id"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ &lt;p&gt;
+This extension defines a parent category &quot;Java&quot;and a child category &quot;Java9&quot;. Then the provider
+is defined within category &quot;Java9&quot;.
+&lt;pre&gt;
+ &lt;extension
+ point=&quot;org.eclipse.tips.ide.tips&quot;&gt;
+ &lt;category
+ id=&quot;org.eclipse.tips.examples.java&quot;
+ name=&quot;Java&quot;&gt;
+ &lt;/category&gt;
+ &lt;category
+ id=&quot;org.eclipse.tips.examples.java9&quot;
+ name=&quot;Java9&quot;
+ parentCategory=&quot;org.eclipse.tips.examples.java9&quot;&gt;
+ &lt;/category&gt;
+ &lt;provider
+ category=&quot;org.eclipse.tips.examples.java9&quot;
+ class=&quot;org.eclipse.tips.examples.java.java9.Java9TipProvider&quot;
+ description=&quot;Java 9 Tips&quot;
+ id=&quot;org.eclipse.tips.examples.java.java9.Java9TipProvider&quot;&gt;
+ &lt;enablement&gt;
+ &lt;with
+ variable=&quot;activeWorkbenchWindow.activePerspective&quot;&gt;
+ &lt;equals
+ value=&quot;org.eclipse.jdt.ui.JavaPerspective&quot;&gt;
+ &lt;/equals&gt;
+ &lt;/with&gt;
+ &lt;/enablement&gt;
+ &lt;/provider&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+
+&lt;p&gt;
+&lt;b&gt;Sample implementation TipProvider&lt;/b&gt;
+&lt;/p&gt;
+
+&lt;p&gt;
+
+&lt;h1&gt;The Implementation of a TipProvider&lt;/h1&gt;
+This is an example of a TipProvider. The framework also contains an JSon TipProvider that fetches tips inside a JSon file from a remote URL.
+&lt;b&gt;
+The example below instantiates Tips from Java.
+&lt;pre&gt;
+
+/**
+ * Class to provide tips to the tip framework. It is the job of this provider to
+ * manage its tips. Examples of managing tips are:
+ *
+ * &lt;ul&gt;
+ * &lt;li&gt;Loading tips from the internet&lt;/li&gt;
+ * &lt;li&gt;Serve next, previous and current tip on request&lt;/li&gt;
+ * &lt;/ul&gt;
+ *
+ * After the TipProvider is instantiated by the {@link TipManager}, the
+ * TipManager will insert itself by calling {@link #setManager(TipManager)}.
+ * Then the TipManager will asynchronous call this providers&apos; {@link #load()}
+ * method. The job of the load() method is to do long work like fetching new
+ * tips from the internet and storing them locally. There is no defined method
+ * on how tips should be stored locally, implementers are free to do what is
+ * needed.
+ *
+ * The constructor must return fast, meaning that tips may not be fetched from
+ * the internet in the constructor. This should be done in the
+ * {@link #load(IProgressMonitor)} method.
+ *
+ * To indicate that this provider is ready to serve tips, it should call the
+ * {@link #setTips(List)} method which then sets its &lt;code&gt;ready&lt;/code&gt; flag.
+ *
+ */
+public class Java9TipProvider extends TipProvider {
+
+ @Override
+ /**
+ * The Id is used (for example) to manage the read state of this providers tips.
+ * It should be unique.
+ *
+ * @return the ID of this provider
+ */
+ public String getID() {
+ return &quot;org.eclipse.tips.examples.Java9TipProvider&quot;;
+ }
+
+ /**
+ * The 48x48 image may be used by the UI for low resolution displays.
+ *
+ * @return a 48x48 {@link TipImage}
+ */
+ @Override
+ public TipImage getImage48() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry(&quot;icons/48/java.png&quot;)).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(getClass(), e);
+ }
+ return null;
+
+ }
+
+ /**
+ * The 64x64 image may be used by the UI for higher resolution displays.
+ *
+ * @return a 64x64 {@link TipImage}
+ */
+ @Override
+ public TipImage getImage64() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry(&quot;icons/64/java.png&quot;)).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(getClass(), e);
+ }
+ return null;
+
+ }
+
+ /**
+ * Is called asynchronously during startup of the TipManager to gather new tips.
+ * The provider is not available to the UI unless it has called it&apos;s
+ * {@link #setTips(List)} method. It is therefore possible that the provider is
+ * not immediately visible in the tip UI but will be added later.
+ *
+ * One strategy is to do a long running fetch in this method and then store the
+ * tips locally. On the next run of the TipManager, the fetched tips can be
+ * served from the constructor (i.e. by calling {@link #setTips(List)}), making
+ * it immediately available.
+ *
+ * @param pMonitor
+ * The monitor to report back progress.
+ * @see TipProvider#setTips(List)
+ * @see TipProvider#isReady()
+ */
+ @Override
+ public synchronized void load(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask(&quot;Loading Tips&quot;, -1);
+ List&lt;Tip&gt; tips = new ArrayList&lt;&gt;();
+ tips.add(new Tip1(this));
+ tips.add(new Navigate2Tip(this));
+ setTips(tips);
+ subMonitor.done();
+ }
+
+ /**
+ * @return the short description of this provider.
+ */
+ @Override
+ public String getDescription() {
+ return &quot;Java and Java Dev Tools Tips&quot;;
+ }
+
+ /**
+ * Provides the opportunity to release all held resources.
+ */
+ @Override
+ public void dispose() {
+ }
+}
+
+&lt;/pre&gt;
+
+&lt;h1&gt;The Implementation of a Tip&lt;/h1&gt;
+This is an example implementation of a Tip
+&lt;pre&gt;
+/**
+ * This is an example Tip class.
+ *
+ */
+public class Tip1 extends Tip {
+
+ /**
+ * Tips should be created fast.
+ *
+ * @param pProvider
+ * the associated {@link TipProvider}
+ */
+ public Tip1(TipProvider pProvider) {
+ super(pProvider);
+ }
+
+ /**
+ * A getter for a {@link Runnable} action for this tip. Clients may override or
+ * call {@link #setAction(Runnable)}.
+ *
+ * @return the action or null if not set by {@link Tip#setAction(Runnable)}.
+ * @see Tip#setAction(Runnable)
+ */
+ @Override
+ public Runnable getAction() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ Display.getDefault().syncExec(new Runnable() {
+ @Override
+ public void run() {
+ MessageDialog.openConfirm(null, getSubject(), &quot;We can start an action from a Tip!&quot;);
+ }
+ });
+ }
+ };
+ }
+
+ /**
+ * Return the publish date of the tip. The {@link TipProvider} could decide to
+ * server newer tips first.
+ *
+ * @return the date this tip was published which may not be null.
+ */
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD(&quot;10/01/2018&quot;);
+ }
+
+ /**
+ * @return the subject which may not be null.
+ */
+ @Override
+ public String getSubject() {
+ return &quot;Java tip 1&quot;;
+ }
+
+ /**
+ * This method may both return the string representation of an URL or the
+ * descriptive HTML of the tip. If the html of the text is returned then an
+ * effort is made to also inline the image URL. If you supply an URL then
+ * {@link #getImage()} should return null.
+ *
+ * @return the HMTL or URL of the tip which is displayed in a browser widget.
+ * @see #getImage()
+ */
+ @Override
+ public String getHTML() {
+ return &quot;&lt;h2&gt;Javatip 1&lt;/h2&gt;You see this tip because the Tip UI was opened when the java perspective &quot;
+ + &quot;was active or because you selected the Java icon below.&quot; + &quot;&lt;br&gt;&lt;br&gt;&quot;
+ + &quot;More java tips will be displayed here in the near future. For now, select one of the other providers&quot;
+ + &quot; by clicking on the icons below.&quot;;
+ }
+
+ /**
+ * A getter for the {@link TipImage}. Subclasses may override, the default
+ * implementation returns null.
+ *
+ * @return a TipImage with information about the image or null if no image is
+ * provided.
+ */
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry(&quot;images/java/duke.png&quot;)).setAspectRatio(1);
+ } catch (IOException e) {
+ getProvider().getManager().log(getClass(), e);
+ }
+ return null;
+ }
+}
+&lt;/pre&gt;
+
+&lt;h1&gt;Example of a TipImage&lt;/h1&gt;
+TipImage objects are UI agnostic representations of an Image. You may pass a URL to a TipImage or a full base64 string (e.g. &quot;data:image/png;base64,iVBORw0KGgo.....CYII=&quot;).
+&lt;br&gt;
+One import aspect of the TipImage is it&apos;s aspect ratio. The Tip UI may use a browser to display it&apos;s UI. Therefore it can be scaled to the available space. Images look best when they use a 3:2 (1.5) ratio.
+&lt;br&gt;
+&lt;h4&gt;Set an image that is a square (aspect ratio = 1:1 (1))
+&lt;pre&gt;
+TipImage img = new TipImage(bundle.getEntry(&quot;images/java/duke.png&quot;)).setAspectRatio(1)
+&lt;/pre&gt;
+
+&lt;h4&gt;Set an image that is a square (aspect ratio = 1:1 (1))
+&lt;pre&gt;
+ /**
+ * Sets the aspect ratio of this image. If the image is 300 wide and 600 high
+ * then the aspect ratio is 300/600 = 0,5 (1:2). If your image is 1200 wide and
+ * 250 high then the aspect ratio (1200/250) = 4,8. With the supplied values the
+ * best dimensions for the image can be calculated give the available space in
+ * the UI.
+ * &lt;p&gt;
+ * In case you pass true for &lt;code&gt;pSetAsMax&lt;/code&gt; then the image can not be
+ * up-scaled beyond the specified size. So if your image is 200x200 and you want
+ * a maximum up-scale of 2 then pass 400x400 to this method to maintain the
+ * aspect ratio but allow the image to be resized to maximum it&apos;s double size.
+ * &lt;p&gt;
+ * The recommended aspect ratio is around 3:2 (1.5) to be comfortably displayed
+ * in the Tip UI.
+ **
+ TipImage img = new TipImage(&quot;data:image/png;base64,3ff4..eFW&quot;);
+ img.setAspectRatio(800, 400, true); // Image can not be scaled up higer than 800:400
+&lt;/pre&gt;
+
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+
+
+ <annotation>
+ <appinfo>
+ <meta.section type="copyright"/>
+ </appinfo>
+ <documentation>
+ Copyright (c) 2017 Remain Software
+
+ 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:
+ Wim Jongman &lt;wim.jongman@remainsoftware.com&gt; - Initial implementation
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/IHtmlTip.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/IHtmlTip.java
new file mode 100644
index 000000000..b6373111a
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/IHtmlTip.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+
+/**
+ * Decoration of {@link Tip} that enables HTML content.
+ *
+ */
+public interface IHtmlTip {
+
+ /**
+ * Returns the HTML of the tip to be rendered in the tip UI, together or without
+ * the {@link #getImage()}.
+ *
+ * @return the HMTL of the tip
+ * @see #getImage()
+ */
+ public String getHTML();
+
+ /**
+ * Returns the {@link TipImage}.
+ *
+ * @return a TipImage with information about the image or <code>null</code> if the this information cannot be created
+ * @see #getHTML()
+ */
+ public TipImage getImage();
+
+}
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/ITipManager.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/ITipManager.java
new file mode 100644
index 000000000..a722914b5
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/ITipManager.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * The model maintained by the TipManager.
+ *
+ */
+public interface ITipManager {
+
+ /**
+ * Indicates whether already read tips must be served or not.
+ *
+ * @return true or false
+ * @see TipManager#setServeReadTips(boolean)
+ */
+ public boolean mustServeReadTips();
+
+ /**
+ * Consults TipManager to determine the Tip's read status.
+ *
+ * @param tip
+ * the tip to query for its read status
+ * @return true if the tip is read, false otherwise.
+ */
+ public abstract boolean isRead(Tip tip);
+
+ /**
+ * Instructs the TipManager to mark this tip as read.
+ *
+ * @param tip
+ * the tip to set as read.
+ * @return this
+ */
+ public abstract ITipManager setAsRead(Tip tip);
+
+ /**
+ * Central place of logging for the Tip Framework.
+ *
+ * @param status
+ * the {@link IStatus} which may not be null
+ * @return this
+ */
+ public ITipManager log(IStatus status);
+
+ /**
+ * Binds the passed provider to this manager. After registration, ITipManager
+ * implementations should asynchronously call the
+ * {@link TipProvider#loadNewTips(org.eclipse.core.runtime.IProgressMonitor)}
+ * method.
+ *
+ * @param provider
+ * the {@link TipProvider} to register which may not be null.
+ * @return this
+ */
+ public ITipManager register(TipProvider provider);
+
+ /**
+ * Returns the disposed stated.
+ *
+ * @return true if this manager is disposed, false otherwise.
+ */
+ public boolean isDisposed();
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/IUrlTip.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/IUrlTip.java
new file mode 100644
index 000000000..01ec04a59
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/IUrlTip.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import java.net.URL;
+
+/**
+ * Decoration of {@link Tip} that enables URL content.
+ *
+ */
+public interface IUrlTip {
+
+ /**
+ * Return an URL with the primary goal to be rendered by the tip manager.
+ * Implementations of Tip may also use the URL to aid the rendering (e.g. by
+ * providing other data than HTML, e.g. a text file).
+ *
+ * @return the URL to the (remote) content
+ *
+ */
+ public URL getURL();
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/Tip.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/Tip.java
new file mode 100644
index 000000000..8d5cf112c
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/Tip.java
@@ -0,0 +1,109 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * This is the base Tip class of the UI agnostic Tip framework. You might want
+ * to check specializations of this class that may make your life easier.
+ *
+ */
+public abstract class Tip {
+
+ private String providerId;
+
+ private final List<TipAction> fActions = new ArrayList<>();
+
+ /**
+ * Constructor for a Tip. For the best user experience, Tips should be created
+ * really fast.
+ *
+ */
+ public Tip(String providerId) {
+ this.providerId = providerId;
+ }
+
+ /**
+ * For the sanity of the Framework, Tips should be created really fast.
+ *
+ * @param pProvider
+ * the associated {@link TipProvider}
+ * @param actions
+ * the list of actions which may not be null
+ */
+ public Tip(List<TipAction> actions) {
+ fActions.addAll(actions);
+ }
+
+ /**
+ * A getter for a list of {@link TipAction}s for this tip. Clients may override
+ * or provide the actions through the constructor.
+ *
+ * @return the list of actions, never null but could be empty.
+ */
+ public List<TipAction> getActions() {
+ return fActions;
+ }
+
+ /**
+ * Return the publish date of the tip. The UI could decide to server newer tips
+ * first.
+ *
+ * @return the date this tip was published which may not be null.
+ */
+ public abstract Date getCreationDate();
+
+
+ /**
+ * @return the subject which may not be null.
+ */
+ public abstract String getSubject();
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((getCreationDate() == null) ? 0 : getCreationDate().hashCode());
+ result = prime * result + ((providerId == null) ? 0 : providerId.hashCode());
+ result = prime * result + ((getSubject() == null) ? 0 : getSubject().hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Tip other = (Tip) obj;
+ if (getCreationDate() == null) {
+ if (other.getCreationDate() != null)
+ return false;
+ } else if (!getCreationDate().equals(other.getCreationDate()))
+ return false;
+ if (providerId == null) {
+ if (other.providerId != null)
+ return false;
+ } else if (!providerId.equals(other.providerId))
+ return false;
+ if (getSubject() == null) {
+ if (other.getSubject() != null)
+ return false;
+ } else if (!getSubject().equals(other.getSubject()))
+ return false;
+ return true;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipAction.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipAction.java
new file mode 100644
index 000000000..e73a2ac72
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipAction.java
@@ -0,0 +1,81 @@
+/****************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+/**
+ * Provides an action to be executed by a Tip.
+ *
+ */
+public class TipAction {
+
+ private final String fText;
+ private final TipImage fTipImage;
+ private final Runnable fRunner;
+ private final String fTooltip;
+
+ /**
+ * Creates a new TipAction. Tip actions can be executed in the tip UI when the
+ * associated Tip is displayed.
+ *
+ * @param text
+ * a very short description to be used on buttons and menus.
+ * @param tooltip
+ * a longer description to be shown as tool tip when possible.
+ * @param runner
+ * the actual code to run
+ * @param image
+ * the image to be shown when possible.
+ *
+ */
+ public TipAction(String text, String tooltip, Runnable runner, TipImage image) {
+ fText = text;
+ fTooltip = tooltip;
+ fRunner = runner;
+ fTipImage = image;
+ }
+
+ /**
+ * The short description of the action to be shown as button text or menu entry
+ * when possible.
+ *
+ * @return the text
+ */
+ public String getText() {
+ return fText;
+ }
+
+ /**
+ * A longer description to be shown as tool tip when possible.
+ *
+ * @return the tool tip.
+ */
+ public String getTooltip() {
+ return fTooltip;
+ }
+
+ /**
+ * The icon of the image wrapped in a TipImage.
+ *
+ * @return the icon
+ */
+ public TipImage getTipImage() {
+ return fTipImage;
+ }
+
+ /**
+ * The actual code to run when this action is executed.
+ *
+ * @return the runner.
+ */
+ public Runnable getRunner() {
+ return fRunner;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java
new file mode 100644
index 000000000..18a12e791
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipImage.java
@@ -0,0 +1,298 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Base64;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.tips.core.internal.ImageUtil;
+
+/**
+ * Provides more information about the image to be used in the tip. The image
+ * aspect ratio must be around 3:2 to be comfortably displayed in the Tip UI.
+ *
+ */
+public class TipImage {
+
+ private static final double THREE_TO_TWO = 1.5;
+
+ /**
+ * Value to indicate that the height or width are to be determined by the Tip
+ * framework.
+ */
+ public static final int UNDEFINED = -1;
+
+ private String fExtension = null;
+ private int fMaxWidth = UNDEFINED;
+ private int fMaxHeight = UNDEFINED;
+ final private URL fURL;
+ private double fAspectRatio = THREE_TO_TWO;
+
+ final private String fBase64Image;
+
+ private static final int _4KB = 4096;
+
+ /**
+ * Creates a new TipImage with the specified URL which gets read into a base 64
+ * string.
+ *
+ * @param url
+ * the image URL which may not be null
+ * @throws IOException
+ * in case the stream of the passed URL could not be opened or read.
+ *
+ */
+ public TipImage(URL url) throws IOException {
+ Assert.isNotNull(url);
+ fURL = url;
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ byte[] chunk = new byte[_4KB];
+ int bytesRead;
+ InputStream stream = url.openStream();
+ while ((bytesRead = stream.read(chunk)) > 0) {
+ outputStream.write(chunk, 0, bytesRead);
+ }
+ fBase64Image = "data:image/" //
+ + getExtension() //
+ + ";base64," //
+ + Base64.getEncoder().encodeToString(outputStream.toByteArray());
+ }
+
+ /**
+ * @return true if the URL constructor was used and not the base64 constructor.
+ */
+ public boolean isURLSet() {
+ return fURL != null;
+ }
+
+ /**
+ * Creates a new {@link TipImage} with the specified base64 string which must be
+ * a valid RFC-2397 string thats begins with
+ * <code>"data:image/&lt;subtype&gt;;base64"</code> where <code>subtype</code>
+ * must be a valid image type (pmg, bmp, gif, etc..).
+ *
+ * @param base64Image
+ * the non-null base64 encoded image according to RFC-2397.
+ *
+ * @throws RuntimeException
+ * if the string is not valid
+ * @see TipImage
+ * @see <a href="https://tools.ietf.org/search/rfc2397">RFC-2397
+ * (https://tools.ietf.org/search/rfc2397)</a>
+ *
+ */
+ public TipImage(String base64Image) {
+ Assert.isNotNull(base64Image);
+ fURL = null;
+ if (base64Image.matches("^data:image\\/.*?;base64,.*$")) {
+ fBase64Image = base64Image;
+ int from = base64Image.indexOf("/") + 1;
+ int to = base64Image.indexOf(";");
+ setExtension(base64Image.substring(from, to).trim());
+ setExtension(base64Image.substring(from, to).trim());
+ } else {
+ int length = base64Image.length();
+ throw new RuntimeException("Wrong base64 data " + base64Image.substring(0, length < 50 ? length : 50));
+ }
+ }
+
+ /**
+ * Sets the maximum height that this image can display. For example, if you have
+ * a 32x32 image the framework will blow it up to a larger size which will not
+ * work for the image and you might want to pass 64 to indicate that the image
+ * cannot be resized passed 64 pixels. If the height is not set or set to
+ * {@link #UNDEFINED}, then it is automatically resized based on aspect ratio
+ * and maximum width.
+ *
+ * @param maxHeight
+ * the maximum height for this image or {@link #UNDEFINED}
+ * @return this
+ * @see #setAspectRatio(double)
+ * @see #setAspectRatio(int, int, boolean)
+ * @see #setMaxWidth(int)
+ */
+ public TipImage setMaxHeight(int maxHeight) {
+ fMaxHeight = maxHeight;
+ return this;
+ }
+
+ /**
+ * Sets the maximum width that this image can display. For example, if you have
+ * a 32x32 image the framework will blow it up to a larger size which will not
+ * work for the image and you might want to pass 64 to indicate that the image
+ * cannot be resized passed 64 pixels. If the width is not set or set to
+ * {@link #UNDEFINED}, it is automatically resized based on aspect ratio and
+ * maximum height.
+ *
+ * @param maxWidth
+ * the maximum width for this image or {@link #UNDEFINED}
+ * @return this
+ * @see #setAspectRatio(double)
+ * @see #setAspectRatio(int, int, boolean)
+ * @see #setMaxHeight(int)
+ */
+ public TipImage setMaxWidth(int maxWidth) {
+ fMaxWidth = maxWidth;
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of this image. If the image is 300 wide and 600 high
+ * then the aspect ratio is 300/600 = 0,5 (1:2). If your image is 1200 wide and
+ * 250 high then the aspect ratio (1200/250) = 4,8. With the supplied values the
+ * best dimensions for the image can be calculated give the available space in
+ * the UI.
+ * <p>
+ * In case you pass true for <code>pSetAsMax</code> then the image can not be
+ * up-scaled beyond the specified size. So if your image is 200x200 and you want
+ * a maximum up-scale of 2 then pass 400x400 to this method to maintain the
+ * aspect ratio but allow the image to be resized to maximum it's double size.
+ * <p>
+ * The recommended aspect ratio is around 3:2 (1.5) to be comfortably displayed
+ * in the Tip UI.
+ *
+ * @param width
+ * the width of the image, must be greater than 0
+ * @param height
+ * the height of the image, must be greater than 0
+ * @param setAsMax
+ * true to set the passed width and height as the maximum width and
+ * height for the image
+ * @return this
+ * @see #setAspectRatio(double)
+ * @see #getIMGAttributes(int, int)
+ * @see #setMaxHeight(int)
+ * @see #setMaxWidth(int)
+ */
+ public TipImage setAspectRatio(int width, int height, boolean setAsMax) {
+ Assert.isTrue(width > 0);
+ Assert.isTrue(height > 0);
+ fAspectRatio = (double) width / (double) height;
+ if (setAsMax) {
+ setMaxHeight(height);
+ setMaxWidth(width);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the aspect ratio of your image which is defined by width divided by
+ * height.
+ * <p>
+ * The recommended aspect ratio is around 3:2 (1.5) to be comfortably displayed
+ * in the Tip UI.
+ *
+ *
+ * @param aspectRatio
+ * the aspect ration
+ * @return this
+ * @see #setAspectRatio(int, int, boolean)
+ * @see #getIMGAttributes(int, int)
+ */
+ public TipImage setAspectRatio(double aspectRatio) {
+ fAspectRatio = aspectRatio;
+ return this;
+ }
+
+ /**
+ * Changes the default value "null" to the passed value which commonly is "png",
+ * "gif" and such.
+ *
+ * @param extension
+ * the extension of this file
+ * @return this
+ * @see #getExtension()
+ */
+ public TipImage setExtension(String extension) {
+ fExtension = extension;
+ return this;
+ }
+
+ /**
+ * Returns the base64 encoded image string according to RFC-2397 or null. The
+ * recommended aspect ratio is around 3:2.
+ *
+ * @return the base64 encoded image string according to RFC-2397 or null
+ */
+ public String getBase64Image() {
+ return fBase64Image;
+ }
+
+ /**
+ * Returns the width and height attributes of the HTML IMG tag.
+ *
+ * <pre>
+ * &lt;img src="smiley.gif" height="42" width="42"&gt;
+ * </pre>
+ *
+ * The available space in the UI is passed and with it the best size of the
+ * image will be calculated based on the aspect ratio of this image.
+ *
+ * Clients may override if they can provide better information.
+ *
+ * @param widthHint
+ * the available width which must be greater than 0
+ * @param heightHint
+ * the available height which must be greater than 0
+ * @return the attributes in the HTML img tag
+ * @see TipImage#setAspectRatio(double)
+ * @see TipImage#setAspectRatio(int, int, boolean)
+ * @see TipImage#setMaxHeight(int)
+ * @see TipImage#setMaxWidth(int)
+ */
+ public String getIMGAttributes(int widthHint, int heightHint) {
+
+ int myWidthHint = (fMaxWidth == UNDEFINED) ? widthHint : Math.min(widthHint, fMaxWidth);
+ int myHeightHint = (fMaxHeight == UNDEFINED) ? heightHint : Math.min(heightHint, fMaxHeight);
+
+ int width = ImageUtil.getWidth(fAspectRatio, myWidthHint, myHeightHint);
+ int height = ImageUtil.getHeight(fAspectRatio, myWidthHint, myHeightHint);
+
+ String result = "";
+ if (fMaxWidth == UNDEFINED) {
+ result += " width=\"" + width + "\"";
+ } else {
+ result += " width=\"" + Math.min(fMaxWidth, width) + "\"";
+ }
+
+ if (fMaxHeight == UNDEFINED) {
+ result += " height=\"" + height + "\"";
+ } else {
+ result += " height=\"" + Math.min(fMaxHeight, height) + "\"";
+ }
+ return result;
+ }
+
+ /**
+ * Returns the image extension for use in the IMG tag for the data attribute
+ * (<code>data:image/???</code>). If the extension is not set in this object,
+ * then the URL is examined to find the extension. If that can not be determined
+ * then "png" is returned.
+ *
+ * @return the extension
+ */
+ private String getExtension() {
+ if (fExtension != null) {
+ return fExtension;
+ }
+ String[] split = fURL.getPath().split("\\.");
+ if (split.length > 1) {
+ fExtension = split[split.length - 1];
+ } else {
+ fExtension = "png";
+ }
+ return fExtension;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipManager.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipManager.java
new file mode 100644
index 000000000..2101f160c
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipManager.java
@@ -0,0 +1,260 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.eclipse.tips.core.internal.LogUtil;
+
+/**
+ * An abstract implementation of ITipManager with additional control API. While
+ * the rest of the framework must work with ITipManager, this class provides API
+ * to open the dialog and do low level housekeeping that is of no concern to
+ * external participants (Tip and TipProvider).
+ *
+ */
+public abstract class TipManager implements ITipManager {
+
+ private Map<String, TipProvider> fProviders = new HashMap<>();
+ private Map<Integer, List<String>> fProviderPrio = new TreeMap<>();
+ protected boolean fOpen;
+ private boolean fServeReadTips = false;
+ private TipProviderListenerManager fListenerManager = new TipProviderListenerManager();
+ private boolean fIsDiposed;
+
+ /**
+ * Instantiates a new TipManager.
+ */
+ public TipManager() {
+ }
+
+ /**
+ * Gets the provider with the specified ID.
+ *
+ * @param providerID
+ * the id of the provider to fetch
+ * @return the provider with the specified ID or null if no such provider
+ * exists.
+ * @see TipProvider#getID()
+ */
+ public TipProvider getProvider(String providerID) {
+ checkDisposed();
+ return fProviders.get(providerID);
+ }
+
+ /**
+ * Binds the passed provider to this manager. Implementations should override,
+ * call super and the asynchronously call the
+ * {@link TipProvider#loadNewTips(org.eclipse.core.runtime.IProgressMonitor)}
+ * method.
+ *
+ * @param provider
+ * the {@link TipProvider} to register.
+ *
+ * @return this
+ */
+ @Override
+ public ITipManager register(TipProvider provider) {
+ checkDisposed();
+ log(LogUtil.info("Registering provider: " + provider.getID() + " : " + provider.getDescription()));
+ provider.setManager(this);
+ addToMaps(provider, new Integer(getPriority(provider)));
+ provider.getListenerManager().addProviderListener(
+ myProvider -> getListenerManager().notifyListeners(TipProviderListener.EVENT_READY, myProvider));
+ return this;
+ }
+
+ private void checkDisposed() {
+ if (isDisposed()) {
+ throw new RuntimeException("This TipManager is disposed.");
+ }
+
+ }
+
+ /**
+ * Calculates the priority that this provider has in the Tips framework. The
+ * {@link TipProvider#getExpression()} was purposed to aid in the calculation of
+ * the priority.
+ *
+ * @param provider
+ * the provider
+ * @return the priority, lower is higher, never negative.
+ */
+ public abstract int getPriority(TipProvider provider);
+
+ private synchronized void addToMaps(TipProvider pProvider, Integer pPriorityHint) {
+ removeFromMaps(pProvider);
+ addToProviderMaps(pProvider, pPriorityHint);
+ addToPriorityMap(pProvider, pPriorityHint);
+ }
+
+ private void addToPriorityMap(TipProvider provider, Integer priorityHint) {
+ if (!fProviderPrio.get(priorityHint).contains(provider.getID())) {
+ if (!fProviderPrio.get(priorityHint).contains(provider.getID())) {
+ fProviderPrio.get(priorityHint).add(provider.getID());
+ }
+ }
+ }
+
+ private void addToProviderMaps(TipProvider provider, Integer priorityHint) {
+ fProviders.put(provider.getID(), provider);
+ if (fProviderPrio.get(priorityHint) == null) {
+ fProviderPrio.put(priorityHint, new ArrayList<>());
+ }
+ }
+
+ private void removeFromMaps(TipProvider provider) {
+ if (fProviders.containsKey(provider.getID())) {
+ for (Map.Entry<Integer, List<String>> entry : fProviderPrio.entrySet()) {
+ entry.getValue().remove(provider.getID());
+ }
+ fProviders.remove(provider.getID());
+ }
+ }
+
+ /**
+ * The returned list contains providers ready to serve tips and is guaranteed to
+ * be in a prioritised order according the implementation of this manager.
+ *
+ * @return the prioritised list of ready providers with tips in an immutable
+ * list.
+ */
+ public List<TipProvider> getProviders() {
+ checkDisposed();
+ if (fProviders == null) {
+ return Collections.emptyList();
+ }
+ ArrayList<TipProvider> result = new ArrayList<>();
+ for (Map.Entry<Integer, List<String>> entry : fProviderPrio.entrySet()) {
+ for (String id : entry.getValue()) {
+ if (fProviders.get(id).isReady()) {
+ result.add(fProviders.get(id));
+ }
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ /**
+ * Determines if the Tips framework must run at startup. The default
+ * implementation returns true, subclasses should probably override this.
+ *
+ * @return true if the Tips framework should run at startup.
+ * @see TipManager#setRunAtStartup(boolean)
+ */
+ public boolean isRunAtStartup() {
+ checkDisposed();
+ return true;
+ }
+
+ /**
+ * Determines if the Tips framework must run at startup.
+ *
+ * @param shouldRun
+ * true if the tips should be displayed at startup, false otherwise.
+ *
+ * @return this
+ *
+ * @see #isRunAtStartup()
+ */
+ public abstract TipManager setRunAtStartup(boolean shouldRun);
+
+ /**
+ * Opens the Tip of the Day dialog.
+ *
+ * @param startUp
+ * When called from a startup situation, true must be passed for
+ * <code>pStartup</code>. If in a manual starting situation, false
+ * must be passed. This enables the manager to decide to skip opening
+ * the dialog at startup (e.g., no new tip items).
+ *
+ * @return this
+ *
+ * @see #isOpen()
+ */
+ public abstract TipManager open(boolean startUp);
+
+ /**
+ * The default implementation disposes of this manager and all the TipProviders
+ * when the dialog is disposed. Subclasses may override but must call super.
+ */
+ public void dispose() {
+ checkDisposed();
+ try {
+ for (TipProvider provider : fProviders.values()) {
+ try {
+ provider.dispose();
+ } catch (Exception e) {
+ log(LogUtil.error(e));
+ }
+ }
+ } finally {
+ fProviders.clear();
+ fProviderPrio.clear();
+ fIsDiposed = true;
+ }
+ }
+
+ /**
+ * @return returns true if the tips are currently being displayed in some way.
+ */
+ public boolean isOpen() {
+ checkDisposed();
+ return fOpen;
+ }
+
+ /**
+ * Indicates whether read tips must be served or not. Subclasses could override,
+ * to save the state somewhere, but must call super.
+ *
+ * @param serveRead
+ * true of read tips may be served by the {@link TipProvider}s
+ * @return this
+ * @see TipManager#mustServeReadTips()
+ */
+ public TipManager setServeReadTips(boolean serveRead) {
+ checkDisposed();
+ fServeReadTips = serveRead;
+ return this;
+ }
+
+ /**
+ * Indicates whether already read tips must be served or not.
+ *
+ * @return true or false
+ * @see #setServeReadTips(boolean)
+ */
+ @Override
+ public boolean mustServeReadTips() {
+ checkDisposed();
+ return fServeReadTips;
+ }
+
+ /**
+ * Gets the listener manager so that interested parties can subscribe to the
+ * events of this provider.
+ *
+ * @return the listener manager, never null.
+ */
+ public TipProviderListenerManager getListenerManager() {
+ return fListenerManager;
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return fIsDiposed;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java
new file mode 100644
index 000000000..fde1da7b3
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProvider.java
@@ -0,0 +1,332 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.tips.core.internal.FinalTip;
+
+/**
+ * Class to provide tips to the tip framework. It is the job of this provider to
+ * manage its tips. Examples of managing tips are:
+ *
+ * <ul>
+ * <li>Loading tips from the internet</li>
+ * <li>Serve next, previous and current tip on request</li>
+ * </ul>
+ *
+ * After the TipProvider is instantiated by the {@link ITipManager}, the
+ * TipManager will insert itself by calling {@link #setManager(ITipManager)}.
+ * Then the TipManager will asynchronous call this providers'
+ * {@link #loadNewTips(IProgressMonitor)} method. The job of the load() method
+ * is to do long work like fetching new tips from the internet and storing them
+ * locally. There is no defined method on how tips should be stored locally,
+ * implementers are free to do what is needed.
+ *
+ * The constructor must return fast, meaning that tips may not be fetched from
+ * the Internet in the constructor. This should be done in the
+ * {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * To indicate that this provider is ready to serve tips, it should call the
+ * {@link #setTips(List)} method which then sets its <code>ready</code> flag.
+ *
+ */
+public abstract class TipProvider {
+
+ private ITipManager fTipManager;
+ private int fTipIndex;
+ protected List<Tip> fTips = new ArrayList<>();
+ private Tip fCurrentTip;
+ private boolean fReady;
+ private TipProviderListenerManager fListenerManager = new TipProviderListenerManager();
+ private Tip fFinalTip = new FinalTip(getID());
+ private String fExpression;
+
+ /**
+ * The zero argument constructor must be able to instantiate the TipProvider.
+ * This method may also be used to quickly set the available tips by calling the
+ * {@link #setTips(List)} method. The constructor may not be used to load tips
+ * from the internet. Use the {@link #loadNewTips(IProgressMonitor)} method for
+ * this purpose.
+ *
+ * @see #loadNewTips(IProgressMonitor)
+ * @see #setTips(List)
+ */
+ public TipProvider() {
+ }
+
+ /**
+ * Provides the opportunity to release all held resources.
+ */
+ public abstract void dispose();
+
+ /**
+ * @return the short description of this provider.
+ */
+ public abstract String getDescription();
+
+ /**
+ * @return the ID of this provider
+ */
+ public abstract String getID();
+
+ /**
+ * The image used by the UI for low resolution
+ *
+ * @return a 48x48 {@link TipImage}
+ */
+ public abstract TipImage getImage();
+
+ /**
+ * Get a list of tips. The default implementation returns tips based on the
+ * following conditions: <br>
+ * <dl>
+ * <dt><code>pFilter</code> is false</dt>
+ * <dd>Return all read and unread tips.</dd>
+ * <dt><code>pFilter</code> is true</dt>
+ * <dd>Return read and unread tips if the tipManager may serve unread tips,
+ * otherwise return only unread tips.</dd>
+ * </dl>
+ * <p>
+ * Subclasses may override (calling super(false) to fetch the list) if they want
+ * to serve or sort the list of tips in a different way.
+ *
+ * @param filter
+ * false or true, see description above.
+ * @return an unmodifiable list of tips.
+ */
+ public synchronized List<Tip> getTips(boolean filter) {
+ if (filter) {
+ return Collections.unmodifiableList(fTips //
+ .stream() //
+ .filter(tip -> getManager().mustServeReadTips() || !getManager().isRead(tip)) //
+ .sorted(Comparator.comparing(Tip::getCreationDate).reversed()) //
+ .collect(Collectors.toList()));
+ }
+ return Collections.unmodifiableList(fTips);
+ }
+
+ /**
+ * @return the {@link Tip} that was last returned by {@link #getNextTip()} or
+ * {@link #getPreviousTip()}
+ */
+ public synchronized Tip getCurrentTip() {
+ if (fCurrentTip == null) {
+ return getNextTip();
+ }
+ return fCurrentTip;
+ }
+
+ /**
+ * The next {@link Tip} is returned based on the read status of the Tip and the
+ * fact if already read tips must be served or not which is known by the
+ * {@link ITipManager}: ({@link ITipManager#mustServeReadTips()}).
+ *
+ * @return the next {@link Tip}
+ * @see #getPreviousTip()
+ * @see #getCurrentTip()
+ */
+ public synchronized Tip getNextTip() {
+ boolean unreadOnly = !getManager().mustServeReadTips();
+ List<Tip> list = getTips(unreadOnly);
+ if (list.isEmpty()) {
+ return setCurrentTip(fFinalTip);
+ }
+ if (!unreadOnly && fCurrentTip != null) {
+ fTipIndex++;
+ } else if (fCurrentTip != null && getManager().isRead(fCurrentTip)) {
+ fTipIndex++;
+ }
+ if (fTipIndex >= list.size()) {
+ fTipIndex = 0;
+ }
+ return setCurrentTip(list.get(fTipIndex));
+ }
+
+ /**
+ * @return the previous {@link Tip}
+ * @see #getNextTip()
+ * @see #getCurrentTip()
+ */
+ public Tip getPreviousTip() {
+ List<Tip> list = getTips(!getManager().mustServeReadTips());
+ if (list.isEmpty()) {
+ return setCurrentTip(fFinalTip);
+ }
+ fTipIndex--;
+ if (fTipIndex < 0) {
+ fTipIndex = list.size() - 1;
+ }
+ return setCurrentTip(list.get(fTipIndex));
+ }
+
+ /**
+ * @return the {@link ITipManager} of this provider, never null.
+ */
+ public synchronized ITipManager getManager() {
+ return fTipManager;
+ }
+
+ /**
+ * @return true if the provider is ready to deliver tips
+ */
+ public final boolean isReady() {
+ return fReady;
+ }
+
+ /**
+ * Is called asynchronously during startup of the TipManager to gather new tips.
+ *
+ * The provider is not available to the UI unless it has called it's
+ * {@link #setTips(List)} method. It is therefore possible that the provider is
+ * not immediately visible in the tip UI but will be added later.
+ * <p>
+ * If you run out of tips and you feel that you should load more tips on your
+ * own then you can also asynchronously call this method. A good place would be
+ * to override {@link #getTips(boolean)}, check if the supply of tips is
+ * sufficient and then call this method asynchronously.
+ * <p>
+ * One strategy is to do a long running fetch in this method and then store the
+ * tips locally. On the next run of the TipManager, the fetched tips can be
+ * served from the constructor (i.e. by calling {@link #setTips(List)}), making
+ * them available immediately
+ *
+ * @param monitor
+ * The monitor to report back progress.
+ * @return the status in case you want to report problems.
+ * @see TipProvider#setTips(List)
+ * @see TipProvider#isReady()
+ */
+ public abstract IStatus loadNewTips(IProgressMonitor monitor);
+
+ private synchronized Tip setCurrentTip(Tip pTip) {
+ fCurrentTip = pTip;
+ return fCurrentTip;
+ }
+
+ /**
+ * Sets the TipManager. You should probably not call this method directly. This
+ * method is normally called after the provider is instantiated by the
+ * {@link ITipManager}. If you create the provider yourself you should register
+ * the provider with {@link ITipManager#register(TipProvider)} which in turn
+ * will call this method. Subclasses may override but must not forget to call
+ * super in order to save the {@link ITipManager}.
+ *
+ * @param tipManager
+ * the {@link ITipManager}
+ * @return this
+ */
+ public synchronized TipProvider setManager(ITipManager tipManager) {
+ fTipManager = tipManager;
+ return this;
+ }
+
+ /**
+ * Sets the tips for this provider, replacing the current set of tips, and sets
+ * the <code>ready</code> flag to true. This method is typically called from the
+ * constructor of the {@link TipProvider} but may also be called from the
+ * asynchronous {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * @param tips
+ * a list of {@link Tip} objects
+ * @return this
+ * @see #addTips(List)
+ * @see #isReady()
+ * @see #loadNewTips(IProgressMonitor)
+ */
+ public TipProvider setTips(List<Tip> tips) {
+ if(getManager().isDisposed()) {
+ return this;
+ }
+ doSetTips(tips, true);
+ fReady = true;
+ fListenerManager.notifyListeners(TipProviderListener.EVENT_READY, this);
+ return this;
+ }
+
+ /**
+ * Adds the passed tips to the set of tips this provider already has sets the
+ * <code>ready</code> flag to true. This method is typically called from the
+ * constructor of the {@link TipProvider} but may also be called from the
+ * asynchronous {@link #loadNewTips(IProgressMonitor)} method.
+ *
+ * @param tips
+ * a list of {@link Tip} objects
+ * @return this
+ * @see #setTips(List)
+ * @see #isReady()
+ * @see #loadNewTips(IProgressMonitor)
+ */
+ public TipProvider addTips(List<Tip> tips) {
+ doSetTips(tips, false);
+ fReady = true;
+ fListenerManager.notifyListeners(TipProviderListener.EVENT_READY, this);
+ return this;
+ }
+
+ private synchronized void doSetTips(List<Tip> tips, boolean replace) {
+ if (replace) {
+ fTips.clear();
+ }
+ fTips.addAll(tips);
+ }
+
+ /**
+ * Gets the listener manager so that interested parties can subscribe to the
+ * events of this provider.
+ *
+ * @return the {@link TipProviderListenerManager}
+ */
+ public TipProviderListenerManager getListenerManager() {
+ return fListenerManager;
+ }
+
+ /**
+ * Returns an expression that is used by the {@link ITipManager} to determine
+ * the priority of this provider. The expression can be used to advice the
+ * TipManager when the tips of this provider deserve priority. The Eclipse IDE
+ * TipManager uses the core expression from the o.e.core.runtime bundle.
+ * Example: The expression
+ *
+ * <pre>
+ * &lt;with
+ * variable="activeWorkbenchWindow.activePerspective"&gt;
+ * &lt;equals value="org.eclipse.jdt.ui.JavaPerspective"&gt;&lt;/equals&gt;
+ * &lt;/with&gt;
+ * </pre>
+ *
+ * will give the provider priority when the java perspective is active in the
+ * IDE
+ *
+ * @return the expression which can be empty or null.
+ */
+ public String getExpression() {
+ return fExpression;
+ }
+
+ /**
+ * Sets the expression to determine the priority of the provider.
+ *
+ * @param expression
+ * the expression, may be null.
+ *
+ * @see #getExpression()
+ */
+ public void setExpression(String expression) {
+ fExpression = expression;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListener.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListener.java
new file mode 100644
index 000000000..f9d4b59d3
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListener.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+/**
+ * Provides notifications on TipProvider events.
+ *
+ */
+@FunctionalInterface
+public interface TipProviderListener {
+
+ /**
+ * Event ready. The TipProvider is ready to serve tips.
+ */
+ public static final int EVENT_READY = 1;
+
+ /**
+ * When the subject is ready to serve tips (e.g. fetched new tips from
+ * somewhere) it will The provider is ready to serve tips. The default
+ * implementation does nothing, subclasses may override.
+ *
+ * @param provider
+ * the provider.
+ */
+ public void providerReady(TipProvider provider);
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListenerManager.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListenerManager.java
new file mode 100644
index 000000000..985cb0ecd
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/TipProviderListenerManager.java
@@ -0,0 +1,67 @@
+/****************************************************************************
+ * Copyright (c) 2017, 2018 Remain Software
+ * 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:
+ * Wim Jongman <wim.jongman@remainsoftware.com> - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to manage provider listeners.
+ */
+public class TipProviderListenerManager {
+
+ protected List<TipProviderListener> fListeners = new ArrayList<>();
+
+ /**
+ * Notifies all listeners that an event occurred.
+ *
+ * @param event
+ * the event
+ * @param provider
+ * the provider
+ */
+ public void notifyListeners(int event, TipProvider provider) {
+ synchronized (fListeners) {
+ for (TipProviderListener tipProviderListener : fListeners) {
+ switch (event) {
+ case TipProviderListener.EVENT_READY:
+ tipProviderListener.providerReady(provider);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a listener to the list of listeners.
+ *
+ * @param pProviderListener
+ * the listener to be notified
+ * @return this
+ */
+ public TipProviderListenerManager addProviderListener(TipProviderListener pProviderListener) {
+ fListeners.remove(pProviderListener);
+ fListeners.add(pProviderListener);
+ return this;
+ }
+
+ /**
+ * Removes a listener from the list of listeners.
+ *
+ * @param pProviderListener
+ * the listener to remove
+ * @return this
+ */
+ public TipProviderListenerManager removeProviderListener(TipProviderListener pProviderListener) {
+ fListeners.remove(pProviderListener);
+ return this;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/FinalTip.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/FinalTip.java
new file mode 100644
index 000000000..fbc8b8c26
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/FinalTip.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core.internal;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * Special generic tip that tells the user that there are no more tips.
+ *
+ */
+public class FinalTip extends Tip implements IHtmlTip {
+
+ /**
+ * Constructor.
+ */
+ public FinalTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ Calendar instance = Calendar.getInstance();
+ return instance.getTime();
+ }
+
+ @Override
+ public String getSubject() {
+ return "No more tips";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h1>There are no more tips</h1>" //
+ + "This provider has no more new tips. You can toggle the " //
+ + "<b>Unread</b> checkbox below or select another provider.";
+ }
+
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("images/nomoretips.png")).setAspectRatio(417, 640, false);
+ } catch (IOException e) {
+// getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/ImageUtil.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/ImageUtil.java
new file mode 100644
index 000000000..0a338de6c
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/ImageUtil.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core.internal;
+
+/**
+ * Image helper class.
+ *
+ */
+public class ImageUtil {
+
+ /**
+ * Calculate a height that will fit in the proposed rectangle given the passed
+ * aspect ratio.
+ *
+ * @param aspectRatio
+ * the aspect ration, e.g. 1.5 for a 3/2 ratio (width/height).
+ * @param widthHint
+ * the available width in the viewport
+ * @param heightHint
+ * the available height in the viewport
+ * @return the maximum height that will fit in the passed rectangle.
+ */
+ public static int getHeight(double aspectRatio, int widthHint, int heightHint) {
+ return (int) Math.min(heightHint, widthHint / aspectRatio);
+ }
+
+ /**
+ * Calculate a width that will fit in the proposed rectangle given the passed
+ * aspect ratio.
+ *
+ * @param aspectRatio
+ * the aspect ration, e.g. 1.5 for a 3/2 ratio (width/height).
+ * @param widthHint
+ * the available width in the viewport
+ * @param heightHint
+ * the available height in the viewport
+ * @return the maximum width that will fit in the rectangle.
+ */
+ public static int getWidth(double aspectRatio, int widthHint, int heightHint) {
+ return (int) Math.min(widthHint, heightHint * aspectRatio);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/LogUtil.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/LogUtil.java
new file mode 100644
index 000000000..976f4e03e
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/LogUtil.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core.internal;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.FrameworkUtil;
+
+public class LogUtil {
+
+ public static IStatus getStatus(int severity, String bundleId, String message, Throwable throwable) {
+ return new Status(severity, bundleId, message, throwable);
+ }
+
+ public static IStatus error(Throwable throwable) {
+ return getStatus(IStatus.ERROR, "org.eclipse.tips.core", throwable.getMessage(), throwable);
+ }
+
+ public static IStatus warn(Throwable throwable) {
+ return getStatus(IStatus.WARNING, "org.eclipse.tips.core", throwable.getMessage(), throwable);
+ }
+
+ public static IStatus info(Throwable throwable) {
+ return getStatus(IStatus.INFO, "org.eclipse.tips.core", throwable.getMessage(), throwable);
+ }
+
+ public static IStatus error(Class<?> clazz, Throwable throwable) {
+ return getStatus(IStatus.ERROR, getBundleId(clazz), throwable.getMessage(), throwable);
+ }
+
+ public static IStatus warn(Class<?> clazz, Throwable throwable) {
+ return getStatus(IStatus.WARNING, getBundleId(clazz), throwable.getMessage(), throwable);
+ }
+
+ public static IStatus info(Class<?> clazz, Throwable throwable) {
+ return getStatus(IStatus.INFO, getBundleId(clazz), throwable.getMessage(), throwable);
+ }
+
+ public static IStatus info(Class<?> clazz, String message) {
+ return getStatus(IStatus.INFO, getBundleId(clazz), message, null);
+ }
+
+ public static IStatus warn(Class<?> clazz, String pessage) {
+ return getStatus(IStatus.WARNING, getBundleId(clazz), pessage, null);
+ }
+
+ public static IStatus error(Class<?> clazz, String message) {
+ return getStatus(IStatus.ERROR, getBundleId(clazz), message, null);
+ }
+
+ private static String getBundleId(Class<?> clazz) {
+ if (FrameworkUtil.getBundle(clazz) != null) {
+ return FrameworkUtil.getBundle(clazz).getSymbolicName();
+ }
+ return "osgi.not.running";
+ }
+
+ public static IStatus info(String message) {
+ return getStatus(IStatus.INFO, "org.eclipse.tips.core", message, null);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/package-info.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/package-info.java
new file mode 100644
index 000000000..c790f96d4
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/internal/package-info.java
@@ -0,0 +1,14 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Tip Framework API internal classes.
+ */
+package org.eclipse.tips.core.internal; \ No newline at end of file
diff --git a/org.eclipse.tips.core/src/org/eclipse/tips/core/package-info.java b/org.eclipse.tips.core/src/org/eclipse/tips/core/package-info.java
new file mode 100644
index 000000000..f63aef9af
--- /dev/null
+++ b/org.eclipse.tips.core/src/org/eclipse/tips/core/package-info.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Tip Framework core API. A UI agnostic API for the Tip of the Day. Known
+ * implementation is in package org.eclipse.tips.ui. If you run in the Eclipse
+ * IDE you may use the tips extension point to register a new
+ * {@link org.eclipse.tips.core.TipProvider}.If you want to run the Tip
+ * framework outside of the IDE (e.g. in an RCP application) then you should
+ * also implement your own {@link org.eclipse.tips.core.TipManager} and start
+ * that with some action or at startup of your application.
+ */
+package org.eclipse.tips.core; \ No newline at end of file
diff --git a/org.eclipse.tips.examples/.classpath b/org.eclipse.tips.examples/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.eclipse.tips.examples/.classpath
@@ -0,0 +1,7 @@
+<?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"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.tips.examples/.gitignore b/org.eclipse.tips.examples/.gitignore
new file mode 100644
index 000000000..09e3bc9b2
--- /dev/null
+++ b/org.eclipse.tips.examples/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/org.eclipse.tips.examples/.project b/org.eclipse.tips.examples/.project
new file mode 100644
index 000000000..13d6a0d84
--- /dev/null
+++ b/org.eclipse.tips.examples/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.examples</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>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.examples/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tips.examples/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/org.eclipse.tips.examples/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.tips.examples/META-INF/MANIFEST.MF b/org.eclipse.tips.examples/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..7dd6c1b4d
--- /dev/null
+++ b/org.eclipse.tips.examples/META-INF/MANIFEST.MF
@@ -0,0 +1,16 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Tip of the Day Examples
+Bundle-SymbolicName: org.eclipse.tips.examples;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-Vendor: Eclipse
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.eclipse.tips.core;bundle-version="0.1.0",
+ org.eclipse.tips.json;bundle-version="0.1.0",
+ org.eclipse.swt,
+ org.eclipse.jface,
+ org.eclipse.tips.ui;bundle-version="0.1.0"
+Eclipse-BundleShape: dir
+Import-Package: org.osgi.framework;version="1.8.0"
+Automatic-Module-Name: org.eclipse.tips.examples
diff --git a/org.eclipse.tips.examples/build.properties b/org.eclipse.tips.examples/build.properties
new file mode 100644
index 000000000..5e55821c1
--- /dev/null
+++ b/org.eclipse.tips.examples/build.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/,\
+ matrixrain/,\
+ images/
diff --git a/org.eclipse.tips.examples/icons/48/c++.png b/org.eclipse.tips.examples/icons/48/c++.png
new file mode 100644
index 000000000..11e205e6d
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/c++.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/ecf.png b/org.eclipse.tips.examples/icons/48/ecf.png
new file mode 100644
index 000000000..af873b30f
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/ecf.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/eclipse.png b/org.eclipse.tips.examples/icons/48/eclipse.png
new file mode 100644
index 000000000..276f40be6
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/eclipse.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/java.png b/org.eclipse.tips.examples/icons/48/java.png
new file mode 100644
index 000000000..7129f6d73
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/java.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/key.png b/org.eclipse.tips.examples/icons/48/key.png
new file mode 100644
index 000000000..920d543e5
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/key.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/nebula.png b/org.eclipse.tips.examples/icons/48/nebula.png
new file mode 100644
index 000000000..80d5057cf
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/nebula.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/pin.png b/org.eclipse.tips.examples/icons/48/pin.png
new file mode 100644
index 000000000..a7bfc70e1
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/pin.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/plugin.png b/org.eclipse.tips.examples/icons/48/plugin.png
new file mode 100644
index 000000000..ef644a0a8
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/plugin.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/pydev.png b/org.eclipse.tips.examples/icons/48/pydev.png
new file mode 100644
index 000000000..72639f65c
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/pydev.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/swt.png b/org.eclipse.tips.examples/icons/48/swt.png
new file mode 100644
index 000000000..cc6a5ea3a
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/swt.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/tips.png b/org.eclipse.tips.examples/icons/48/tips.png
new file mode 100644
index 000000000..cd19de252
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/tips.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/48/twitter.png b/org.eclipse.tips.examples/icons/48/twitter.png
new file mode 100644
index 000000000..5f17261f9
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/48/twitter.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/c++.png b/org.eclipse.tips.examples/icons/64/c++.png
new file mode 100644
index 000000000..a948ccbef
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/c++.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/ecf.png b/org.eclipse.tips.examples/icons/64/ecf.png
new file mode 100644
index 000000000..57e09478a
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/ecf.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/eclipse.png b/org.eclipse.tips.examples/icons/64/eclipse.png
new file mode 100644
index 000000000..01aa23b91
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/eclipse.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/java.png b/org.eclipse.tips.examples/icons/64/java.png
new file mode 100644
index 000000000..36cbf25c2
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/java.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/key.png b/org.eclipse.tips.examples/icons/64/key.png
new file mode 100644
index 000000000..cc1694c12
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/key.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/nebula.png b/org.eclipse.tips.examples/icons/64/nebula.png
new file mode 100644
index 000000000..674fe6cb4
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/nebula.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/pin.png b/org.eclipse.tips.examples/icons/64/pin.png
new file mode 100644
index 000000000..c065db4cf
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/pin.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/plugin.png b/org.eclipse.tips.examples/icons/64/plugin.png
new file mode 100644
index 000000000..a75ab0593
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/plugin.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/pydev.png b/org.eclipse.tips.examples/icons/64/pydev.png
new file mode 100644
index 000000000..ece92aa5d
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/pydev.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/swt.png b/org.eclipse.tips.examples/icons/64/swt.png
new file mode 100644
index 000000000..6acbcb124
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/swt.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/tips.png b/org.eclipse.tips.examples/icons/64/tips.png
new file mode 100644
index 000000000..32fb73b60
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/tips.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/64/twitter.png b/org.eclipse.tips.examples/icons/64/twitter.png
new file mode 100644
index 000000000..dc874f378
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/64/twitter.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/asterisk_yellow.png b/org.eclipse.tips.examples/icons/asterisk_yellow.png
new file mode 100644
index 000000000..bab7cc9bc
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/asterisk_yellow.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/bug_link.png b/org.eclipse.tips.examples/icons/bug_link.png
new file mode 100644
index 000000000..30e25abac
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/bug_link.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/clock.png b/org.eclipse.tips.examples/icons/clock.png
new file mode 100644
index 000000000..e2672c206
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/clock.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/cut.png b/org.eclipse.tips.examples/icons/cut.png
new file mode 100644
index 000000000..f215d6f6b
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/cut.png
Binary files differ
diff --git a/org.eclipse.tips.examples/icons/lightbulb.png b/org.eclipse.tips.examples/icons/lightbulb.png
new file mode 100644
index 000000000..d22fde8ba
--- /dev/null
+++ b/org.eclipse.tips.examples/icons/lightbulb.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/eclipsetips/tip1.gif b/org.eclipse.tips.examples/images/eclipsetips/tip1.gif
new file mode 100644
index 000000000..14a62bdd9
--- /dev/null
+++ b/org.eclipse.tips.examples/images/eclipsetips/tip1.gif
Binary files differ
diff --git a/org.eclipse.tips.examples/images/eclipsetips/tip2.png b/org.eclipse.tips.examples/images/eclipsetips/tip2.png
new file mode 100644
index 000000000..2e2fab32e
--- /dev/null
+++ b/org.eclipse.tips.examples/images/eclipsetips/tip2.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/eclipsetips/tip3.png b/org.eclipse.tips.examples/images/eclipsetips/tip3.png
new file mode 100644
index 000000000..adbe9bdb3
--- /dev/null
+++ b/org.eclipse.tips.examples/images/eclipsetips/tip3.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/java/duke.png b/org.eclipse.tips.examples/images/java/duke.png
new file mode 100644
index 000000000..4a4e303d1
--- /dev/null
+++ b/org.eclipse.tips.examples/images/java/duke.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/tips/navigate1.png b/org.eclipse.tips.examples/images/tips/navigate1.png
new file mode 100644
index 000000000..4f4fc3a2d
--- /dev/null
+++ b/org.eclipse.tips.examples/images/tips/navigate1.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/tips/navigate2.png b/org.eclipse.tips.examples/images/tips/navigate2.png
new file mode 100644
index 000000000..dcac33c9b
--- /dev/null
+++ b/org.eclipse.tips.examples/images/tips/navigate2.png
Binary files differ
diff --git a/org.eclipse.tips.examples/images/tips/starttip.gif b/org.eclipse.tips.examples/images/tips/starttip.gif
new file mode 100644
index 000000000..045318617
--- /dev/null
+++ b/org.eclipse.tips.examples/images/tips/starttip.gif
Binary files differ
diff --git a/org.eclipse.tips.examples/images/tips/tip2.gif b/org.eclipse.tips.examples/images/tips/tip2.gif
new file mode 100644
index 000000000..d3850990f
--- /dev/null
+++ b/org.eclipse.tips.examples/images/tips/tip2.gif
Binary files differ
diff --git a/org.eclipse.tips.examples/images/tips/tipsfw.png b/org.eclipse.tips.examples/images/tips/tipsfw.png
new file mode 100644
index 000000000..8eeb6009f
--- /dev/null
+++ b/org.eclipse.tips.examples/images/tips/tipsfw.png
Binary files differ
diff --git a/org.eclipse.tips.examples/matrixrain/index.html b/org.eclipse.tips.examples/matrixrain/index.html
new file mode 100644
index 000000000..f3d53bd7c
--- /dev/null
+++ b/org.eclipse.tips.examples/matrixrain/index.html
@@ -0,0 +1,26 @@
+<html>
+<head>
+ <meta charset="UTF-8">
+ <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.min.js"></script>
+ <!-- uncomment lines below to include extra p5 libraries -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/addons/p5.dom.min.js"></script>
+ <!--<script language="javascript" src="libraries/p5.sound.js"></script>-->
+ <script type="text/javascript" src="matrixrain.js"></script>
+ <script type="text/javascript" src="particle.js"></script>
+ <!-- this line removes any default padding and style. you might only need one of these values set. -->
+ <style type="text/css">
+ body {
+ margin: 0px;
+ }
+
+ canvas {
+ display: block;
+ }
+
+ </style>
+</head>
+
+<body>
+
+</body>
+</html> \ No newline at end of file
diff --git a/org.eclipse.tips.examples/matrixrain/matrixrain.js b/org.eclipse.tips.examples/matrixrain/matrixrain.js
new file mode 100644
index 000000000..a03ca729c
--- /dev/null
+++ b/org.eclipse.tips.examples/matrixrain/matrixrain.js
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+// Copyright 2017 Wim Jongman
+// var nice = [200, 200, 0.02, 0.002, 10, 2000, 4];
+// var nice = [7280, 4320 , 0.02, 0.002, 10, 2000, 4];
+var nice = [ 1440, 900, 0.02, 0.002, 20, 50, 4 ];
+// var nice = [600, 600, 0.02, 0.002, 50, 1000, 3];
+var myX;
+var myY;
+var inc;
+var incTime;
+var scl;
+var parts;
+var magnitude;
+
+var fr;
+var time = 0;
+var iteration = 0;
+var counter = 0;
+
+var particles = [];
+
+var flowField;
+
+var lanes = new Array();
+
+var pg;
+
+function setup() {
+
+ myX = nice[0];
+ myY = nice[1];
+ inc = nice[2];
+ incTime = nice[3];
+ scl = nice[4];
+ parts = nice[5];
+ magnitude = nice[6];
+
+ createCanvas(window.innerWidth, window.innerHeight);
+ document.body.scrollTop = 0; // <-- pull the page back up to the top
+ document.body.style.overflow = 'hidden'; // <-- relevant addition
+
+ pixelDensity(1);
+ generateParticles();
+}
+
+function draw() {
+ background(0, 120);
+ updateParticles();
+}
+
+function generateParticles() {
+ for (var i = 0; i < parts; i++) {
+ particles[i] = new Particle();
+ }
+}
+
+function updateParticles() {
+ textSize(12);
+ textStyle(BOLD);
+ for (var i = 0; i < parts; i++) {
+ p = particles[i];
+ if (p.isActive()) {
+ p.applyForce();
+ p.update();
+ show(p);
+ }
+ }
+}
+
+function show(particle) {
+ doShow(particle, 0);
+ for (var i = 1; i < particle.fTrail.length; i++) {
+ doShow(particle, i, particle.fTrail.length);
+ }
+}
+
+function doShow(particle, pos, len) {
+ if (pos == 0) {
+ fill(200, 255, 200, 255);
+ } else if ((len - pos) < 4) {
+ fill(255 - (len - pos) * 50, 255, 255 - (len - pos) * 50,
+ 200 - (len - pos) * 20);
+ } else {
+ fill(0, 255, 0, particle.getOpacity(pos));
+ }
+ text(particle.getChar(pos), particle.getX(pos), particle.getY(pos));
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/matrixrain/particle.js b/org.eclipse.tips.examples/matrixrain/particle.js
new file mode 100644
index 000000000..d67254fc1
--- /dev/null
+++ b/org.eclipse.tips.examples/matrixrain/particle.js
@@ -0,0 +1,124 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+// Copyright 2017 Wim Jongman
+function Particle() {
+
+ this.init = function(first) {
+ this.fTrail = new Array();
+ this.fChar = new Array();
+ this.fOpac = new Array();
+ this.scalar = 10;
+ this.fForce = createVector(0, this.scalar);
+ if (first) {
+ this.active = Math.floor(random(50));
+ }
+ this.alive = 0;
+ this.laneNumber = this.getFreeLane();
+ lanes[this.laneNumber] = true;
+ this.theY = 0;
+ if (first) {
+ this.theY = Math.floor(random(height / 2));
+ }
+ this.pos = createVector(this.laneNumber * this.scalar, this.theY);
+ this.vel = createVector(0, 0);
+ this.acc = createVector(0, 0);
+ this.prv = createVector(0, 0);
+ this.speed = Math.floor(random(10) + 1);
+ this.char = this.getRandomChar();
+ this.fTrail[this.fTrail.length] = this.pos;
+ this.fChar[this.fChar.length] = this.char;
+ this.fOpac[this.fOpac.length] = 255;
+ }
+
+ this.getRandomChar = function() {
+ if (Math.round(random()) == 1) {
+ return char(Math.floor(12458 + random(74)));
+ }
+ return char(Math.floor(47 + random(43)));
+ }
+
+ /**
+ * Become active after a while.
+ */
+ this.isActive = function() {
+ if (this.active <= 0) {
+ return true;
+ }
+ this.active--;
+ return false;
+ }
+
+ this.getFreeLane = function() {
+ var numberOfLanes = Math.floor(width / this.scalar);
+ var result = Math.floor(random(numberOfLanes));
+ while (lanes[result] == true) {
+ result = Math.floor(random(numberOfLanes));
+ }
+ return result;
+ }
+
+ this.update = function() {
+ // console.log(this.alive, this.fTrail.length, this.pos.y);
+ if (this.pos.y > height
+ && (this.alive > 0 && (this.alive + 5) >= this.fTrail.length)) {
+ lanes[this.laneNumber] = false;
+ this.init(false);
+ }
+
+ else if (this.pos.y <= height + 20) {
+ var copy = this.pos.copy();
+ this.fTrail[this.fTrail.length] = copy;
+ this.fChar[this.fChar.length] = this.char;
+ this.fOpac[this.fOpac.length] = 200;
+ this.vel.add(this.acc);
+ this.pos.add(this.vel);
+ this.vel.limit(this.speed);
+ this.acc.mult(0);
+ this.char = this.getRandomChar();
+ }
+ }
+
+ this.getOpacity = function(pos) {
+ var count = this.fOpac[pos];
+ this.fOpac[pos] -= this.speed;
+ if (this.fTrail[pos].y > height) {
+ this.fOpac[pos] = 0;// (this.speed * 100);
+ }
+ if (count > 0 && this.fOpac[pos] <= 0) {
+ this.alive += 2;
+ }
+ return this.fOpac[pos];
+ }
+
+ this.getX = function(pos) {
+ return this.fTrail[pos].x;
+ }
+
+ this.getY = function(pos) {
+ return this.fTrail[pos].y;
+ }
+
+ this.getChar = function(pos) {
+ if (Math.floor(random(10)) == 5) {
+ this.fChar[pos] = this.getRandomChar();
+ }
+ if (pos == 0) {
+ return this.getRandomChar();
+ }
+ return this.fChar[pos];
+ }
+
+ this.applyForce = function() {
+ this.acc.add(this.fForce);
+ }
+
+ this.init(true);
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/plugin.xml b/org.eclipse.tips.examples/plugin.xml
new file mode 100644
index 000000000..6c13e2410
--- /dev/null
+++ b/org.eclipse.tips.examples/plugin.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<plugin>
+ <extension
+ point="org.eclipse.tips.core.tips">
+ <category
+ id="org.eclipse.tips.examples.tips.tipsfw"
+ name="Tips Framework">
+ </category>
+ <provider
+ category="org.eclipse.tips.examples.tips.tipsfw"
+ class="org.eclipse.tips.examples.tipsframework.TipsTipProvider"
+ description="Eclipse Tips"
+ id="org.eclipse.tips.examples.tips.tipsframework.TipsTipProvider">
+ </provider>
+ <provider
+ category="org.eclipse.tips.examples.tips.tipsfw"
+ class="org.eclipse.tips.examples.json.JsonTipProviderPhoton"
+ description="Eclipse Photon New and Noteworthy"
+ id="org.eclipse.tips.examples.tips.tipsframework.photonnn">
+ </provider>
+ </extension>
+ <extension
+ point="org.eclipse.tips.core.tips">
+ <category
+ id="org.eclipse.tips.examples.tips.java"
+ name="Java">
+ </category>
+ <category
+ id="org.eclipse.tips.examples.tips.java9"
+ name="Java9"
+ parentCategory="org.eclipse.tips.examples.tips.java9">
+ </category>
+ <provider
+ category="org.eclipse.tips.examples.tips.java9"
+ class="org.eclipse.tips.examples.java.java9.Java9TipProvider"
+ description="Java 9 Tips"
+ id="org.eclipse.tips.examples.tips.java.java9.Java9TipProvider">
+ <enablement>
+ <with
+ variable="activeWorkbenchWindow.activePerspective">
+ <equals
+ value="org.eclipse.jdt.ui.JavaPerspective">
+ </equals>
+ </with>
+ </enablement>
+ </provider>
+ </extension>
+ <extension
+ point="org.eclipse.tips.core.tips">
+ <category
+ id="org.eclipse.tips.examples.tips.eclipsetips"
+ name="Eclipse IDE Tips">
+ </category>
+ <provider
+ category="org.eclipse.tips.examples.tips.eclipsetips"
+ class="org.eclipse.tips.examples.eclipsetips.EclipseTipsProvider"
+ description="Eclipse Tips"
+ id="org.eclipse.tips.examples.tips.eclipsetips.EclipseTipsProvider">
+ </provider>
+ </extension>
+ <extension
+ point="org.eclipse.tips.core.tips">
+ <category
+ id="org.eclipse.tips.examples.tips.eclipsetips"
+ name="Eclipse IDE Tips">
+ </category>
+ <provider
+ category="org.eclipse.tips.examples.tips.eclipsetips"
+ class="org.eclipse.tips.examples.test.TestProvider"
+ description="Test Provider"
+ id="org.eclipse.tips.examples.tips.test.TestProvider">
+ </provider>
+ </extension>
+ <extension
+ point="org.eclipse.tips.core.tips">
+ <provider
+ category="org.eclipse.tips.examples.tips.eclipsetips"
+ class="org.eclipse.tips.examples.swttip.SwtTipsProvider"
+ description="SWT Tips Provider"
+ id="org.eclipse.tips.examples.swttip.SWTTipsProvider">
+ </provider>
+ </extension>
+
+</plugin>
diff --git a/org.eclipse.tips.examples/pom.xml b/org.eclipse.tips.examples/pom.xml
new file mode 100644
index 000000000..f18f8274c
--- /dev/null
+++ b/org.eclipse.tips.examples/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.examples</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/DateUtil.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/DateUtil.java
new file mode 100644
index 000000000..2d5226e7a
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/DateUtil.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Date utilities.
+ *
+ */
+public class DateUtil {
+
+ /**
+ * Convenience method that creates a date from a dd/mm/yy string.
+ *
+ * @param pYYMMDD
+ * the date in a dd/mm/yy format, e.g. "01/01/2017"
+ * @return the date
+ * @throws RuntimeException
+ * if the date is not correct
+ */
+ public static Date getDateFromYYMMDD(String pYYMMDD) {
+ SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+ try {
+ return sdf.parse(pYYMMDD);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/EclipseTipsProvider.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/EclipseTipsProvider.java
new file mode 100644
index 000000000..c9fdbbacc
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/EclipseTipsProvider.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.eclipsetips;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.eclipse.tips.examples.tips.MediaWikiTip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class EclipseTipsProvider extends org.eclipse.tips.core.TipProvider {
+
+ public int fCurrentTip;
+
+ private Tip createTip1() {
+ return new org.eclipse.tips.examples.tips.TwitterTip(getID(), "https://twitter.com/EclipseJavaIDE/status/919915440041840641", DateUtil.getDateFromYYMMDD("08/12/017"),
+ "Twitter Tip");
+ }
+
+ private Tip createTip2() {
+ return new org.eclipse.tips.examples.tips.MediaWikiTip(getID(), "https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Show_In_System_Explorer",
+ DateUtil.getDateFromYYMMDD("08/01/2017"), "Show in Systems Explorer");
+ }
+
+ private MediaWikiTip createTip3() {
+ return new org.eclipse.tips.examples.tips.MediaWikiTip(getID(),
+ "https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Now_where_was_I",
+ DateUtil.getDateFromYYMMDD("08/01/2017"), "Where was I?");
+ }
+
+ private Tip createTip4() {
+ return new org.eclipse.tips.examples.tips.MediaWikiTip(getID(),
+ "https://twitter.com/EclipseJavaIDE/status/949238007051235328",
+ DateUtil.getDateFromYYMMDD("08/01/2017"), "Extract class");
+ }
+
+ private MediaWikiTip createTip5() {
+ return new org.eclipse.tips.examples.tips.MediaWikiTip(getID(),
+ "https://twitter.com/EclipseJavaIDE/status/919915440041840641",
+ DateUtil.getDateFromYYMMDD("08/01/2017"), "Junit Jupiter Test");
+ }
+
+ @Override
+ public String getDescription() {
+ return "General Eclipse IDE Tips";
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ private TipImage fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/eclipse.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Tip2(getID()));
+ tips.add(new Tip3(getID()));
+ tips.add(createTip1());
+ tips.add(createTip2());
+ tips.add(createTip3());
+ tips.add(createTip4());
+ tips.add(createTip5());
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip1.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip1.java
new file mode 100644
index 000000000..86903b0de
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip1.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.eclipsetips;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class Tip1 extends Tip implements IHtmlTip {
+
+ public Tip1(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "This is SwtTipImpl";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h1>Iterate with Iterator</h1>" //
+ + "Workbench editors keep a navigation history." //
+ + " If you open a second editor while you're editing,"
+ + " you can press <b>Navigate > Backward (Alt+Left Arrow,"
+ + " or the Left arrow icon back arrow icon on the"
+ + " workbench toolbar)</b> to go back to the last editor."
+ + " This makes working with several open editors a whole lot easier." + "<br/><br/>";
+ }
+
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("images/eclipsetips/tip1.gif")).setAspectRatio(560.0 / 266.0);
+ } catch (IOException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip2.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip2.java
new file mode 100644
index 000000000..b6bd14322
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip2.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.eclipsetips;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class Tip2 extends Tip implements IHtmlTip {
+
+
+ public Tip2(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Quick Access";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h1>Quick access</h1>You can quickly find all manner of"
+ + " user interface elements with the <b>Quick Access</b>"
+ + " search bar at the top of the workbench window. Click"
+ + " in the field or use the <b>Ctrl+3</b> binding to switch"
+ + " focus to it. Matching elements include (but are not limited to)"
+ + " open editors, available perspectives, views, preferences, wizards,"
+ + " and commands. Simply start typing the name of the item you wish to"
+ + " invoke and we will attempt to find something in the Workbench that"
+ + " matches the provided string. ";
+ }
+
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("images/eclipsetips/tip2.png"));
+ } catch (IOException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip3.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip3.java
new file mode 100644
index 000000000..c67d4403f
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/Tip3.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.eclipsetips;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class Tip3 extends Tip implements IHtmlTip {
+
+
+ public Tip3(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Auto Save";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h1>Automatic Save of dirty editors</h1>You can configure the automatic save of dirty editors in Eclipse via the <b>General > Editors > Autosave</b> preference page which allows you to enable/disable the autosave and change the interval of autosave.";
+ }
+
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("images/eclipsetips/tip3.png")).setAspectRatio(658.0 / 581.0);
+ } catch (IOException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/TwitterTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/TwitterTip.java
new file mode 100644
index 000000000..afe11e766
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/eclipsetips/TwitterTip.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.eclipsetips;
+
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.examples.DateUtil;
+
+public class TwitterTip extends Tip implements IHtmlTip {
+
+ public TwitterTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "CTRL+1 Quick Assists";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<html><head><style>"
+ + "</style></head><body><div><blockquote class=\"twitter-tweet\" data-lang=\"en\"><p lang=\"en\" dir=\"ltr\">The &#39;Extract class...&#39; refactoring (from Alt+Shift+T) extracts a group of fields into a separate class and replaces all occurrences to fit the new structure. See example. <a href=\"https://twitter.com/hashtag/EclipseTips?src=hash&amp;ref_src=twsrc%5Etfw\">#EclipseTips</a> <a href=\"https://t.co/tEI7ic7C1g\">pic.twitter.com/tEI7ic7C1g</a></p>&mdash; Eclipse Java IDE (@EclipseJavaIDE) <a href=\"https://twitter.com/EclipseJavaIDE/status/949238007051235328?ref_src=twsrc%5Etfw\">January 5, 2018</a></blockquote>"
+ + "<script src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script></div></body></html>";
+ }
+
+ @Override
+ public TipImage getImage() {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Java9TipProvider.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Java9TipProvider.java
new file mode 100644
index 000000000..16e532921
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Java9TipProvider.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.java.java9;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ *
+
+ *
+ */
+public class Java9TipProvider extends TipProvider {
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("icons/48/java.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Java and Java Dev Tools Tips";
+ }
+
+ @Override
+ public void dispose() {
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Tip1.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Tip1.java
new file mode 100644
index 000000000..5d9045522
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/java/java9/Tip1.java
@@ -0,0 +1,109 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.java.java9;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipAction;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * This is an example Tip class.
+ *
+ */
+public class Tip1 extends Tip implements IHtmlTip {
+
+ /**
+ * Tips should be created fast.
+ *
+ * @param pProvider
+ * the associated {@link TipProvider}
+ */
+ public Tip1(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public List<TipAction> getActions() {
+ Runnable runnable = () -> Display.getDefault()
+ .syncExec(() -> MessageDialog.openConfirm(null, getSubject(), "We can start an action from a Tip!"));
+
+ ArrayList<TipAction> actions = new ArrayList<>();
+ actions.add(new TipAction("Tip1 Action", "Just a silly action", runnable, null));
+ return actions;
+ }
+
+ /**
+ * Return the publish date of the tip. The {@link TipProvider} could decide to
+ * server newer tips first.
+ *
+ * @return the date this tip was published which may not be null.
+ */
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ /**
+ * @return the subject which may not be null.
+ */
+ @Override
+ public String getSubject() {
+ return "Java tip 1";
+ }
+
+ /**
+ * This method may both return the string representation of an URL or the
+ * descriptive HTML of the tip. If the html of the text is returned then an
+ * effort is made to also inline the image URL. If you supply an URL then
+ * {@link #getImage()} should return null.
+ *
+ * @return the HMTL or URL of the tip which is displayed in a browser widget.
+ * @see #getImage()
+ */
+ @Override
+ public String getHTML() {
+ return "<h2>Javatip 1</h2>You see this tip because the Tip UI was opened when the java perspective "
+ + "was active or because you selected the Java icon below." + "<br><br>"
+ + "More java tips will be displayed here in the near future. For now, select one of the other providers"
+ + " by clicking on the icons below.";
+ }
+
+ /**
+ * A getter for the {@link TipImage}. Subclasses may override, the default
+ * implementation returns null.
+ *
+ * @return a TipImage with information about the image or null if no image is
+ * provided.
+ */
+ @Override
+ public TipImage getImage() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry("images/java/duke.png")).setAspectRatio(1);
+ } catch (IOException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/json/JsonTipProviderPhoton.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/json/JsonTipProviderPhoton.java
new file mode 100644
index 000000000..d77193c1a
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/json/JsonTipProviderPhoton.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.json;
+
+import java.net.MalformedURLException;
+
+import org.eclipse.tips.json.JsonTipProvider;
+
+public class JsonTipProviderPhoton extends JsonTipProvider {
+
+ public JsonTipProviderPhoton() throws MalformedURLException {
+ setJsonUrl("https://raw.githubusercontent.com/wimjongman/jsontips/master/photon/m3tips.json");
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipImpl.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipImpl.java
new file mode 100644
index 000000000..faeb61c3b
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipImpl.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.swttip;
+
+import java.util.Date;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.examples.DateUtil;
+import org.eclipse.tips.ui.ISwtTip;
+
+public class SwtTipImpl extends Tip implements ISwtTip {
+
+ private final class Beeper extends SelectionAdapter {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ e.widget.getDisplay().beep();
+ if (((Button) e.widget).getText().contains("2")) {
+ try {
+ Thread.sleep(600);
+ } catch (InterruptedException e1) {
+ }
+ e.widget.getDisplay().beep();
+ }
+ }
+ }
+
+ private String fSubject;
+
+ public SwtTipImpl(String providerId, int number) {
+ super(providerId);
+ fSubject = "This is a tip " + number;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("10/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return fSubject;
+ }
+
+ @Override
+ public void createControl(Composite pParent) {
+ pParent.setLayout(new GridLayout(2, false));
+ Group group = new Group(pParent, SWT.NONE);
+ GridDataFactory.fillDefaults().span(2, SWT.DEFAULT).grab(true, true).applyTo(group);
+ group.setLayout(new FillLayout());
+ group.setText(fSubject);
+ Text text = new Text(group, SWT.MULTI | SWT.WRAP);
+ text.setText(fSubject + System.lineSeparator() + System.lineSeparator()
+ + "There are thousands of these tips. Just press next tip and you will see.");
+ Button button1 = new Button(pParent, SWT.PUSH);
+ button1.setText("Beep once");
+ button1.addSelectionListener(new Beeper());
+ GridDataFactory.fillDefaults().applyTo(button1);
+ Button button2 = new Button(pParent, SWT.PUSH);
+ button2.setText("Beep 2 times");
+ button2.addSelectionListener(new Beeper());
+ GridDataFactory.fillDefaults().applyTo(button2);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipsProvider.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipsProvider.java
new file mode 100644
index 000000000..5424fdd68
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/swttip/SwtTipsProvider.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.swttip;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class SwtTipsProvider extends TipProvider {
+
+ private TipImage fImage64, fImage48;
+ private int fCounter;
+ private boolean fFetching;
+
+ @Override
+ public String getDescription() {
+ return "Never ending list of SWT Tips";
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public synchronized List<Tip> getTips(boolean pFilter) {
+ List<Tip> tips = super.getTips(pFilter);
+ if (tips.size() <= 1) {
+ Job job = new Job(getDescription() + " is getting more tips.") {
+
+ @Override
+ protected IStatus run(IProgressMonitor pMonitor) {
+ return loadNewTips(pMonitor);
+ }
+ };
+ job.setUser(true);
+ job.schedule();
+ }
+ return tips;
+ }
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/swt.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ if (fFetching) {
+ return Status.CANCEL_STATUS;
+ }
+ try {
+ subMonitor.beginTask("Loading Tips for " + getDescription(), -1);
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new SwtTipImpl(getID(),1));
+ tips.add(new SwtTipImpl(getID(),2));
+ tips.add(new SwtTipImpl(getID(),3));
+ tips.add(new SwtTipImpl(getID(),4));
+ tips.add(new SwtTipImpl(getID(),5));
+ addTips(tips);
+ return Status.OK_STATUS;
+ } finally {
+ fFetching = false;
+ subMonitor.done();
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ public int getCounter() {
+ return ++fCounter;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider.java
new file mode 100644
index 000000000..6036ace4d
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/c++.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+
+ if (System.getProperty("tips.test") == null) {
+ return Status.OK_STATUS;
+ }
+
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ getManager().register(new TestProvider1());
+ getManager().register(new TestProvider2());
+ getManager().register(new TestProvider3());
+ getManager().register(new TestProvider4());
+ getManager().register(new TestProvider5());
+ getManager().register(new TestProvider6());
+ getManager().register(new TestProvider7());
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider1.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider1.java
new file mode 100644
index 000000000..5e900cd70
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider1.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider1 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/key.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider2.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider2.java
new file mode 100644
index 000000000..3bd6b11b2
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider2.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider2 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/key.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider3.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider3.java
new file mode 100644
index 000000000..18ee0bf31
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider3.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider3 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/ecf.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider4.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider4.java
new file mode 100644
index 000000000..d896afae1
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider4.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider4 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/pin.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider5.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider5.java
new file mode 100644
index 000000000..cff85162c
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider5.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider5 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/plugin.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ int sleep = new Random().nextInt(10000) + 1000;
+ while (sleep > 0) {
+ try {
+ Thread.sleep(500);
+ sleep -= 500;
+ subMonitor.subTask("Sleeping " + sleep);
+ } catch (InterruptedException e) {
+ }
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider6.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider6.java
new file mode 100644
index 000000000..6626d1b59
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider6.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider6 extends org.eclipse.tips.core.TipProvider {
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/pydev.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider7.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider7.java
new file mode 100644
index 000000000..f3fe1fdee
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/test/TestProvider7.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.java.java9.Tip1;
+import org.eclipse.tips.examples.tipsframework.Navigate2Tip;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TestProvider7 extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/twitter.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ }
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new Tip1(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test Provider " + getClass().getSimpleName();
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/MediaWikiTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/MediaWikiTip.java
new file mode 100644
index 000000000..b43867fe0
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/MediaWikiTip.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tips;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.tips.core.IUrlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+
+/**
+ * Specialisation of Tip that receives an URL of a mediawiki page (e.g. Eclipse
+ * wiki) and renders this URL as a tip.
+ * <p>
+ * For an example see:
+ *
+ * <a href=
+ * "https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Now_where_was_I">https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Now_where_was_I</a>
+ *
+ */
+public class MediaWikiTip extends Tip implements IUrlTip {
+
+ private String fPageUrl;
+ private Date fCreationDate;
+ private String fSubject;
+
+ /**
+ * Constructor.
+ *
+ * <p>
+ * For an example see:
+ *
+ * <a href=
+ * "https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Now_where_was_I">https://wiki.eclipse.org/Tip_of_the_Day/Eclipse_Tips/Now_where_was_I</a>
+ * </p>
+ *
+ * @param pProvider
+ * the {@link TipProvider} that created this Tip
+ * @param pPageUrl
+ * the Eclipse wiki page containing a simple tip
+ * @param pCreationDate
+ * creation date
+ * @param pSubject
+ * the tips' subject
+ */
+ public MediaWikiTip(String providerId, String pPageUrl, Date pCreationDate, String pSubject) {
+ super(providerId);
+ fPageUrl = pPageUrl;
+ fCreationDate = pCreationDate;
+ fSubject = pSubject;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return fCreationDate;
+ }
+
+ @Override
+ public String getSubject() {
+ return fSubject;
+ }
+
+ @Override
+ public URL getURL() {
+ try {
+ return new URL(fPageUrl.trim() + "?action=render");
+ } catch (MalformedURLException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/TwitterTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/TwitterTip.java
new file mode 100644
index 000000000..0d1e2cbcc
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/TwitterTip.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tips;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.tips.core.IUrlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+
+/**
+ * Specialisation of Tip that receives an URL of a tweet in the constructor. The
+ * URL points directly to the tweet:
+ * <p>
+ * For an example see: <a href=
+ * "https://twitter.com/EclipseJavaIDE/status/919915440041840641">https://twitter.com/EclipseJavaIDE/status/919915440041840641</a>
+ * </p>
+ */
+public class TwitterTip extends Tip implements IUrlTip {
+
+ private String fPageUrl;
+ private Date fCreationDate;
+ private String fSubject;
+
+ /**
+ * Constructor.
+ *
+ * <p>
+ * For an example see: <a href=
+ * "https://twitter.com/EclipseJavaIDE/status/919915440041840641">https://twitter.com/EclipseJavaIDE/status/919915440041840641</a>
+ * </p>
+ *
+ * @param pProvider
+ * the {@link TipProvider} that created this Tip
+ * @param pTweetUrl
+ * the URL of the tweet
+ * @param pCreationDate
+ * creation date
+ * @param pSubject
+ * the tips' subject
+ */
+ public TwitterTip(String providerId, String pTweetUrl, Date pCreationDate, String pSubject) {
+ super(providerId);
+ fPageUrl = pTweetUrl;
+ fCreationDate = pCreationDate;
+ fSubject = pSubject;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return fCreationDate;
+ }
+
+ @Override
+ public String getSubject() {
+ return fSubject;
+ }
+
+ @Override
+ public URL getURL() {
+ try {
+ return new URL(fPageUrl);
+ } catch (MalformedURLException e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/package-info.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/package-info.java
new file mode 100644
index 000000000..77276a13c
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tips/package-info.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Contains some example tip implementations,
+ *
+ */
+package org.eclipse.tips.examples.tips; \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/GithubTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/GithubTip.java
new file mode 100644
index 000000000..cad686913
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/GithubTip.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.awt.Desktop;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipAction;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+
+public class GithubTip extends Tip implements IHtmlTip {
+
+ public GithubTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public List<TipAction> getActions() {
+ Runnable runner = () -> Display.getDefault().asyncExec(() -> {
+ if (Platform.isRunning() && Platform.getWS().startsWith("gtk")) {
+ MessageDialog.openInformation(null, "Action", "Can't open a browser in GTK. It crashes the JVM.");
+ } else {
+ Desktop d = Desktop.getDesktop();
+ try {
+ d.browse(new URI("https://github.com/wimjongman/tips"));
+ } catch (IOException | URISyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ ArrayList<TipAction> actions = new ArrayList<>();
+ actions.add(new TipAction("Show in Github", "Opens a browser.", runner, null));
+ return actions;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "On GitHub";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h2>Incubating on GitHub</h2>We are incubating this project on Github "
+ + "and we could use your help. Press the <b>More...</b> button to open the " + "GitHub repository. "
+ + "<br>" + "<br>" + "We are looking forward to your pull requests." + "<br>";
+ }
+
+ private TipImage fImage;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage == null) {
+ try {
+ fImage = new TipImage(new URL("https://assets-cdn.github.com/images/modules/logos_page/Octocat.png"))
+ .setAspectRatio(800, 665, true);
+ } catch (Exception e) {
+// getProvider().getManager().log(LogUtil.error(getClass(), e));
+ }
+ }
+ return fImage;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/MatrixRainTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/MatrixRainTip.java
new file mode 100644
index 000000000..c672a1bec
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/MatrixRainTip.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.net.URL;
+import java.util.Date;
+
+import org.eclipse.tips.core.IUrlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class MatrixRainTip extends Tip implements IUrlTip {
+
+ public MatrixRainTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Welcome to the Matrix";
+ }
+
+ @Override
+ public URL getURL() {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ return bundle.getEntry("matrixrain/index.html");
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate1Tip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate1Tip.java
new file mode 100644
index 000000000..e39a02e2f
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate1Tip.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipAction;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class Navigate1Tip extends Tip implements IHtmlTip {
+
+ private TipImage fImage;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage == null) {
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ fImage = new TipImage(bundle.getEntry("images/tips/navigate1.png")).setAspectRatio(600, 200, true);
+ } catch (Exception e) {
+// getProvider().getManager().log(LogUtil.info(getClass(), e));
+ }
+ }
+ return fImage;
+ }
+
+ public Navigate1Tip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public List<TipAction> getActions() {
+ Runnable runnable = () -> Display.getDefault()
+ .syncExec(() -> MessageDialog.openConfirm(null, getSubject(), "We can do anything we want."));
+ Runnable clock = () -> Display.getDefault().syncExec(() -> MessageDialog.openConfirm(null, getSubject(),
+ DateFormat.getTimeInstance().format(Calendar.getInstance().getTime())));
+ Runnable runner2 = () -> Display.getDefault()
+ .syncExec(() -> MessageDialog.openConfirm(null, getSubject(), "Like open preferences..."));
+ ArrayList<TipAction> actions = new ArrayList<>();
+ actions.add(new TipAction("Clock", "Some sort of clock action", clock, getImage("icons/clock.png")));
+ actions.add(
+ new TipAction("Open Preferences", "Opens the preferences", runner2, getImage("icons/bug_link.png")));
+ actions.add(
+ new TipAction("Cut or Paste", "Just another silly action", runnable, getImage("icons/lightbulb.png")));
+ actions.add(new TipAction("Eclipse Rocks, Idea Scissors", "Paper", runnable, getImage("icons/cut.png")));
+ actions.add(new TipAction("Totally Bonkers", "The quick brown fox", runnable, getImage("icons/notfound.png")));
+ return actions;
+ }
+
+ private TipImage getImage(String pIcon) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ return new TipImage(bundle.getEntry(pIcon)).setAspectRatio(1);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Navigate Tip 1";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h2>Navigating Tips</h2>You can navigate tips by using the button bar. "
+ + "<br><b>Next Tip</b><br>Navigates to the next tip."
+ + "<br><b>Previous Tip</b></br>Navigates to the previous tip."
+ + "<br><b>Close</b></br>Closes the Dialog (<b>Escape</b> does the same)."
+ + "<br><b>Show tips at startup</b><br/>A toggle to show this dialog when you start Eclipse."
+ + "<br><br>"
+ + "If a tip can do something special then the <b>More...</b> button is activated, like with this tip."
+ + "<br>Go on, press it!";
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate2Tip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate2Tip.java
new file mode 100644
index 000000000..e997e5102
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/Navigate2Tip.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class Navigate2Tip extends Tip implements IHtmlTip {
+
+ private TipImage fImage;
+
+ public Navigate2Tip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Navigate Tip 2";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h2>Navigating Tips</h2>You can activate other Tip Providers by clicking on the big icons below."
+ + "<br>"
+ + "You are currently looking at the Tips tips but as you can see there are other providers. Go ahead and"
+ + " select some of the other providers. If you click on the lightbulb below you will return to this tip.<br><br>";
+ }
+
+ @Override
+ public TipImage getImage() {
+ if (fImage == null) {
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ fImage = new TipImage(bundle.getEntry("images/tips/navigate2.png")).setAspectRatio(650, 220, true);
+ } catch (Exception e) {
+// getProvider().getManager().log(LogUtil.info(getClass(), e));
+ }
+ }
+ return fImage;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/StartingTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/StartingTip.java
new file mode 100644
index 000000000..f55cdacd5
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/StartingTip.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.examples.DateUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class StartingTip extends Tip implements IHtmlTip {
+
+ public StartingTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Opening the Tips Dialog";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h2>Opening the Tips Dialog</h2>The tips are started automatically at startup but you can switch this off."
+ + " In case the tips are not loaded at startup you can active the tips manually from the Help menu."
+ + "<br><br>" + "Press <b><i>Next Tip</i></b> to see how to navigate Tips.<br><br>";
+ }
+
+ private TipImage fImage;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage == null) {
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ fImage = new TipImage(bundle.getEntry("images/tips/starttip.gif")).setAspectRatio(780, 430, true);
+ } catch (Exception e) {
+// getProvider().getManager().log(LogUtil.info(getClass(), e));
+ }
+ }
+ return fImage;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/TipsTipProvider.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/TipsTipProvider.java
new file mode 100644
index 000000000..ff6a1299f
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/TipsTipProvider.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+public class TipsTipProvider extends org.eclipse.tips.core.TipProvider {
+
+ private TipImage fImage64, fImage48;
+
+ @Override
+ public TipImage getImage() {
+ if (fImage48 == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ try {
+ fImage48 = new TipImage(bundle.getEntry("icons/48/tips.png")).setAspectRatio(1);
+ } catch (IOException e) {
+ getManager().log(LogUtil.info(getClass(), e));
+ }
+ }
+ return fImage48;
+ }
+
+ @Override
+ public synchronized IStatus loadNewTips(IProgressMonitor pMonitor) {
+ SubMonitor subMonitor = SubMonitor.convert(pMonitor);
+ subMonitor.beginTask("Loading Tips", -1);
+ List<Tip> tips = new ArrayList<>();
+ tips.add(new WelcomeTip(getID()));
+ tips.add(new StartingTip(getID()));
+ tips.add(new Navigate1Tip(getID()));
+ tips.add(new Navigate2Tip(getID()));
+ tips.add(new GithubTip(getID()));
+ tips.add(new MatrixRainTip(getID()));
+ setTips(tips);
+ subMonitor.done();
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Tips about Tips";
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/WelcomeTip.java b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/WelcomeTip.java
new file mode 100644
index 000000000..f07436127
--- /dev/null
+++ b/org.eclipse.tips.examples/src/org/eclipse/tips/examples/tipsframework/WelcomeTip.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.examples.tipsframework;
+
+import java.util.Date;
+
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.examples.DateUtil;
+
+public class WelcomeTip extends Tip implements IHtmlTip {
+
+ public WelcomeTip(String providerId) {
+ super(providerId);
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("09/01/2018");
+ }
+
+ @Override
+ public String getSubject() {
+ return "Welcome to the tips framework";
+ }
+
+ @Override
+ public String getHTML() {
+ return "<h2>Welcome to the Tips Framework</h2>It can show tips from various tip providers. This provider has tips about tips which will show you how to navigate this UI."
+ + " The dialog is this Tip UI. Tips appear here in various forms. They can come from Twitter, a Wiki, a Website, a file or even from Java, like this one."
+ + "<br><br>" + "Press <b><i>Next Tip</i></b> to see how to start tips manually.";
+ }
+
+ @Override
+ public TipImage getImage() {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.feature/.gitignore b/org.eclipse.tips.feature/.gitignore
new file mode 100644
index 000000000..b83d22266
--- /dev/null
+++ b/org.eclipse.tips.feature/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/org.eclipse.tips.feature/.project b/org.eclipse.tips.feature/.project
new file mode 100644
index 000000000..a59eaefd9
--- /dev/null
+++ b/org.eclipse.tips.feature/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.feature</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.pde.FeatureBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.FeatureNature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.feature/build.properties b/org.eclipse.tips.feature/build.properties
new file mode 100644
index 000000000..96dd6d0f0
--- /dev/null
+++ b/org.eclipse.tips.feature/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+bin.includes = feature.xml,\
+ epl-v10.html,\
+ feature.properties,\
+ license.html
diff --git a/org.eclipse.tips.feature/feature.properties b/org.eclipse.tips.feature/feature.properties
new file mode 100644
index 000000000..6b58e96f8
--- /dev/null
+++ b/org.eclipse.tips.feature/feature.properties
@@ -0,0 +1,156 @@
+###############################################################################
+# Copyright (c) 2018, Wim Jongman Remain Software.
+# 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
+###############################################################################
+
+featureName=Tip of the Day UI Feature
+providerName=Remain Software
+
+updateSiteName=Eclipse Update Site
+
+# description property - text of the "Feature Description"
+description=Contains the Eclipse Tips framework.
+################ end of description property ##################################
+
+# "copyright" property - text of the "Feature Update Copyright"
+copyright=\
+Copyright (c) 2018 Wim Jongman, Remain Software\n\
+All rights reserved. This program and the accompanying materials\n\
+are made available under the terms of the Eclipse Public License v1.0\n\
+which accompanies this distribution, and is available at\n\
+http://www.eclipse.org/legal/epl-v10.html\n
+################ end of copyright property ####################################
+
+# "licenseURL" property - URL of the "Feature License"
+# do not translate value - just change to point to a locale-specific HTML page
+licenseURL=license.html
+
+# "license" property - text of the "Feature Update License"
+# should be plain text version of license agreement pointed to be "licenseURL"
+license=\
+Eclipse Foundation Software User Agreement\n\
+April 9, 2014\n\
+\n\
+Usage Of Content\n\
+\n\
+THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR\n\
+OTHER MATERIALS FOR OPEN SOURCE PROJECTS (COLLECTIVELY "CONTENT").\n\
+USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS\n\
+AGREEMENT AND/OR THE TERMS AND CONDITIONS OF LICENSE AGREEMENTS OR\n\
+NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU\n\
+AGREE THAT YOUR USE OF THE CONTENT IS GOVERNED BY THIS AGREEMENT\n\
+AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS\n\
+OR NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE\n\
+TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND CONDITIONS\n\
+OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED\n\
+BELOW, THEN YOU MAY NOT USE THE CONTENT.\n\
+\n\
+Applicable Licenses\n\
+\n\
+Unless otherwise indicated, all Content made available by the\n\
+Eclipse Foundation is provided to you under the terms and conditions of\n\
+the Eclipse Public License Version 1.0 ("EPL"). A copy of the EPL is\n\
+provided with this Content and is also available at http://www.eclipse.org/legal/epl-v10.html.\n\
+For purposes of the EPL, "Program" will mean the Content.\n\
+\n\
+Content includes, but is not limited to, source code, object code,\n\
+documentation and other files maintained in the Eclipse Foundation source code\n\
+repository ("Repository") in software modules ("Modules") and made available\n\
+as downloadable archives ("Downloads").\n\
+\n\
+\t- Content may be structured and packaged into modules to facilitate delivering,\n\
+\t extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"),\n\
+\t plug-in fragments ("Fragments"), and features ("Features").\n\
+\t- Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java(TM) ARchive)\n\
+\t in a directory named "plugins".\n\
+\t- A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material.\n\
+\t Each Feature may be packaged as a sub-directory in a directory named "features".\n\
+\t Within a Feature, files named "feature.xml" may contain a list of the names and version\n\
+\t numbers of the Plug-ins and/or Fragments associated with that Feature.\n\
+\t- Features may also include other Features ("Included Features"). Within a Feature, files\n\
+\t named "feature.xml" may contain a list of the names and version numbers of Included Features.\n\
+\n\
+The terms and conditions governing Plug-ins and Fragments should be\n\
+contained in files named "about.html" ("Abouts"). The terms and\n\
+conditions governing Features and Included Features should be contained\n\
+in files named "license.html" ("Feature Licenses"). Abouts and Feature\n\
+Licenses may be located in any directory of a Download or Module\n\
+including, but not limited to the following locations:\n\
+\n\
+\t- The top-level (root) directory\n\
+\t- Plug-in and Fragment directories\n\
+\t- Inside Plug-ins and Fragments packaged as JARs\n\
+\t- Sub-directories of the directory named "src" of certain Plug-ins\n\
+\t- Feature directories\n\
+\n\
+Note: if a Feature made available by the Eclipse Foundation is installed using the\n\
+Provisioning Technology (as defined below), you must agree to a license ("Feature \n\
+Update License") during the installation process. If the Feature contains\n\
+Included Features, the Feature Update License should either provide you\n\
+with the terms and conditions governing the Included Features or inform\n\
+you where you can locate them. Feature Update Licenses may be found in\n\
+the "license" property of files named "feature.properties" found within a Feature.\n\
+Such Abouts, Feature Licenses, and Feature Update Licenses contain the\n\
+terms and conditions (or references to such terms and conditions) that\n\
+govern your use of the associated Content in that directory.\n\
+\n\
+THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER\n\
+TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS.\n\
+SOME OF THESE OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):\n\
+\n\
+\t- Eclipse Distribution License Version 1.0 (available at http://www.eclipse.org/licenses/edl-v1.0.html)\n\
+\t- Common Public License Version 1.0 (available at http://www.eclipse.org/legal/cpl-v10.html)\n\
+\t- Apache Software License 1.1 (available at http://www.apache.org/licenses/LICENSE)\n\
+\t- Apache Software License 2.0 (available at http://www.apache.org/licenses/LICENSE-2.0)\n\
+\t- Mozilla Public License Version 1.1 (available at http://www.mozilla.org/MPL/MPL-1.1.html)\n\
+\n\
+IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR\n\
+TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License\n\
+is provided, please contact the Eclipse Foundation to determine what terms and conditions\n\
+govern that particular Content.\n\
+\n\
+\n\Use of Provisioning Technology\n\
+\n\
+The Eclipse Foundation makes available provisioning software, examples of which include,\n\
+but are not limited to, p2 and the Eclipse Update Manager ("Provisioning Technology") for\n\
+the purpose of allowing users to install software, documentation, information and/or\n\
+other materials (collectively "Installable Software"). This capability is provided with\n\
+the intent of allowing such users to install, extend and update Eclipse-based products.\n\
+Information about packaging Installable Software is available at\n\
+http://eclipse.org/equinox/p2/repository_packaging.html ("Specification").\n\
+\n\
+You may use Provisioning Technology to allow other parties to install Installable Software.\n\
+You shall be responsible for enabling the applicable license agreements relating to the\n\
+Installable Software to be presented to, and accepted by, the users of the Provisioning Technology\n\
+in accordance with the Specification. By using Provisioning Technology in such a manner and\n\
+making it available in accordance with the Specification, you further acknowledge your\n\
+agreement to, and the acquisition of all necessary rights to permit the following:\n\
+\n\
+\t1. A series of actions may occur ("Provisioning Process") in which a user may execute\n\
+\t the Provisioning Technology on a machine ("Target Machine") with the intent of installing,\n\
+\t extending or updating the functionality of an Eclipse-based product.\n\
+\t2. During the Provisioning Process, the Provisioning Technology may cause third party\n\
+\t Installable Software or a portion thereof to be accessed and copied to the Target Machine.\n\
+\t3. Pursuant to the Specification, you will provide to the user the terms and conditions that\n\
+\t govern the use of the Installable Software ("Installable Software Agreement") and such\n\
+\t Installable Software Agreement shall be accessed from the Target Machine in accordance\n\
+\t with the Specification. Such Installable Software Agreement must inform the user of the\n\
+\t terms and conditions that govern the Installable Software and must solicit acceptance by\n\
+\t the end user in the manner prescribed in such Installable Software Agreement. Upon such\n\
+\t indication of agreement by the user, the provisioning Technology will complete installation\n\
+\t of the Installable Software.\n\
+\n\
+Cryptography\n\
+\n\
+Content may contain encryption software. The country in which you are\n\
+currently may have restrictions on the import, possession, and use,\n\
+and/or re-export to another country, of encryption software. BEFORE\n\
+using any encryption software, please check the country's laws,\n\
+regulations and policies concerning the import, possession, or use, and\n\
+re-export of encryption software, to see if this is permitted.\n\
+\n\
+Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.\n
+########### end of license property ##########################################
diff --git a/org.eclipse.tips.feature/feature.xml b/org.eclipse.tips.feature/feature.xml
new file mode 100644
index 000000000..b1f0efc37
--- /dev/null
+++ b/org.eclipse.tips.feature/feature.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+<feature
+ id="org.eclipse.tips.feature"
+ label="%featureName"
+ version="0.1.0.qualifier"
+ provider-name="%providerName"
+ license-feature="org.eclipse.license"
+ license-feature-version="0.0.0">
+
+ <description>
+ %description
+ </description>
+
+ <copyright>
+ %copyright
+ </copyright>
+
+ <license url="%licenseURL">
+ %license
+ </license>
+
+ <plugin
+ id="org.eclipse.tips.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.tips.core"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="org.eclipse.tips.json"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/org.eclipse.tips.feature/pom.xml b/org.eclipse.tips.feature/pom.xml
new file mode 100644
index 000000000..3ce586521
--- /dev/null
+++ b/org.eclipse.tips.feature/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ <relativePath>../../</relativePath>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.feature</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/org.eclipse.tips.ide/.classpath b/org.eclipse.tips.ide/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.eclipse.tips.ide/.classpath
@@ -0,0 +1,7 @@
+<?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"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.tips.ide/.gitignore b/org.eclipse.tips.ide/.gitignore
new file mode 100644
index 000000000..09e3bc9b2
--- /dev/null
+++ b/org.eclipse.tips.ide/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/org.eclipse.tips.ide/.project b/org.eclipse.tips.ide/.project
new file mode 100644
index 000000000..c1911190d
--- /dev/null
+++ b/org.eclipse.tips.ide/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.ide</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>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.ide/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tips.ide/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/org.eclipse.tips.ide/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.tips.ide/META-INF/MANIFEST.MF b/org.eclipse.tips.ide/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..35e3add48
--- /dev/null
+++ b/org.eclipse.tips.ide/META-INF/MANIFEST.MF
@@ -0,0 +1,14 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: IDE Enablement for Tip of the Day
+Bundle-SymbolicName: org.eclipse.tips.ide;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.ui;bundle-version="3.109.0",
+ org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.eclipse.core.expressions;bundle-version="3.6.0",
+ org.eclipse.e4.ui.workbench;bundle-version="1.5.1",
+ org.eclipse.tips.core;bundle-version="0.1.0",
+ org.eclipse.tips.ui;bundle-version="0.1.0"
+Export-Package: org.eclipse.tips.ide.internal;x-internal:=true
+Automatic-Module-Name: org.eclipse.tips.ide
diff --git a/org.eclipse.tips.ide/build.properties b/org.eclipse.tips.ide/build.properties
new file mode 100644
index 000000000..acbb37a1e
--- /dev/null
+++ b/org.eclipse.tips.ide/build.properties
@@ -0,0 +1,16 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ plugin.xml,\
+ icons/
diff --git a/org.eclipse.tips.ide/icons/lightbulb.png b/org.eclipse.tips.ide/icons/lightbulb.png
new file mode 100644
index 000000000..d22fde8ba
--- /dev/null
+++ b/org.eclipse.tips.ide/icons/lightbulb.png
Binary files differ
diff --git a/org.eclipse.tips.ide/plugin.xml b/org.eclipse.tips.ide/plugin.xml
new file mode 100644
index 000000000..9fc3c7f66
--- /dev/null
+++ b/org.eclipse.tips.ide/plugin.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.4"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<plugin>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup
+ class="org.eclipse.tips.ide.internal.Startup">
+ </startup>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ defaultHandler="org.eclipse.tips.ide.internal.TipsHandler"
+ id="org.eclipse.tips.ide.command.open"
+ name="Tip of the Day">
+ </command>
+ <command
+ id="org.eclipse.tips.ide.command.trim.open"
+ name="Tip of the Day">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="menu:help?before=tipsAndTricks">
+ <command
+ commandId="org.eclipse.tips.ide.command.open"
+ icon="icons/lightbulb.png"
+ id="org.eclipse.tips.ide.tip.menu"
+ label="Tip of the Day"
+ style="push"> variable="newtips">
+ </command>
+ </menuContribution>
+ <menuContribution
+ allPopups="false"
+ locationURI="toolbar:org.eclipse.ui.trim.status">
+ <toolbar
+ id="org.eclipse.tips.ide.toolbar.status"
+ label="Tip of the Day Toolbar">
+ <command
+ commandId="org.eclipse.tips.ide.command.trim.open"
+ icon="icons/lightbulb.png"
+ id="org.eclipse.tips.ide.tip.tool"
+ label="Tip of the Day"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="newtips">
+ <equals
+ value="true">
+ </equals>
+ </with>
+ </visibleWhen>
+ </command>
+ </toolbar>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.themes">
+ <themeElementCategory
+ id="org.eclipse.tips.presentation"
+ label="Tip of the Day">
+ </themeElementCategory>
+ <fontDefinition
+ categoryId="org.eclipse.tips.presentation"
+ defaultsTo="org.eclipse.jface.textfont"
+ id="org.eclipse.tips.browser.font"
+ label="Browser font">
+ <description>
+ The browser font (may be overridden by HTML and CSS)
+ </description>
+ </fontDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.browser.background.color"
+ label="Browser background color"
+ value="COLOR_WHITE">
+ <description>
+ The browser background color (may be overridden by HTML and CSS)
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.browser.foreground.color"
+ label="Browser foreground color"
+ value="COLOR_BLACK">
+ <description>
+ The browser foreground color (may be overridden by HTML and CSS)
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.hover.color"
+ label="Slider button hover color"
+ value="COLOR_GRAY">
+ <description>
+ The background color of the button when the mouse hovers over the button.
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.selection.color"
+ label="Slider button selection color"
+ value="COLOR_TITLE_BACKGROUND">
+ <description>
+ The color of the button when this is the selected button.
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.unreadTipCount.background.color"
+ label="Unread tip counter background color"
+ value="COLOR_RED">
+ <description>
+ The background color of the tip counter badge when in unread mode.
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.unreadTipCount.foreground.color"
+ label="Unread tip counter foreground color"
+ value="COLOR_WHITE">
+ <description>
+ The foreground color of the tip counter badge when in unread mode.
+ </description>
+ </colorDefinition>
+ <fontDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.badge.font"
+ label="Slider badge font"
+ value="Sans-bold-10">
+ <description>
+ The counter badge font.
+ </description>
+ </fontDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.tipCount.background.color"
+ label="Tip counter background color"
+ value="COLOR_DARK_GREEN">
+ <description>
+ The background color of the counter badge when not in unread mode.
+ </description>
+ </colorDefinition>
+ <colorDefinition
+ categoryId="org.eclipse.tips.presentation"
+ id="org.eclipse.tips.slider.tipCount.foreground.color"
+ label="Tip counter foreground color"
+ value="COLOR_WHITE">
+ <description>
+ The background color of the tip counter badge when not in unread mode.
+ </description>
+ </colorDefinition>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="org.eclipse.tips.ide.internal.TipsHandler"
+ commandId="org.eclipse.tips.ide.command.trim.open">
+ <activeWhen>
+ <with
+ variable="newtips">
+ <equals
+ value="true">
+ </equals>
+ </with>
+ </activeWhen>
+ </handler>
+ </extension>
+ <extension
+ point="org.eclipse.core.runtime.preferences">
+ <initializer
+ class="org.eclipse.tips.ide.internal.Preferences">
+ </initializer>
+ </extension>
+</plugin>
diff --git a/org.eclipse.tips.ide/pom.xml b/org.eclipse.tips.ide/pom.xml
new file mode 100644
index 000000000..d7389d0a5
--- /dev/null
+++ b/org.eclipse.tips.ide/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.ide</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Constants.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Constants.java
new file mode 100644
index 000000000..ffe557322
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Constants.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+/**
+ * Shared constants.
+ *
+ */
+public class Constants {
+
+ /**
+ * The ID of this bundle
+ */
+ public static final String BUNDLE_ID = "org.eclipse.tips.ide";
+
+ /**
+ * Dialog. menu and tool item icon.
+ */
+ public static final String ICON = "icons/lightbulb.png";
+
+ /**
+ * The workbench variable to be used on core expressions.
+ */
+ public static final String SOURCE_UNREAD_TIPS = "newtips";
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/IDETipManager.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/IDETipManager.java
new file mode 100644
index 000000000..b4652412d
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/IDETipManager.java
@@ -0,0 +1,239 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.eclipse.core.expressions.EvaluationResult;
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.ExpressionConverter;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.tips.core.ITipManager;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipManager;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.ui.DefaultTipManager;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.services.IEvaluationService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Class to manage the tip providers and start the tip of the day UI.
+ */
+@SuppressWarnings("restriction")
+public class IDETipManager extends DefaultTipManager {
+
+ private TipSourceProvider fSourceProvider = new TipSourceProvider();
+
+ private List<Integer> fReadTips = new ArrayList<>();
+
+ private boolean fNewTips;
+
+ private boolean fSourceProviderAdded;
+
+ private static IDETipManager instance = new IDETipManager();
+
+ /**
+ * @return the tip manager instance.
+ */
+ public static synchronized IDETipManager getInstance() {
+ if (instance.isDisposed()) {
+ instance = new IDETipManager();
+ }
+ return instance;
+ }
+
+ private IDETipManager() {
+ }
+
+ @Override
+ public ITipManager register(TipProvider provider) {
+ super.register(provider);
+ load(provider);
+ return this;
+ }
+
+ private void load(TipProvider provider) {
+ Job job = new Job("Loading " + provider.getDescription()) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ return provider.loadNewTips(monitor);
+ }
+ };
+ job.addJobChangeListener(new ProviderLoadJobChangeListener(this, provider));
+ job.schedule();
+ }
+
+ @Override
+ public TipManager open(boolean startUp) {
+ // if (isOpen()) {
+ // return this;
+ // }
+ if (!fSourceProviderAdded) {
+ IEvaluationService evaluationService = PlatformUI.getWorkbench().getService(IEvaluationService.class);
+ evaluationService.addSourceProvider(fSourceProvider);
+ fSourceProviderAdded = true;
+ }
+ return super.open(startUp);
+ }
+
+ /**
+ * Calculates the new tip count to find if we need to expose the status trim
+ * tool item.
+ *
+ * @param newTips
+ */
+ private void refreshUI(boolean newTips) {
+ Job job = new Job("Tip of the Day..") {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ setNewTips(newTips);
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+
+ @Override
+ public boolean isRunAtStartup() {
+ return Preferences.isRunAtStartup();
+ }
+
+ @Override
+ public TipManager setRunAtStartup(boolean runAtStartup) {
+ Preferences.setRunAtStartup(runAtStartup);
+ return this;
+ }
+
+ @Override
+ public boolean mustServeReadTips() {
+ return Preferences.isServeReadTips();
+ }
+
+ @Override
+ public TipManager setServeReadTips(boolean serveRead) {
+ Preferences.setServeReadTips(serveRead);
+ return this;
+ }
+
+ @Override
+ public ITipManager log(IStatus status) {
+ if (status.matches(IStatus.ERROR | IStatus.WARNING)) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ Platform.getLog(bundle).log(status);
+ }
+ if (System.getProperty("org.eclipse.tips.consolelog") != null) {
+ System.out.println(status.toString());
+ }
+ return this;
+ }
+
+ @Override
+ public boolean isRead(Tip tip) {
+ if (fReadTips.contains(new Integer(tip.hashCode()))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public TipManager setAsRead(Tip tip) {
+ if (!isRead(tip)) {
+ fReadTips.add(new Integer(tip.hashCode()));
+ }
+ return this;
+ }
+
+ protected synchronized IDETipManager setNewTips(boolean newTips) {
+ if (fNewTips != newTips) {
+ fNewTips = newTips;
+ fSourceProvider.setStatus(fNewTips);
+ }
+ return this;
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ boolean newTips = getProviders().stream().filter(p -> !p.getTips(true).isEmpty()).count() > 0;
+ refreshUI(newTips);
+ } finally {
+ super.dispose();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * The weight is determined by the enablement expression. If there is no
+ * enablement expression then the weight is 20. If there is a non matching
+ * enablement then the weight is 30. If there is a matching enablement then the
+ * weight is 10.
+ *
+ * @param provider
+ * the provider
+ *
+ * @return the weight
+ */
+ @Override
+ public int getPriority(TipProvider provider) {
+ log(LogUtil.info("Evaluating expression: " + provider.getExpression()));
+ int priority = doGetPriority(provider.getExpression());
+ log(LogUtil.info("Evaluating expression done. Priority: " + priority));
+ return priority;
+ }
+
+ private int doGetPriority(String expression) {
+ if (expression == null) {
+ return 20;
+ }
+ try {
+ String myExpression = "<enablement>" + expression + "</enablement>";
+ myExpression = "<?xml version=\"1.0\"?>" + myExpression;
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(myExpression.getBytes()));
+ Element element = (Element) doc.getElementsByTagName("enablement").item(0);
+ Expression expressionObj = ExpressionConverter.getDefault().perform(element);
+ final EvaluationResult result = expressionObj.evaluate(getEvaluationContext());
+ if (result == EvaluationResult.TRUE) {
+ return 10;
+ } else {
+ return 30;
+ }
+ } catch (Exception e) {
+ log(LogUtil.error(e));
+ return 20;
+ }
+ }
+
+ /**
+ *
+ * @return Evaluation Context to evaluate core expression
+ */
+ private static IEvaluationContext getEvaluationContext() {
+ IEvaluationService evalService = PlatformUI.getWorkbench().getService(IEvaluationService.class);
+ IEvaluationContext currentState = evalService.getCurrentState();
+ return currentState;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Preferences.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Preferences.java
new file mode 100644
index 000000000..d03451bd0
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Preferences.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.core.runtime.preferences.ConfigurationScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.osgi.service.prefs.BackingStoreException;
+
+/**
+ * Internal class to store preferences.
+ *
+ */
+public class Preferences extends AbstractPreferenceInitializer {
+
+ /**
+ * Preference store key to indicate showing tips at startup.
+ */
+ public static final String PREF_RUN_AT_STARTUP = "activate_at_startup";
+
+ /**
+ * Preference store key to indicate serving tips that the user as already seen.
+ */
+ public static final String PREF_SERVE_READ_TIPS = "serve_read_tips";
+
+ public Preferences() {
+ }
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IEclipsePreferences node = getPreferences();
+ node.putBoolean(PREF_RUN_AT_STARTUP, true);
+ node.putBoolean(PREF_SERVE_READ_TIPS, false);
+ try {
+ node.flush();
+ } catch (BackingStoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static IEclipsePreferences getPreferences() {
+ IEclipsePreferences node = ConfigurationScope.INSTANCE.getNode(Constants.BUNDLE_ID);
+ return node;
+ }
+
+ public static boolean isRunAtStartup() {
+ return getPreferences().getBoolean(PREF_RUN_AT_STARTUP, true);
+ }
+
+ public static boolean isServeReadTips() {
+ return getPreferences().getBoolean(PREF_SERVE_READ_TIPS, false);
+ }
+
+ public static void setRunAtStartup(boolean runAtStartup) {
+ IEclipsePreferences node = getPreferences();
+ node.putBoolean(PREF_RUN_AT_STARTUP, runAtStartup);
+ try {
+ node.flush();
+ } catch (BackingStoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void setServeReadTips(boolean serveReadTips) {
+ IEclipsePreferences node = getPreferences();
+ node.putBoolean(PREF_SERVE_READ_TIPS, serveReadTips);
+ try {
+ node.flush();
+ } catch (BackingStoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/ProviderLoadJobChangeListener.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/ProviderLoadJobChangeListener.java
new file mode 100644
index 000000000..36af35e29
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/ProviderLoadJobChangeListener.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipProvider;
+
+/**
+ *
+ * Internal class to listen to async provider load completions.
+ *
+ */
+public class ProviderLoadJobChangeListener extends JobChangeAdapter {
+
+ private IDETipManager fManager;
+ private TipProvider fProvider;
+
+ public ProviderLoadJobChangeListener(IDETipManager manager, TipProvider provider) {
+ fManager = manager;
+ fProvider = provider;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * If this provider has new tips then the {@link IDETipManager} gets a callback
+ * to update the UI.
+ *
+ * @see IDETipManager#setNewTips(boolean)
+ */
+ @Override
+ public void done(IJobChangeEvent event) {
+ for (Tip tip : fProvider.getTips(false)) {
+ if (!fManager.isRead(tip)) {
+ fManager.setNewTips(true);
+ return;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Startup.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Startup.java
new file mode 100644
index 000000000..8c3b67b9b
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/Startup.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.UIJob;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ * Early startup to run the TipManager in the IDE.
+ *
+ */
+public class Startup implements IStartup {
+
+ @Override
+ public void earlyStartup() {
+ loadProviders();
+ openManager();
+ }
+
+ /**
+ * Reloads the tip providers.
+ */
+ public static void loadProviders() {
+ IConfigurationElement[] elements = Platform.getExtensionRegistry()
+ .getConfigurationElementsFor("org.eclipse.tips.core.tips");
+ for (IConfigurationElement element : elements) {
+ if (element.getName().equals("provider")) {
+ try {
+ TipProvider provider = (TipProvider) element.createExecutableExtension("class");
+ provider.setExpression(getExpression(element));
+ IDETipManager.getInstance().register(provider);
+ } catch (CoreException e) {
+ log(e);
+ }
+ }
+ }
+ }
+
+ /**
+ * @return the core expression
+ */
+ private static String getExpression(IConfigurationElement element) {
+ IConfigurationElement[] enablements = element.getChildren("enablement");
+ if (enablements.length == 0) {
+ return null;
+ }
+ IConfigurationElement enablement = enablements[0];
+ String result = getXML(enablement.getChildren());
+ return result;
+ }
+
+ private static String getXML(IConfigurationElement[] children) {
+ String result = "";
+ for (IConfigurationElement element : children) {
+ IConfigurationElement[] myChildren = element.getChildren();
+ result += "<" + element.getName() + " " + getXMLAttributes(element) + ">";
+ if (myChildren.length > 0) {
+ result += getXML(myChildren);
+ } else {
+ String value = element.getValue();
+ result += value == null ? "" : value;
+ }
+ result += "</" + element.getName() + ">";
+ }
+ return result;
+ }
+
+ private static String getXMLAttributes(IConfigurationElement element) {
+ String result = "";
+ for (String name : element.getAttributeNames()) {
+ result += name;
+ result += "=\"";
+ result += element.getAttribute(name);
+ result += "\" ";
+ }
+ return result;
+ }
+
+ private static void openManager() {
+ UIJob job = new UIJob(PlatformUI.getWorkbench().getDisplay(), "Tip of the Day") {
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ IDETipManager.getInstance().open(true);
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule();
+ }
+
+ private static void log(CoreException e) {
+ Bundle bundle = FrameworkUtil.getBundle(Startup.class);
+ Status status = new Status(IStatus.ERROR, bundle.getSymbolicName(), e.getMessage(), e);
+ Platform.getLog(bundle).log(status);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipSourceProvider.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipSourceProvider.java
new file mode 100644
index 000000000..3877c8c73
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipSourceProvider.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.AbstractSourceProvider;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * Internal class to source a new boolean variable in the IDE called "newtips".
+ *
+ */
+public class TipSourceProvider extends AbstractSourceProvider {
+ private boolean fNewTips;
+
+ public TipSourceProvider() {
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public Map<?, ?> getCurrentState() {
+ Map<Object, Object> currentState = new HashMap<>();
+ currentState.put(Constants.SOURCE_UNREAD_TIPS, new Boolean(fNewTips));
+ return currentState;
+ }
+
+ @Override
+ public String[] getProvidedSourceNames() {
+ return new String[] { Constants.SOURCE_UNREAD_TIPS };
+ }
+
+ /**
+ * Propagate the new status of the <code>newtips</code> variable but always
+ * layouts all workbench windows to update the trim status.
+ *
+ * @param newTips
+ * true if there are new tips, false if there are no more new tips.
+ */
+ public synchronized void setStatus(boolean newTips) {
+ boolean changed = fNewTips != newTips;
+ if (changed) {
+ fNewTips = newTips;
+ }
+ layoutWorkbench(changed);
+ }
+
+ private void layoutWorkbench(boolean changed) {
+ UIJob job = new UIJob(PlatformUI.getWorkbench().getDisplay(), "Tip of the Day. Layout Shell") {
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (changed) {
+ fireSourceChanged(ISources.ACTIVE_WORKBENCH_WINDOW, getCurrentState());
+ }
+ for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
+ System.out.println("layout on " + window + " -> " + fNewTips);
+ window.getShell().layout(true, true);
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ job.schedule(5000); // allow the workbench to settle in.
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipsHandler.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipsHandler.java
new file mode 100644
index 000000000..901823467
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/TipsHandler.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ide.internal;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+/**
+ * Internal class to open the IDE Tip Dialog.
+ *
+ */
+public class TipsHandler extends AbstractHandler {
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ Startup.loadProviders();
+ IDETipManager.getInstance().open(false);
+ return null;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/package-info.java b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/package-info.java
new file mode 100644
index 000000000..93f73dde0
--- /dev/null
+++ b/org.eclipse.tips.ide/src/org/eclipse/tips/ide/internal/package-info.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Internal implementation of the TipManager for the Eclipse IDE. You can use
+ * these classes to see how we have implemented our TipManager and then create
+ * your own.
+ */
+package org.eclipse.tips.ide.internal; \ No newline at end of file
diff --git a/org.eclipse.tips.tests/.classpath b/org.eclipse.tips.tests/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.eclipse.tips.tests/.classpath
@@ -0,0 +1,7 @@
+<?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"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.tips.tests/.gitignore b/org.eclipse.tips.tests/.gitignore
new file mode 100644
index 000000000..09e3bc9b2
--- /dev/null
+++ b/org.eclipse.tips.tests/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/org.eclipse.tips.tests/.project b/org.eclipse.tips.tests/.project
new file mode 100644
index 000000000..50e9e6052
--- /dev/null
+++ b/org.eclipse.tips.tests/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.tests</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>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.tests/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tips.tests/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/org.eclipse.tips.tests/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.tips.tests/META-INF/MANIFEST.MF b/org.eclipse.tips.tests/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..f1959ea29
--- /dev/null
+++ b/org.eclipse.tips.tests/META-INF/MANIFEST.MF
@@ -0,0 +1,17 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Tip of the Day Tests
+Bundle-SymbolicName: org.eclipse.tips.tests
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: Eclipse
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.junit;bundle-version="4.12.0",
+ org.eclipse.tips.examples;bundle-version="0.1.0",
+ org.eclipse.tips.ide;bundle-version="0.1.0",
+ org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.eclipse.swt,
+ org.eclipse.jface;bundle-version="3.13.2",
+ org.eclipse.tips.core;bundle-version="0.1.0",
+ org.eclipse.tips.ui;bundle-version="0.1.0",
+ org.eclipse.tips.json;bundle-version="0.1.0"
+Automatic-Module-Name: org.eclipse.tips.tests
diff --git a/org.eclipse.tips.tests/build.properties b/org.eclipse.tips.tests/build.properties
new file mode 100644
index 000000000..07f663ae2
--- /dev/null
+++ b/org.eclipse.tips.tests/build.properties
@@ -0,0 +1,14 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/org.eclipse.tips.tests/pom.xml b/org.eclipse.tips.tests/pom.xml
new file mode 100644
index 000000000..45d395555
--- /dev/null
+++ b/org.eclipse.tips.tests/pom.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ <relativePath>../../</relativePath>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.tests</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>eclipse-test-plugin</packaging>
+</project>
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/JsonTestProvider.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/JsonTestProvider.java
new file mode 100644
index 000000000..8c5829a26
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/JsonTestProvider.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import java.net.MalformedURLException;
+
+import org.eclipse.tips.json.JsonTipProvider;
+
+public class JsonTestProvider extends JsonTipProvider {
+
+ public JsonTestProvider() throws MalformedURLException {
+ setJsonUrl("https://raw.githubusercontent.com/wimjongman/jsontips/master/photon/m3tips.json");
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTip.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTip.java
new file mode 100644
index 000000000..8c0b50b84
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTip.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.Date;
+
+import org.eclipse.tips.ui.internal.util.DateUtil;
+
+public class TestTip extends Tip implements IHtmlTip {
+
+ private String fSubject;
+ private String fHTML;
+
+ public TestTip(String providerId, String html, String subject) {
+ super(providerId);
+ fHTML = html;
+ fSubject = subject;
+ }
+
+ @Override
+ public Date getCreationDate() {
+ return DateUtil.getDateFromYYMMDD("31/12/1964");
+ }
+
+ @Override
+ public String getHTML() {
+ return fHTML;
+ }
+
+ @Override
+ public String getSubject() {
+ return fSubject;
+ }
+
+ @Override
+ public TipImage getImage() {
+ return null;
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipManager.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipManager.java
new file mode 100644
index 000000000..59ab3f1d4
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipManager.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+
+public class TestTipManager extends TipManager {
+
+ private boolean fShouldRun = true;
+ private List<Integer> fReadList = new ArrayList<>();
+
+ @Override
+ public TipManager setRunAtStartup(boolean shouldRun) {
+ fShouldRun = shouldRun;
+ return this;
+ }
+
+ @Override
+ public boolean isRunAtStartup() {
+ return fShouldRun;
+ }
+
+ @Override
+ public ITipManager register(TipProvider provider) {
+ super.register(provider);
+ load(provider);
+ return this;
+ }
+
+ private void load(TipProvider provider) {
+ provider.loadNewTips(new NullProgressMonitor());
+ }
+
+ @Override
+ public boolean isRead(Tip tip) {
+ return fReadList.contains(tip.hashCode());
+ }
+
+ @Override
+ public TipManager setAsRead(Tip tip) {
+ fReadList.remove((Integer) tip.hashCode());
+ fReadList.add(tip.hashCode());
+ return this;
+ }
+
+ @Override
+ public ITipManager log(IStatus status) {
+ System.out.println(status.toString());
+ return this;
+ }
+
+ @Override
+ public TipManager open(boolean startUp) {
+ return this;
+ }
+
+ @Override
+ public int getPriority(TipProvider provider) {
+ return 20;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipProvider.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipProvider.java
new file mode 100644
index 000000000..74423bf17
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TestTipProvider.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import java.util.Collections;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tips.ui.internal.util.ImageUtil;
+import org.eclipse.tips.ui.internal.util.ResourceManager;
+
+public class TestTipProvider extends TipProvider {
+
+ private static TipImage image;
+
+ @Override
+ public String getDescription() {
+ return "Test Tip Provider";
+ }
+
+ @Override
+ public String getID() {
+ return getClass().getName();
+ }
+
+ @Override
+ public TipImage getImage() {
+ if (image == null) {
+ Image pluginImage = ResourceManager.getPluginImage("org.eclipse.tips.examples", "icons/48/c++.png");
+ String base64 = ImageUtil.decodeFromImage(pluginImage, SWT.IMAGE_PNG);
+ image = new TipImage(base64);
+ }
+ return image;
+ }
+
+ @Override
+ public IStatus loadNewTips(IProgressMonitor monitor) {
+ setTips(Collections.emptyList());
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public void dispose() {
+
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageBas64Test.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageBas64Test.java
new file mode 100644
index 000000000..a1e575f70
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageBas64Test.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.junit.Test;
+
+public class TipImageBas64Test {
+
+ private static final String BASE64 = "data:image/png;base64,thequickbrownfox";
+ private static final String BASE64WRONG = "date:image/png;base64,thequickbrownfox";
+ private static final String BASE64WRONG2 = "data:image/plip ;base64,thequickbrownfox";
+
+ private TipImage getTipImage() {
+ return new TipImage(BASE64);
+ }
+
+ @Test(expected = AssertionFailedException.class)
+ public void testAssertHeight() {
+ new TipImage(BASE64).setAspectRatio(1000, 0, false);
+ }
+
+ @Test(expected = AssertionFailedException.class)
+ public void testAssertWidth() {
+ new TipImage(BASE64).setAspectRatio(0, 100, false);
+ }
+
+ @Test
+ public void testSetExtension() {
+ // assertTrue(getTipImage().getIMGAttributes(19, 10).contains("png"));
+ }
+
+ @Test
+ public void testSetExtension2() {
+ // assertTrue(getTipImage().setExtension("bmp").getBase64Image().contains("bmp"));
+ // assertTrue(getTipImage().getIMGAttributes(19, 10).contains("png"));
+ }
+
+ @Test
+ public void testGetIMGAttributes() {
+ String result = getTipImage().setAspectRatio(1.5).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"555\" height=\"370\""));
+ }
+
+ @Test
+ public void testGetBase64() {
+ assertTrue(getTipImage().getBase64Image().equals(BASE64));
+ }
+
+ @Test
+ public void testSetAspectRatioDouble() {
+ String result = getTipImage().setAspectRatio(1.5).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"555\" height=\"370\""));
+ }
+
+ @Test
+ public void testSetAspectRatioIntIntFalse() {
+ String result = getTipImage().setAspectRatio(200, 50, false).getIMGAttributes(100, 100).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"100\" height=\"25\""));
+ }
+
+ @Test
+ public void testSetAspectRatioIntIntTrue() {
+ String result = getTipImage().setAspectRatio(400, 300, true).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"400\" height=\"300\""));
+ }
+
+ @Test
+ public void testSetMaxHeight() {
+ String imgAttributes = new TipImage(BASE64).setAspectRatio(2).setMaxHeight(300).getIMGAttributes(200, 200);
+ assertTrue(imgAttributes, imgAttributes.trim().equalsIgnoreCase("width=\"200\" height=\"100\""));
+ }
+
+ @Test
+ public void testSetMaxWidth() {
+ String imgAttributes = new TipImage(BASE64).setAspectRatio(1.6).setMaxWidth(200).getIMGAttributes(400, 300);
+ assertTrue(imgAttributes, imgAttributes.trim().equalsIgnoreCase("width=\"200\" height=\"125\""));
+ }
+
+ public void testTipImage() {
+ new TipImage(BASE64);
+ }
+
+ @Test
+ public void testTipImage2() {
+ getTipImage();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testTipImage3() {
+ new TipImage(BASE64WRONG);
+ }
+
+ public void testTipImage4() {
+ TipImage tipImage = new TipImage(BASE64WRONG2);
+ assertTrue(tipImage.getIMGAttributes(1, 1).contains("plip"));
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageURLTest.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageURLTest.java
new file mode 100644
index 000000000..c182d09fe
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipImageURLTest.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.AssertionFailedException;
+import org.junit.Test;
+
+public class TipImageURLTest {
+
+ private static final String URL = "http://remainsoftware.com/img.png";
+
+ @Test(expected = Exception.class)
+ public void testTipImage() throws IOException {
+ new TipImage((URL) null);
+ }
+
+ @Test(expected = MalformedURLException.class)
+ public void testTipImage3() throws IOException {
+ new TipImage(new URL("0gl kjfslkfjsl dkfjsldkfjl"));
+ }
+
+ @Test
+ public void testTipImage2() throws IOException {
+ getTipImage();
+ }
+
+ private TipImage getTipImage() throws IOException {
+ return new TipImage(new URL(URL));
+ }
+
+ @Test
+ public void testSetMaxHeight() throws IOException {
+ String imgAttributes = new TipImage(new URL(URL)).setAspectRatio(2).setMaxHeight(300).getIMGAttributes(200,
+ 200);
+ assertTrue(imgAttributes, imgAttributes.trim().equalsIgnoreCase("width=\"200\" height=\"100\""));
+ }
+
+ @Test
+ public void testSetMaxWidth() throws IOException {
+ String imgAttributes = new TipImage(new URL(URL)).setAspectRatio(1.6).setMaxWidth(200).getIMGAttributes(400,
+ 300);
+ assertTrue(imgAttributes, imgAttributes.trim().equalsIgnoreCase("width=\"200\" height=\"125\""));
+
+ }
+
+ @Test(expected = AssertionFailedException.class)
+ public void testAssertWidth() throws IOException {
+ new TipImage(new URL(URL)).setAspectRatio(0, 100, false);
+ }
+
+ @Test(expected = AssertionFailedException.class)
+ public void testAssertHeight() throws IOException {
+ new TipImage(new URL(URL)).setAspectRatio(1000, 0, false);
+ }
+
+ @Test
+ public void testSetAspectRatioIntIntFalse() throws IOException {
+ String result = getTipImage().setAspectRatio(200, 50, false).getIMGAttributes(100, 100).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"100\" height=\"25\""));
+ }
+
+ @Test
+ public void testSetAspectRatioIntIntTrue() throws IOException {
+ String result = getTipImage().setAspectRatio(400, 300, true).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"400\" height=\"300\""));
+ }
+
+ @Test
+ public void testSetAspectRatioDouble() throws IOException {
+ String result = getTipImage().setAspectRatio(1.5).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"555\" height=\"370\""));
+ }
+
+ @Test
+ public void testGetIMGAttributes() throws IOException {
+ String result = getTipImage().setAspectRatio(1.5).getIMGAttributes(740, 370).trim();
+ assertTrue(result, result.equalsIgnoreCase("width=\"555\" height=\"370\""));
+ }
+
+ @Test
+ public void testSetExtension() throws IOException {
+ // assertTrue(getTipImage().getIMGAttributes(19, 10).contains("png"));
+ }
+
+ @Test
+ public void testSetExtension2() throws IOException {
+ // assertTrue(getTipImage().setExtension("bmp").getBase64Image().contains("bmp"));
+ // assertTrue(getTipImage().getIMGAttributes(19, 10).contains("png"));
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipManagerTest.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipManagerTest.java
new file mode 100644
index 000000000..f9d5c563e
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipManagerTest.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TipManagerTest {
+
+ private TestTipManager fManager;
+ private TestTipProvider fProvider1;
+ private TestTipProvider fProvider2;
+
+ @Before
+ public void testTipManager() {
+ fManager = new TestTipManager();
+ fProvider1 = new TestTipProvider();
+ fProvider2 = new TestTipProvider();
+ }
+
+ @Test
+ public void testGetProvider() {
+ fManager.register(fProvider1);
+ TipProvider provider = fManager.getProvider(fProvider1.getID());
+ assertTrue(provider == fProvider1);
+ }
+
+ @Test
+ public void testRegister() {
+ fManager.register(fProvider1);
+ TipProvider provider = fManager.getProvider(fProvider1.getID());
+ assertTrue(provider == fProvider1);
+ }
+
+ /**
+ * Only one provider with the same id can be registered.
+ */
+ @Test
+ public void testGetProviders() {
+ fManager.register(fProvider1);
+ fManager.register(fProvider2);
+ fManager.register(fProvider2);
+ assertTrue(fManager.getProviders().size() + "", fManager.getProviders().size() == 1);
+ }
+
+ @Test
+ public void testIsRunAtStartup() {
+ assertTrue(fManager.isRunAtStartup());
+ fManager.setRunAtStartup(false);
+ assertTrue(!fManager.isRunAtStartup());
+ }
+
+ @Test
+ public void testSetRunAtStartup() {
+ assertTrue(fManager.isRunAtStartup());
+ fManager.setRunAtStartup(false);
+ assertTrue(!fManager.isRunAtStartup());
+ }
+
+ @Test
+ public void testLoad() {
+ ArrayList<String> test = new ArrayList<>();
+ TestTipProvider testTipProvider = new TestTipProvider() {
+ @Override
+ public IStatus loadNewTips(IProgressMonitor monitor) {
+ test.add("fff");
+ return Status.OK_STATUS;
+ }
+ };
+ fManager.register(testTipProvider);
+ assertTrue(!test.isEmpty());
+ }
+
+ @Test
+ public void testLoad2() {
+ ArrayList<String> test = new ArrayList<>();
+ TestTipProvider testTipProvider = new TestTipProvider() {
+ @Override
+ public IStatus loadNewTips(IProgressMonitor monitor) {
+ test.add("fff");
+ return Status.OK_STATUS;
+ }
+ };
+ fManager.register(testTipProvider);
+ assertTrue(!test.isEmpty());
+ }
+
+ @Test
+ public void testOpen() {
+ ArrayList<String> test = new ArrayList<>();
+ TestTipManager m = new TestTipManager() {
+ @Override
+ public TipManager open(boolean startUp) {
+ if (startUp) {
+ test.add("1");
+ } else {
+ test.add("1");
+ test.add("2");
+ }
+ return this;
+ }
+ };
+ m.open(true);
+ assertTrue(test.size() == 1);
+ test.clear();
+ m.open(false);
+ assertTrue(test.size() == 2);
+ }
+
+ @Test
+ public void testDispose() {
+ fManager.dispose();
+ }
+
+ @Test
+ public void testDispose2() {
+ fManager.register(fProvider1);
+ fManager.dispose();
+ }
+
+ @Test
+ public void testLogClassOfQException() {
+ Exception e = new Exception("FFF");
+ fManager.log(LogUtil.error(getClass(), e));
+ }
+
+ @Test
+ public void testIsRead() {
+ fManager.register(fProvider1);
+ fProvider1.setTips(Arrays.asList(new TestTip(fProvider1.getID(),"<b>bold</b>", "Tip 1"),
+ new TestTip(fProvider1.getID(),"<b>bold2</b>", "Tip 2")));
+ fManager.setAsRead(fProvider1.getCurrentTip());
+ assertTrue(fManager.isRead(fProvider1.getCurrentTip()));
+ }
+
+ @Test
+ public void testSetRead() {
+ fManager.register(fProvider1);
+ fProvider1.setTips(Arrays.asList(new TestTip(fProvider1.getID(),"<b>bold</b>", "Tip 1"),
+ new TestTip(fProvider1.getID(),"<b>bold2</b>", "Tip 2")));
+ fManager.setAsRead(fProvider1.getCurrentTip());
+ assertTrue(fManager.isRead(fProvider1.getCurrentTip()));
+ }
+
+ @Test
+ public void testSetServeUnread() {
+ fManager.register(fProvider1);
+ fProvider1.setTips(Arrays.asList(new TestTip(fProvider1.getID(),"<b>bold</b>", "Tip 1"),
+ new TestTip(fProvider1.getID(),"<b>bold2</b>", "Tip 2")));
+ fManager.setAsRead(fProvider1.getCurrentTip());
+ assertTrue(fProvider1.getTips(true).size() + "", fProvider1.getTips(true).size() == 1);
+ fManager.setServeReadTips(true);
+ assertTrue(fProvider1.getTips(true).size() == 2);
+ }
+
+ @Test
+ public void testIsServeUnread() {
+ assertTrue(fManager.mustServeReadTips() == false);
+ fManager.setServeReadTips(true);
+ assertTrue(fManager.mustServeReadTips());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipProviderTest.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipProviderTest.java
new file mode 100644
index 000000000..f56013e64
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipProviderTest.java
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TipProviderTest {
+
+ private TestTipManager fManager;
+ private TestTipProvider fProvider;
+
+ @Before
+ public void testTipProvider() {
+ fManager = new TestTipManager();
+ fProvider = (TestTipProvider) new TestTipProvider().setManager(fManager);
+ }
+
+ @Test
+ public void testDispose() {
+ fProvider.dispose();
+ }
+
+ @Test
+ public void testGetDescription() {
+ assertTrue(fProvider.getDescription() != null);
+ }
+
+ @Test
+ public void testGetID() {
+ assertTrue(fProvider.getID() != null);
+ assertTrue(fProvider.getID().equals(fProvider.getClass().getName()));
+ }
+
+
+ @Test
+ public void testGetImage() {
+ assertTrue(fProvider.getImage() != null);
+ }
+
+ @Test
+ public void testGetTips() {
+ assertTrue(fProvider.getTips(false).size() == 0);
+ createTestDate();
+ fManager.setAsRead(fProvider.getNextTip());
+ assertTrue(fProvider.getTips(false).size() == 2);
+ assertTrue(fProvider.getTips(false).size() == 2);
+ List<Tip> tips = fProvider.getTips(true);
+ assertTrue(fProvider.getTips(true).size() == 1);
+ ((TipManager) fProvider.getManager()).setServeReadTips(true);
+ assertTrue(fProvider.getTips(false).size() == 2);
+ }
+
+ private void createTestDate() {
+ fProvider.setTips(Arrays.asList(new TestTip(fProvider.getID(),"<b>bold</b>", "Tip 1"),
+ new TestTip(fProvider.getID(),"<b>bold2</b>", "Tip 2")));
+ }
+
+ @Test
+ public void testGetCurrentTip() {
+ assertTrue(fProvider.getNextTip().equals(fProvider.getCurrentTip()));
+ }
+
+ @Test
+ public void testGetCurrentTip2() {
+ assertTrue(fProvider.getCurrentTip().equals(fProvider.getPreviousTip()));
+ }
+
+ @Test
+ public void testGetNextTip() {
+ createTestDate();
+ fManager.setAsRead(fProvider.getNextTip());
+ assertTrue(fProvider.getNextTip().equals(fProvider.getCurrentTip()));
+ Tip nextTip = fProvider.getNextTip();
+ fManager.setAsRead(nextTip);
+ assertTrue(fManager.isRead(nextTip));
+ Tip nextTip2 = fProvider.getNextTip();
+ fManager.setAsRead(nextTip2);
+ assertTrue(fManager.isRead(nextTip2));
+ assertTrue(fProvider.getNextTip().getClass().getSimpleName().equals("FinalTip"));
+ ((TipManager) fProvider.getManager()).setServeReadTips(true);
+ assertFalse(fProvider.getNextTip().getClass().getSimpleName().equals("FinalTip"));
+ }
+
+ @Test
+ public void testGetPreviousTip() {
+ assertTrue(fProvider.getPreviousTip().equals(fProvider.getCurrentTip()));
+ assertTrue(fProvider.getPreviousTip().equals(fProvider.getCurrentTip()));
+ }
+
+ @Test
+ public void testGetPreviousTip2() {
+ assertTrue(!fProvider.getPreviousTip().equals(null));
+ assertTrue(fProvider.getNextTip().getClass().getSimpleName().equals("FinalTip"));
+ }
+
+ @Test
+ public void testGetPreviousTip3() {
+ ((TipManager) fProvider.getManager()).setServeReadTips(true);
+ assertTrue(fProvider.getPreviousTip().equals(fProvider.getCurrentTip()));
+ }
+
+ @Test
+ public void testGetPreviousTip4() {
+ createTestDate();
+ assertTrue(fProvider.getPreviousTip() != null);
+ assertTrue(fProvider.getPreviousTip() != null);
+ assertTrue(fProvider.getPreviousTip() != null);
+ }
+
+ @Test
+ public void testGetTipManager() {
+ assertTrue(fProvider.getManager().equals(fManager));
+ }
+
+ @Test
+ public void testIsReady() {
+ TestTipProvider p = (TestTipProvider) new TestTipProvider().setManager(fManager);
+ assertTrue(!p.isReady());
+ p.setTips(Collections.emptyList());
+ assertTrue(p.isReady());
+ }
+
+ @Test
+ public void testLoad() {
+ TestTipProvider p = (TestTipProvider) new TestTipProvider().setManager(fManager);
+ assertTrue(!p.isReady());
+ p.loadNewTips(new NullProgressMonitor());
+ assertTrue(p.isReady());
+ }
+
+ @Test
+ public void testSetManager() {
+ TestTipProvider p = new TestTipProvider();
+ assertTrue(p.getManager() == null);
+ p.setManager(fManager);
+ assertTrue(p.getManager() != null);
+ }
+
+ @Test
+ public void testSetTips() {
+ TestTipProvider p = new TestTipProvider() {
+ @Override
+ public IStatus loadNewTips(IProgressMonitor pMonitor) {
+ assertTrue(getTips(false).size() == 0);
+ assertTrue(setTips(Arrays.asList(new Tip[] { new TestTip(getID(),"DDD", "XXX") })).getTips(false)
+ .size() == 1);
+ return Status.OK_STATUS;
+ }
+ };
+ assertTrue(p.getTips(false).size() == 0);
+ fManager.register(p);
+ assertTrue(p.getTips(false).size() == 1);
+ }
+
+ @Test
+ public void testAddTips() {
+ TestTipProvider p = new TestTipProvider() {
+ @Override
+ public IStatus loadNewTips(IProgressMonitor pMonitor) {
+ assertTrue(getTips(false).size() == 0);
+ assertTrue(setTips(Arrays.asList(new Tip[] { new TestTip(getID(),"DDD", "XXX") })).getTips(false)
+ .size() == 1);
+ assertTrue(addTips(Arrays.asList(new Tip[] { new TestTip(getID(),"DDD", "XXX") })).getTips(false)
+ .size() == 2);
+ return Status.OK_STATUS;
+ }
+ };
+ fManager.register(p);
+
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipTest.java b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipTest.java
new file mode 100644
index 000000000..81e4a09e9
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/core/TipTest.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class TipTest {
+
+ private static final String HTML = "<head></head>";
+ private static final String SUBJECT_TIP = "Tip Subject";
+ private TestTipManager fManager;
+ private TestTipProvider fProvider;
+ private TestTip fTip;
+
+ @Before
+ public void setup() {
+ fManager = new TestTipManager();
+ fProvider = (TestTipProvider) new TestTipProvider().setManager(fManager);
+ createTestDate();
+ fTip = new TestTip(fProvider.getID(), HTML, SUBJECT_TIP) {
+ @Override
+ public List<TipAction> getActions() {
+ ArrayList<TipAction> actions = new ArrayList<>();
+ ArrayList<String> result = new ArrayList<>();
+ Runnable runner = () -> result.add("entry");
+
+ actions.add(new TipAction("text", "tooltip", runner, null));
+ return actions;
+ }
+ };
+ }
+
+ @Test
+ public void testHashCode() {
+ assertTrue(fProvider.getNextTip().hashCode() != 0);
+ }
+
+ @Test
+ public void testHashCode2() {
+ TestTip testTip = new TestTip(fProvider.getID(), HTML, SUBJECT_TIP);
+ TestTip testTip2 = new TestTip(fProvider.getID(), HTML, SUBJECT_TIP);
+ assertTrue(testTip.hashCode() == testTip2.hashCode());
+ }
+
+ @Test
+ public void testTip() {
+ new TestTip(fProvider.getID(),HTML, SUBJECT_TIP);
+ }
+
+ @Test
+ public void testGetAction() {
+ assertTrue(fTip.getActions().size() > 0);
+ }
+
+ @Test
+ public void testGetCreationDate() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(fTip.getCreationDate());
+ assertTrue(calendar.get(Calendar.MONTH) == 11);
+ assertTrue(calendar.get(Calendar.YEAR) == 1964);
+ assertTrue(calendar.get(Calendar.DAY_OF_MONTH) == 31);
+ }
+
+ @Test
+ public void testGetHTML() {
+ assertTrue(fTip.getHTML() != null);
+ }
+
+ @Test
+ public void testGetImage() {
+ assertTrue(fTip.getImage() == null);
+ }
+
+ @Test
+ public void testGetSubject() {
+ assertTrue(fTip.getSubject() != null);
+ assertTrue(fTip.getSubject().equals(SUBJECT_TIP));
+ }
+
+ @Test
+ public void testEqualsObject() {
+ TestTip testTip = new TestTip(fProvider.getID(),HTML, SUBJECT_TIP);
+ TestTip testTipx = new TestTip(fProvider.getID(),HTML, SUBJECT_TIP);
+ assertTrue(!testTip.equals("hello"));
+ assertTrue(!testTip.equals(null));
+ assertTrue(testTip.equals(testTip));
+ assertTrue(!testTip.equals(fTip));
+ assertTrue(testTip.equals(testTipx));
+
+ TestTipProvider testTipProvider = new TestTipProvider() {
+ @Override
+ public String getID() {
+ return "sss";
+ }
+ };
+ TestTip testTip2 = new TestTip(fProvider.getID(),HTML, SUBJECT_TIP + "DDD");
+ assertTrue(!testTip.equals(testTip2));
+ assertTrue(!testTip.equals(testTip2));
+
+ TestTip testTip3 = new TestTip(fProvider.getID(),HTML, SUBJECT_TIP + "DDD");
+ assertTrue(!testTip.equals(testTip3));
+ assertTrue(!testTip3.equals(testTip));
+
+ TestTipProvider testTipProvider2 = new TestTipProvider() {
+ @Override
+ public String getID() {
+ return null;
+ }
+ };
+
+ TestTip testTip4 = new TestTip(fProvider.getID(), HTML, SUBJECT_TIP + "DDD");
+ assertTrue(!testTip.equals(testTip4));
+ assertTrue(!testTip4.equals(testTip));
+
+ TestTip testTip5 = new TestTip(fProvider.getID(),HTML, SUBJECT_TIP + "DDDWW");
+ assertTrue(!testTip.equals(testTip5));
+ assertTrue(!testTip5.equals(testTip));
+
+ TestTip testTip6 = new TestTip(fProvider.getID(),HTML, null);
+ assertTrue(!testTip.equals(testTip6));
+ assertTrue(!testTip6.equals(testTip));
+
+ }
+
+ @Test
+ public void testIsRead() {
+ assertTrue(!fManager.isRead(fTip));
+ fManager.setAsRead(fTip);
+ fManager.setAsRead(fTip);
+ assertTrue(fManager.isRead(fTip));
+ }
+
+ private void createTestDate() {
+ fProvider.setTips(Arrays.asList(new TestTip(fProvider.getID(),"<b>bold</b>", "Tip 1"),
+ new TestTip(fProvider.getID(),"<b>bold2</b>", "Tip 2")));
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/Sleak.java b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/Sleak.java
new file mode 100644
index 000000000..88d1856d8
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/Sleak.java
@@ -0,0 +1,306 @@
+package org.eclipse.tips.manual.tests;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/*
+ * Copyright (c) 2000, 2018 IBM Corp. All rights reserved.
+ * This file is made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ */
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.DeviceData;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Region;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class Sleak {
+ Display display;
+ Shell shell;
+ List list;
+ Canvas canvas;
+ Button start, stop, check;
+ Text text;
+ Label label;
+
+ Object[] oldObjects = new Object[0];
+ Error[] oldErrors = new Error[0];
+ Object[] objects = new Object[0];
+ Error[] errors = new Error[0];
+
+ public void open() {
+ display = Display.getCurrent();
+ shell = new Shell(display);
+ shell.setText("S-Leak");
+ list = new List(shell, SWT.BORDER | SWT.V_SCROLL);
+ list.addListener(SWT.Selection, event -> refreshObject());
+ text = new Text(shell, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+ canvas = new Canvas(shell, SWT.BORDER);
+ canvas.addListener(SWT.Paint, event -> paintCanvas(event));
+ check = new Button(shell, SWT.CHECK);
+ check.setText("Stack");
+ check.addListener(SWT.Selection, e -> toggleStackTrace());
+ start = new Button(shell, SWT.PUSH);
+ start.setText("Snap");
+ start.addListener(SWT.Selection, event -> refreshAll());
+ stop = new Button(shell, SWT.PUSH);
+ stop.setText("Diff");
+ stop.addListener(SWT.Selection, event -> refreshDifference());
+ label = new Label(shell, SWT.BORDER);
+ label.setText("0 object(s)");
+ shell.addListener(SWT.Resize, e -> layout());
+ check.setSelection(false);
+ text.setVisible(false);
+ Point size = shell.getSize();
+ shell.setSize(size.x / 2, size.y / 2);
+ shell.open();
+ refreshAll();
+ }
+
+ void refreshLabel() {
+ int colors = 0, cursors = 0, fonts = 0, gcs = 0, images = 0;
+ for (Object object : objects) {
+ if (object instanceof Color) {
+ colors++;
+ }
+ if (object instanceof Cursor) {
+ cursors++;
+ }
+ if (object instanceof Font) {
+ fonts++;
+ }
+ if (object instanceof GC) {
+ gcs++;
+ }
+ if (object instanceof Image) {
+ images++;
+ }
+ }
+ String string = "";
+ if (colors != 0) {
+ string += colors + " Color(s)\n";
+ }
+ if (cursors != 0) {
+ string += cursors + " Cursor(s)\n";
+ }
+ if (fonts != 0) {
+ string += fonts + " Font(s)\n";
+ }
+ if (gcs != 0) {
+ string += gcs + " GC(s)\n";
+ }
+ if (images != 0) {
+ string += images + " Image(s)\n";
+ }
+ /* Currently regions are not counted. */
+ // if (regions != 0) string += regions + " Region(s)\n";
+ if (string.length() != 0) {
+ string = string.substring(0, string.length() - 1);
+ }
+ label.setText(string);
+ }
+
+ void refreshDifference() {
+ DeviceData info = display.getDeviceData();
+ if (!info.tracking) {
+ MessageBox dialog = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
+ dialog.setText(shell.getText());
+ dialog.setMessage("Warning: Device is not tracking resource allocation");
+ dialog.open();
+ }
+ Object[] newObjects = info.objects;
+ Error[] newErrors = info.errors;
+ Object[] diffObjects = new Object[newObjects.length];
+ Error[] diffErrors = new Error[newErrors.length];
+ int count = 0;
+ for (int i = 0; i < newObjects.length; i++) {
+ int index = 0;
+ while (index < oldObjects.length) {
+ if (newObjects[i] == oldObjects[index]) {
+ break;
+ }
+ index++;
+ }
+ if (index == oldObjects.length) {
+ diffObjects[count] = newObjects[i];
+ diffErrors[count] = newErrors[i];
+ count++;
+ }
+ }
+ objects = new Object[count];
+ errors = new Error[count];
+ System.arraycopy(diffObjects, 0, objects, 0, count);
+ System.arraycopy(diffErrors, 0, errors, 0, count);
+ list.removeAll();
+ text.setText("");
+ canvas.redraw();
+ for (Object object : objects) {
+ list.add(objectName(object));
+ }
+ refreshLabel();
+ layout();
+ }
+
+ String objectName(Object object) {
+ String string = object.toString();
+ int index = string.lastIndexOf('.');
+ if (index == -1) {
+ return string;
+ }
+ return string.substring(index + 1, string.length());
+ }
+
+ void toggleStackTrace() {
+ refreshObject();
+ layout();
+ }
+
+ void paintCanvas(Event event) {
+ canvas.setCursor(null);
+ int index = list.getSelectionIndex();
+ if (index == -1) {
+ return;
+ }
+ GC gc = event.gc;
+ Object object = objects[index];
+ if (object instanceof Color) {
+ if (((Color) object).isDisposed()) {
+ return;
+ }
+ gc.setBackground((Color) object);
+ gc.fillRectangle(canvas.getClientArea());
+ return;
+ }
+ if (object instanceof Cursor) {
+ if (((Cursor) object).isDisposed()) {
+ return;
+ }
+ canvas.setCursor((Cursor) object);
+ return;
+ }
+ if (object instanceof Font) {
+ if (((Font) object).isDisposed()) {
+ return;
+ }
+ gc.setFont((Font) object);
+ FontData[] array = gc.getFont().getFontData();
+ String string = "";
+ String lf = text.getLineDelimiter();
+ for (FontData data : array) {
+ String style = "NORMAL";
+ int bits = data.getStyle();
+ if (bits != 0) {
+ if ((bits & SWT.BOLD) != 0) {
+ style = "BOLD ";
+ }
+ if ((bits & SWT.ITALIC) != 0) {
+ style += "ITALIC";
+ }
+ }
+ string += data.getName() + " " + data.getHeight() + " " + style + lf;
+ }
+ gc.drawString(string, 0, 0);
+ return;
+ }
+ // NOTHING TO DRAW FOR GC
+ // if (object instanceof GC) {
+ // return;
+ // }
+ if (object instanceof Image) {
+ if (((Image) object).isDisposed()) {
+ return;
+ }
+ gc.drawImage((Image) object, 0, 0);
+ return;
+ }
+ if (object instanceof Region) {
+ if (((Region) object).isDisposed()) {
+ return;
+ }
+ String string = ((Region) object).getBounds().toString();
+ gc.drawString(string, 0, 0);
+ return;
+ }
+ }
+
+ void refreshObject() {
+ int index = list.getSelectionIndex();
+ if (index == -1) {
+ return;
+ }
+ if (check.getSelection()) {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ PrintStream s = new PrintStream(stream);
+ errors[index].printStackTrace(s);
+ text.setText(stream.toString());
+ text.setVisible(true);
+ canvas.setVisible(false);
+ } else {
+ canvas.setVisible(true);
+ text.setVisible(false);
+ canvas.redraw();
+ }
+ }
+
+ void refreshAll() {
+ oldObjects = new Object[0];
+ oldErrors = new Error[0];
+ refreshDifference();
+ oldObjects = objects;
+ oldErrors = errors;
+ }
+
+ void layout() {
+ Rectangle rect = shell.getClientArea();
+ int width = 0;
+ String[] items = list.getItems();
+ GC gc = new GC(list);
+ for (int i = 0; i < objects.length; i++) {
+ width = Math.max(width, gc.stringExtent(items[i]).x);
+ }
+ gc.dispose();
+ Point size1 = start.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ Point size2 = stop.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ Point size3 = check.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ Point size4 = label.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+ width = Math.max(size1.x, Math.max(size2.x, Math.max(size3.x, width)));
+ width = Math.max(64, Math.max(size4.x, list.computeSize(width, SWT.DEFAULT).x));
+ start.setBounds(0, 0, width, size1.y);
+ stop.setBounds(0, size1.y, width, size2.y);
+ check.setBounds(0, size1.y + size2.y, width, size3.y);
+ label.setBounds(0, rect.height - size4.y, width, size4.y);
+ int height = size1.y + size2.y + size3.y;
+ list.setBounds(0, height, width, rect.height - height - size4.y);
+ text.setBounds(width, 0, rect.width - width, rect.height);
+ canvas.setBounds(width, 0, rect.width - width, rect.height);
+ }
+
+ public static void main(String[] args) {
+ Display display = new Display();
+ Sleak sleak = new Sleak();
+ sleak.open();
+ while (!sleak.shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ }
+
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/SleakTipManager.java b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/SleakTipManager.java
new file mode 100644
index 000000000..150ba8021
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/SleakTipManager.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.manual.tests;
+
+import java.net.MalformedURLException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.swt.graphics.DeviceData;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tips.core.ITipManager;
+import org.eclipse.tips.core.JsonTestProvider;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipManager;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.ui.internal.TipDialog;
+import org.eclipse.tips.ui.internal.util.ResourceManager;
+
+/**
+ * Class to manage the tip providers and start the tip of the day UI.
+ */
+public class SleakTipManager extends TipManager {
+
+ private static SleakTipManager instance = new SleakTipManager();
+
+ public static void main(String[] args) throws MalformedURLException {
+ instance.register(new JsonTestProvider());
+ if (!instance.getProviders().isEmpty()) {
+ instance.open(true);
+ }
+ }
+
+ /**
+ * @return the tip manager instance.
+ */
+ public static SleakTipManager getInstance() {
+ return instance;
+ }
+
+ private SleakTipManager() {
+ }
+
+ /**
+ * For resource leak detection rename this method to open and run the IDE. Won't
+ * work on Linux because GTK cannot handle multiple displays.
+ */
+ @Override
+ public TipManager open(boolean pStart) {
+
+ Thread t = new Thread(() -> {
+
+ DeviceData data = new DeviceData();
+ data.tracking = true;
+ Display display = new Display(data);
+ Shell shell = new Shell(display);
+ shell.setLayout(new FillLayout());
+ new Sleak().open();
+ TipDialog tipDialog = new TipDialog(shell, SleakTipManager.this, TipDialog.DEFAULT_STYLE);
+ tipDialog.addDisposeListener(pE -> dispose());
+ tipDialog.open();
+ shell.pack();
+ shell.open();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ display.dispose();
+ });
+
+ t.start();
+ return this;
+
+ }
+
+ @Override
+ public ITipManager register(TipProvider provider) {
+ super.register(provider);
+ load(provider);
+ return this;
+ }
+
+ private void load(TipProvider pProvider) {
+ pProvider.loadNewTips(new NullProgressMonitor());
+ }
+
+ @Override
+ public boolean isRead(Tip pTip) {
+ return false;
+ }
+
+ @Override
+ public TipManager setAsRead(Tip pTip) {
+ return this;
+ }
+
+ protected synchronized SleakTipManager setNewTips(boolean pNewTips) {
+ return this;
+ }
+
+ @Override
+ public void dispose() {
+ ResourceManager.dispose();
+ super.dispose();
+ }
+
+ @Override
+ public TipManager setRunAtStartup(boolean pShouldRun) {
+ return this;
+ }
+
+ @Override
+ public ITipManager log(IStatus pStatus) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getPriority(TipProvider pProvider) {
+ return 0;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/XML.java b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/XML.java
new file mode 100644
index 000000000..888bbe11d
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/XML.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.manual.tests;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+public class XML {
+
+ public static void main(String[] args)
+ throws ParserConfigurationException, TransformerException, SAXException, IOException {
+ String pExpression = "<with variable=\"activeWorkbenchWindow.activePerspective\"><equals value=\"org.eclipse.jdt.ui.JavaPerspective\"></equals></with>";
+ pExpression = "<enablement>" + pExpression + "</enablement>";
+ pExpression = "<?xml version=\"1.0\"?>" + pExpression;
+ System.out.println(pExpression);
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc = factory.newDocumentBuilder().parse(new ByteArrayInputStream(pExpression.getBytes()));
+ Element element2 = (Element) doc.getElementsByTagName("enablement").item(0);
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMSource source = new DOMSource(element2);
+ StreamResult result = new StreamResult(System.out);
+ transformer.transform(source, result);
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/package-info.java b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/package-info.java
new file mode 100644
index 000000000..9e3779299
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/manual/tests/package-info.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ *
+ */
+/**
+ * @author jongw
+ *
+ */
+package org.eclipse.tips.manual.tests; \ No newline at end of file
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/tests/Good.java b/org.eclipse.tips.tests/src/org/eclipse/tips/tests/Good.java
new file mode 100644
index 000000000..7232dd419
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/tests/Good.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class Good {
+
+ @Test
+ public void test() {
+ assertTrue(true);
+ }
+}
diff --git a/org.eclipse.tips.tests/src/org/eclipse/tips/util/ImageUtilTest.java b/org.eclipse.tips.tests/src/org/eclipse/tips/util/ImageUtilTest.java
new file mode 100644
index 000000000..d98699d89
--- /dev/null
+++ b/org.eclipse.tips.tests/src/org/eclipse/tips/util/ImageUtilTest.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.util;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.tips.ui.internal.util.ImageUtil;
+import org.junit.Test;
+
+public class ImageUtilTest {
+
+ private static String fImageBase64 = "" //
+ + "iVBORw0KGgoAAAANSUhEUgAAACIAAAAkCAYAAADsHujfAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
+ + "jwv8YQUAAAAJcEhZcwAAJOgAACToAYJjBRwAAAEcSURBVFhH7dZBCsIwEIXh3MkbuPJubtwI0kOIK"
+ + "IJuddseo+eIPORBDZM0MyPaRQr/IiRtP9osEoa+j0uoQdKykPVtiLunPKdtcx/i9iHPMRECRDi+82K"
+ + "A4LNKGBHCG5kVM0UgjKV1SITgxdMHIC1Gg0DZPeLBaBEoC0EWjAWBihCkwVgRaBaCajAeBKqCoBLGi0"
+ + "DVECRhVtfPsQWBVBAkYZgVgdQQlPsy0traTJB0TzDuGUtqSA7BrBgVJEVgLP0mC6YaIiE49w1MFaSEYF"
+ + "7MLKQGwTyYIkSDYFZMFmJBMAtGhHgQTIsRIdObLQiWYnAWltYhEYJDrhfBiCkhUHGz/rIGSWuQtAZJC+M"
+ + "4xn93Ol9iiAu49oduKZAuvgC5R8NSTiN3qAAAAABJRU5ErkJggg==";
+
+ private static String fImageBase64HTML = "data:image/png;base64," //
+ + "iVBORw0KGgoAAAANSUhEUgAAACIAAAAkCAYAAADsHujfAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
+ + "jwv8YQUAAAAJcEhZcwAAJOgAACToAYJjBRwAAAEcSURBVFhH7dZBCsIwEIXh3MkbuPJubtwI0kOIK"
+ + "IJuddseo+eIPORBDZM0MyPaRQr/IiRtP9osEoa+j0uoQdKykPVtiLunPKdtcx/i9iHPMRECRDi+82K"
+ + "A4LNKGBHCG5kVM0UgjKV1SITgxdMHIC1Gg0DZPeLBaBEoC0EWjAWBihCkwVgRaBaCajAeBKqCoBLGi0"
+ + "DVECRhVtfPsQWBVBAkYZgVgdQQlPsy0traTJB0TzDuGUtqSA7BrBgVJEVgLP0mC6YaIiE49w1MFaSEYF"
+ + "7MLKQGwTyYIkSDYFZMFmJBMAtGhHgQTIsRIdObLQiWYnAWltYhEYJDrhfBiCkhUHGz/rIGSWuQtAZJC+M"
+ + "4xn93Ol9iiAu49oduKZAuvgC5R8NSTiN3qAAAAABJRU5ErkJggg==";
+
+ @Test
+ public void decodeImageToBase64Test() throws IOException {
+ Image image = new Image(null, ImageUtil.decodeToImage(fImageBase64));
+ String base64Image = ImageUtil.decodeFromImage(image, SWT.IMAGE_PNG);
+ image.dispose();
+ image = new Image(null, ImageUtil.decodeToImage(base64Image));
+ String base64Image2 = ImageUtil.decodeFromImage(image, SWT.IMAGE_PNG);
+ assertTrue(base64Image, base64Image.equals(base64Image2));
+ image.dispose();
+
+ Image image2 = new Image(null, ImageUtil.decodeToImage(fImageBase64HTML));
+ String base64Image3 = ImageUtil.decodeFromImage(image2, SWT.IMAGE_PNG);
+ image2.dispose();
+ image2 = new Image(null, ImageUtil.decodeToImage(base64Image3));
+ String base64Image4 = ImageUtil.decodeFromImage(image2, SWT.IMAGE_PNG);
+ assertTrue(base64Image3, base64Image3.equals(base64Image4));
+ image2.dispose();
+ }
+
+ @Test
+ public void testGetWidth() {
+ assertTrue(ImageUtil.getWidth(1, 1000, 100) == 100);
+ assertTrue(ImageUtil.getWidth(1, 100, 1000) == 100);
+ assertTrue(ImageUtil.getWidth(1, 100, 99) == 99);
+ assertTrue(ImageUtil.getWidth(1, 100, 98) == 98);
+ assertTrue(ImageUtil.getWidth(1, 100, 100) == 100);
+ assertTrue(ImageUtil.getWidth(1, 99, 100) == 99);
+ assertTrue(ImageUtil.getWidth(1, 77, 77) == 77);
+ assertTrue(ImageUtil.getWidth(1, 101, 100) == 100);
+ assertTrue(ImageUtil.getWidth(1, 149, 100) == 100);
+ assertTrue(ImageUtil.getWidth(1, 200, 300) == 200);
+ assertTrue(ImageUtil.getWidth(1, 11, 10) == 10);
+
+ assertTrue(ImageUtil.getWidth(0.5, 1000, 100) == 50);
+ assertTrue(ImageUtil.getWidth(0.5, 100, 1000) == 100);
+ assertTrue(ImageUtil.getWidth(0.5, 100, 99) == 49);
+ assertTrue(ImageUtil.getWidth(0.5, 100, 98) == 49);
+ assertTrue(ImageUtil.getWidth(0.5, 100, 100) == 50);
+ assertTrue(ImageUtil.getWidth(0.5, 99, 100) == 50);
+ assertTrue(ImageUtil.getWidth(0.5, 77, 77) == 38);
+ assertTrue(ImageUtil.getWidth(0.5, 101, 100) == 50);
+ assertTrue(ImageUtil.getWidth(0.5, 149, 100) == 50);
+ assertTrue(ImageUtil.getWidth(0.5, 200, 300) == 150);
+ assertTrue(ImageUtil.getWidth(0.5, 11, 10) == 5);
+
+ assertTrue(ImageUtil.getWidth(2, 1000, 100) == 200);
+ assertTrue(ImageUtil.getWidth(2, 100, 1000) == 100);
+ assertTrue(ImageUtil.getWidth(2, 100, 99) == 100);
+ assertTrue(ImageUtil.getWidth(2, 100, 50) == 100);
+ assertTrue(ImageUtil.getWidth(2, 100, 49) == 98);
+ assertTrue(ImageUtil.getWidth(2, 100, 98) == 100);
+ assertTrue(ImageUtil.getWidth(2, 100, 100) == 100);
+ assertTrue(ImageUtil.getWidth(2, 99, 100) == 99);
+ assertTrue(ImageUtil.getWidth(2, 77, 77) == 77);
+ assertTrue(ImageUtil.getWidth(2, 101, 100) == 101);
+ assertTrue(ImageUtil.getWidth(2, 149, 100) == 149);
+ assertTrue(ImageUtil.getWidth(2, 200, 300) == 200);
+ assertTrue(ImageUtil.getWidth(2, 11, 10) == 11);
+ }
+
+ @Test
+ public void testGetHeight() {
+ assertTrue(ImageUtil.getHeight(1, 1000, 100) == 100);
+ assertTrue(ImageUtil.getHeight(1, 100, 1000) == 100);
+ assertTrue(ImageUtil.getHeight(1, 100, 99) == 99);
+ assertTrue(ImageUtil.getHeight(1, 100, 98) == 98);
+ assertTrue(ImageUtil.getHeight(1, 100, 100) == 100);
+ assertTrue(ImageUtil.getHeight(1, 99, 100) == 99);
+ assertTrue(ImageUtil.getHeight(1, 77, 77) == 77);
+ assertTrue(ImageUtil.getHeight(1, 101, 100) == 100);
+ assertTrue(ImageUtil.getHeight(1, 149, 100) == 100);
+ assertTrue(ImageUtil.getHeight(1, 200, 300) == 200);
+ assertTrue(ImageUtil.getHeight(1, 11, 10) == 10);
+
+ assertTrue(ImageUtil.getHeight(0.5, 1000, 100) == 100);
+ assertTrue(ImageUtil.getHeight(0.5, 100, 1000) == 200);
+ assertTrue(ImageUtil.getHeight(0.5, 100, 99) == 99);
+ assertTrue(ImageUtil.getHeight(0.5, 100, 98) == 98);
+ assertTrue(ImageUtil.getHeight(0.5, 100, 100) == 100);
+ assertTrue(ImageUtil.getHeight(0.5, 99, 100) == 100);
+ assertTrue(ImageUtil.getHeight(0.5, 77, 77) == 77);
+ assertTrue(ImageUtil.getHeight(0.5, 101, 100) == 100);
+ assertTrue(ImageUtil.getHeight(0.5, 149, 100) == 100);
+ assertTrue(ImageUtil.getHeight(0.5, 200, 300) == 300);
+ assertTrue(ImageUtil.getHeight(0.5, 11, 10) == 10);
+
+ assertTrue(ImageUtil.getHeight(2, 1000, 100) == 100);
+ assertTrue(ImageUtil.getHeight(2, 100, 1000) == 50);
+ assertTrue(ImageUtil.getHeight(2, 100, 99) == 50);
+ assertTrue(ImageUtil.getHeight(2, 100, 50) == 50);
+ assertTrue(ImageUtil.getHeight(2, 100, 49) == 49);
+ assertTrue(ImageUtil.getHeight(2, 100, 98) == 50);
+ assertTrue(ImageUtil.getHeight(2, 100, 100) == 50);
+ assertTrue(ImageUtil.getHeight(2, 99, 100) == 49);
+ assertTrue(ImageUtil.getHeight(2, 77, 77) == 38);
+ assertTrue(ImageUtil.getHeight(2, 101, 100) == 50);
+ assertTrue(ImageUtil.getHeight(2, 149, 100) == 74);
+ assertTrue(ImageUtil.getHeight(2, 200, 300) == 100);
+ assertTrue(ImageUtil.getHeight(2, 11, 10) == 5);
+ }
+}
diff --git a/org.eclipse.tips.ui/.classpath b/org.eclipse.tips.ui/.classpath
new file mode 100644
index 000000000..eca7bdba8
--- /dev/null
+++ b/org.eclipse.tips.ui/.classpath
@@ -0,0 +1,7 @@
+<?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"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.tips.ui/.gitignore b/org.eclipse.tips.ui/.gitignore
new file mode 100644
index 000000000..0f630157f
--- /dev/null
+++ b/org.eclipse.tips.ui/.gitignore
@@ -0,0 +1,2 @@
+/target/
+/bin/
diff --git a/org.eclipse.tips.ui/.project b/org.eclipse.tips.ui/.project
new file mode 100644
index 000000000..c758a1381
--- /dev/null
+++ b/org.eclipse.tips.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.tips.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>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.tips.ui/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.tips.ui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 000000000..0c68a61dc
--- /dev/null
+++ b/org.eclipse.tips.ui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/org.eclipse.tips.ui/META-INF/MANIFEST.MF b/org.eclipse.tips.ui/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..72e9b2379
--- /dev/null
+++ b/org.eclipse.tips.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,13 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Tip of the Day UI
+Bundle-SymbolicName: org.eclipse.tips.ui;singleton:=true
+Bundle-Version: 0.1.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.eclipse.jface;bundle-version="3.13.1",
+ org.eclipse.tips.core;bundle-version="0.1.0"
+Export-Package: org.eclipse.tips.ui,
+ org.eclipse.tips.ui.internal,
+ org.eclipse.tips.ui.internal.util
+Automatic-Module-Name: org.eclipse.tips.ui
diff --git a/org.eclipse.tips.ui/build.properties b/org.eclipse.tips.ui/build.properties
new file mode 100644
index 000000000..da4f2d034
--- /dev/null
+++ b/org.eclipse.tips.ui/build.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2018 Remain Software
+# 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:
+# wim.jongman@remainsoftware.com - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ icons/
diff --git a/org.eclipse.tips.ui/icons/48/aleft.png b/org.eclipse.tips.ui/icons/48/aleft.png
new file mode 100644
index 000000000..dee58a177
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/48/aleft.png
Binary files differ
diff --git a/org.eclipse.tips.ui/icons/48/aright.png b/org.eclipse.tips.ui/icons/48/aright.png
new file mode 100644
index 000000000..0954a79ba
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/48/aright.png
Binary files differ
diff --git a/org.eclipse.tips.ui/icons/64/aleft.png b/org.eclipse.tips.ui/icons/64/aleft.png
new file mode 100644
index 000000000..0ef5cdfd3
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/64/aleft.png
Binary files differ
diff --git a/org.eclipse.tips.ui/icons/64/aright.png b/org.eclipse.tips.ui/icons/64/aright.png
new file mode 100644
index 000000000..c7791cd9b
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/64/aright.png
Binary files differ
diff --git a/org.eclipse.tips.ui/icons/lightbulb.png b/org.eclipse.tips.ui/icons/lightbulb.png
new file mode 100644
index 000000000..d22fde8ba
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/lightbulb.png
Binary files differ
diff --git a/org.eclipse.tips.ui/icons/popup_menu.gif b/org.eclipse.tips.ui/icons/popup_menu.gif
new file mode 100644
index 000000000..bd37eb50b
--- /dev/null
+++ b/org.eclipse.tips.ui/icons/popup_menu.gif
Binary files differ
diff --git a/org.eclipse.tips.ui/pom.xml b/org.eclipse.tips.ui/pom.xml
new file mode 100644
index 000000000..c0c1b7126
--- /dev/null
+++ b/org.eclipse.tips.ui/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2018 Remain Software
+ 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:
+ wim.jongman@remainsoftware.com - initial API and implementation
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>eclipse.platform.ua</groupId>
+ <artifactId>eclipse.platform.ua</artifactId>
+ <version>4.8.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.eclipse.ui</groupId>
+ <artifactId>org.eclipse.tips.ui</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <packaging>eclipse-plugin</packaging>
+</project>
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/DefaultTipManager.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/DefaultTipManager.java
new file mode 100644
index 000000000..f32d420b0
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/DefaultTipManager.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.tips.core.TipManager;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.ui.internal.TipDialog;
+
+/**
+ * Class to manage the tip providers and start the tip of the day UI.
+ *
+ */
+@SuppressWarnings("restriction")
+public abstract class DefaultTipManager extends TipManager {
+
+ private TipDialog fTipDialog;
+
+ /**
+ * Opens the Tip of the Day dialog. Subclasses may override if they want to
+ * present the Tips in a different way, e.g. in a view.
+ *
+ * @param startUp
+ * When called from a startup situation, true must be passed for
+ * <code>pStartup</code>. If in a manual starting situation, false
+ * must be passed. This enables the manager to decide to skip opening
+ * the dialog at startup (e.g., no new tip items).
+ *
+ * @see #isOpen()
+ */
+ @Override
+ public TipManager open(boolean startUp) {
+ if (fOpen && (fTipDialog == null || fTipDialog.isDisposed())) {
+ fOpen = false;
+ }
+ try {
+ Assert.isTrue(!fOpen, "Tip of the Day already open.");
+ } catch (Exception e) {
+ log(LogUtil.error(getClass(), e));
+ throw e;
+ }
+
+ if (!mustOpen(startUp)) {
+ return this;
+ }
+
+ fTipDialog = new TipDialog(Display.getCurrent().getActiveShell(), this, TipDialog.DEFAULT_STYLE);
+ fTipDialog.addDisposeListener(pE -> {
+ try {
+ dispose();
+ } finally {
+ fOpen = false;
+ }
+ });
+ fTipDialog.open();
+ fOpen = true;
+ return this;
+ }
+
+ // Open if not a startup call or if there are unread tips.
+ private boolean mustOpen(boolean startUp) {
+ if (!startUp) {
+ return true;
+ }
+ if (startUp && isRunAtStartup()) {
+ for (TipProvider provider : getProviders()) {
+ if (provider.isReady() && !provider.getTips(true).isEmpty()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/ISwtTip.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/ISwtTip.java
new file mode 100644
index 000000000..46424fc4c
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/ISwtTip.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.tips.core.Tip;
+
+/**
+ * A decoration of {@link Tip} class that provides the opportunity to
+ * create a rich SWT UI.
+ *
+ */
+public interface ISwtTip {
+
+ /**
+ * Creates the top level control for this tip on the given parent composite. You
+ * can implement this method to build your the UI for your Tip in SWT. You can
+ * find and example implementation in the tips example bundle.
+ *
+ * @param parent
+ * the parent composite
+ */
+ public abstract void createControl(Composite parent);
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/ProviderSelectionListener.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/ProviderSelectionListener.java
new file mode 100644
index 000000000..4e0d58e92
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/ProviderSelectionListener.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal;
+
+import org.eclipse.swt.widgets.Slider;
+import org.eclipse.tips.core.TipProvider;
+
+/**
+ * Interface for TipProvider listeners.
+ */
+@FunctionalInterface
+public interface ProviderSelectionListener {
+
+ /**
+ * Is called when the provider is selected in the {@link Slider}.
+ *
+ * @param provider
+ * the {@link TipProvider} that was selected
+ */
+ public void selected(TipProvider provider);
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/Slider.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/Slider.java
new file mode 100644
index 000000000..02f8fdb8b
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/Slider.java
@@ -0,0 +1,406 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipManager;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.TipProviderListener;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.ui.internal.util.ImageUtil;
+import org.eclipse.tips.ui.internal.util.ResourceManager;
+import org.eclipse.tips.ui.internal.util.SWTResourceManager;
+
+@SuppressWarnings("restriction")
+public class Slider extends Composite {
+
+ private Composite fScroller;
+ private TipProvider fSelectedProvider;
+ private int fSpacing = 5;
+ private int fSliderIndex = 0;
+ private List<ProviderSelectionListener> fListeners = new ArrayList<>();
+ private TipManager fTipManager;
+ private Button fLeftButton;
+ private Button fRightButton;
+ private TipProviderListener fProviderListener;
+ private Composite fSelectedProviderButton;
+ private HashMap<String, Image> fProviderImageCache = new HashMap<>();
+ private int fIconSize = 48;
+
+ /**
+ * Constructor for the Slider widget.
+ *
+ * @param parent
+ * the parent
+ * @param style
+ * the SWT style bits
+ */
+ public Slider(Composite parent, int style) {
+ super(parent, style);
+
+ GridLayout layout = new GridLayout(3, false);
+ layout.marginHeight = 0;
+ setLayout(layout);
+
+ fLeftButton = new Button(this, SWT.FLAT);
+ GridData gd_leftButton = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
+ gd_leftButton.widthHint = fIconSize / 2 + 8;
+ gd_leftButton.heightHint = fIconSize;
+ fLeftButton.setLayoutData(gd_leftButton);
+ fLeftButton.setImage(getImage("icons/" + fIconSize + "/aleft.png"));
+ fLeftButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ scrollLeft();
+ }
+ });
+
+ fScroller = new Composite(this, SWT.DOUBLE_BUFFERED);
+ GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
+ layoutData.heightHint = fIconSize + 4;
+ fScroller.setLayoutData(layoutData);
+
+ fRightButton = new Button(this, SWT.FLAT);
+ GridData gd_rightButton = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1);
+ gd_rightButton.widthHint = fIconSize / 2 + 8;
+ gd_rightButton.heightHint = fIconSize;
+ fRightButton.setLayoutData(gd_rightButton);
+ fRightButton.setImage(getImage("icons/" + fIconSize + "/aright.png"));
+ fRightButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ scrollRight();
+ }
+ });
+
+ setupDisposeListener();
+ setupProviderListener();
+ }
+
+ private void setupDisposeListener() {
+ addListener(SWT.Dispose, event -> {
+ fTipManager.getListenerManager().removeProviderListener(fProviderListener);
+ fProviderImageCache.values().forEach(img -> {
+ if (!img.isDisposed()) {
+ img.dispose();
+ }
+ });
+ });
+ }
+
+ private void setupProviderListener() {
+ fProviderListener = provider -> getDisplay().asyncExec(() -> load());
+ }
+
+ private static Image getImage(String icon) {
+ return ResourceManager.getPluginImage("org.eclipse.tips.ui", icon);
+ }
+
+ /**
+ * Loads or reloads the provider list. If you want to update the read count of
+ * the current button or all buttons then check the {@link #updateButton()} and
+ * {@link #updateButtons()} methods.
+ *
+ */
+ public void load() {
+ if (isDisposed() || fScroller.isDisposed()) {
+ return;
+ }
+ Arrays.stream(fScroller.getChildren()).filter(control -> !control.isDisposed())
+ .forEach(control -> control.dispose());
+ List<TipProvider> providers = fTipManager.getProviders();
+ int spaceCount = Math.floorDiv(fScroller.getBounds().width, (fIconSize + fSpacing));
+ int providerCount = providers.size();
+
+ if (fSliderIndex > 0 && spaceCount >= providerCount) {
+ fSliderIndex = 0;
+ }
+
+ if (spaceCount >= providerCount) {
+ if (fRightButton.isEnabled()) {
+ fRightButton.setEnabled(false);
+ fLeftButton.setEnabled(false);
+ fLeftButton.setImage(getImage("icons/" + fIconSize + "/aright.png"));
+ fRightButton.setImage(getImage("icons/" + fIconSize + "/aleft.png"));
+ }
+ } else {
+ if (!fRightButton.isEnabled()) {
+ fRightButton.setEnabled(true);
+ fLeftButton.setEnabled(true);
+ fLeftButton.setImage(getImage("icons/" + fIconSize + "/aleft.png"));
+ fRightButton.setImage(getImage("icons/" + fIconSize + "/aright.png"));
+ }
+ }
+
+ int emptyPixelsLeft = Math.floorMod(fScroller.getBounds().width, (fIconSize + fSpacing));
+ int newSpacing = fSpacing + (emptyPixelsLeft / (spaceCount + 1));
+ for (int i = 0; i < Math.min(providerCount - fSliderIndex, spaceCount); i++) {
+ TipProvider provider = providers.get(i + fSliderIndex);
+ if (fSelectedProvider == null && !provider.getTips(true).isEmpty()) {
+ fSelectedProvider = provider;
+ notifyListeners(fSelectedProvider);
+ }
+ createProviderButton(providers.get(i + fSliderIndex), newSpacing, i);
+ }
+ }
+
+ private Composite createProviderButton(TipProvider provider, int spacing, int index) {
+ Composite button = new Composite(fScroller, SWT.DOUBLE_BUFFERED);
+ button.setToolTipText(provider.getDescription());
+ button.setBackground(fScroller.getBackground());
+ button.setSize(fIconSize + 4, fIconSize + 4);
+ button.setLocation((index * (fIconSize + spacing) + spacing - fSpacing), 2);
+ button.addPaintListener(e -> {
+ if (fSelectedProvider == provider) {
+ fSelectedProviderButton = button;
+ }
+ paintButton(e.gc, button, provider);
+ });
+ button.addListener(SWT.MouseEnter, event -> button.redraw());
+ button.addListener(SWT.MouseExit, event -> button.redraw());
+ button.addListener(SWT.MouseUp, event -> {
+ if (fSelectedProvider == provider) {
+ return;
+ }
+ fSelectedProvider = provider;
+ if (fSelectedProviderButton != null && !fSelectedProviderButton.isDisposed()) {
+ fSelectedProviderButton.redraw();
+ }
+ fSelectedProviderButton = button;
+ fSelectedProviderButton.redraw();
+ notifyListeners(provider);
+ });
+ if (fSelectedProvider == provider) {
+ fSelectedProviderButton = button;
+ }
+ return button;
+ }
+
+ /**
+ * Updates the read count of the currently selected button.
+ */
+ public void updateButton() {
+ if (fSelectedProviderButton != null && !fSelectedProviderButton.isDisposed()) {
+ getDisplay().asyncExec(() -> fSelectedProviderButton.redraw());
+ }
+ }
+
+ /**
+ * Calls redraw on all buttons to update the badge counter.
+ */
+ public void updateButtons() {
+ if (!isDisposed()) {
+ getDisplay().asyncExec(() -> {
+ if (!fScroller.isDisposed()) {
+ for (Control control : fScroller.getChildren()) {
+ if (control instanceof Composite && !control.isDisposed()) {
+ control.redraw();
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Sets the {@link TipManager}.
+ *
+ * @param tipManager
+ * the {@link TipManager}
+ * @return this
+ */
+ public Slider setTipManager(TipManager tipManager) {
+ fTipManager = tipManager;
+ fTipManager.getListenerManager().addProviderListener(fProviderListener);
+ fIconSize = 48;
+ load();
+ return this;
+ }
+
+ private void notifyListeners(TipProvider provider) {
+ fListeners.forEach(listener -> {
+ try {
+ listener.selected(provider);
+ } catch (Exception e) {
+ fTipManager.log(LogUtil.error(getClass(), e));
+ }
+ });
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ Point point = new Point(fIconSize * 3, fIconSize + fSpacing + fSpacing);
+ return point;
+ }
+
+ protected void scrollRight() {
+ if (fSliderIndex < fTipManager.getProviders().size() - 1) {
+ fSliderIndex++;
+ load();
+ }
+ }
+
+ protected void scrollLeft() {
+ if (fSliderIndex > 0) {
+ fSliderIndex--;
+ load();
+ }
+ }
+
+ /**
+ * @return the current selected {@link TipProvider}.
+ */
+ public TipProvider getTipProvider() {
+ return fSelectedProvider;
+ }
+
+ /**
+ * Sets the passed provider as the selected provider in the slider.
+ *
+ * @param provider
+ * the new provider for the slider.
+ *
+ * @return this
+ */
+ public Slider setTipProvider(TipProvider provider) {
+ fSelectedProvider = provider;
+ updateButtons();
+ return this;
+ }
+
+ /**
+ * Adds the listener to the list of listeners to be called when an event with a
+ * {@link TipProvider} occurs.
+ *
+ * @param listener
+ * the {@link ProviderSelectionListener}
+ * @return this
+ */
+ public Slider addTipProviderListener(ProviderSelectionListener listener) {
+ fListeners.add(listener);
+ return this;
+ }
+
+ /**
+ * Removes the listener from the list.
+ *
+ * @param listener
+ * the {@link ProviderSelectionListener}
+ * @return this
+ */
+ public Slider removeTipProviderListener(ProviderSelectionListener listener) {
+ fListeners.remove(listener);
+ return this;
+ }
+
+ private void paintButton(GC gc, Composite providerButton, TipProvider provider) {
+ gc.setAdvanced(true);
+ if (fSelectedProvider.equals(provider)) {
+ gc.drawRectangle(0, 0, fIconSize + 3, fIconSize + 3);
+ } else {
+ boolean mouseIn = getDisplay().getCursorControl() == providerButton;
+ if (mouseIn) {
+ gc.drawRectangle(0, 0, fIconSize + 3, fIconSize + 3);
+ } else {
+ gc.setBackground(fScroller.getBackground());
+ }
+ }
+ gc.fillRectangle(2, 2, fIconSize, fIconSize);
+ Image overlay = getUnreadOverlay(providerButton, provider);
+ gc.drawImage(overlay, 2, 2);
+ if (overlay != getProviderImage(provider, selectProviderImage(provider))) {
+ overlay.dispose();
+ }
+ }
+
+ private TipImage selectProviderImage(TipProvider provider) {
+ return provider.getImage();
+ }
+
+ private Image getProviderImage(TipProvider provider, TipImage image) {
+ if (!fProviderImageCache.containsKey(provider.getID())) {
+ try {
+ fProviderImageCache.put(provider.getID(),
+ new Image(getDisplay(), ImageUtil.decodeToImage(image.getBase64Image())));
+ } catch (Exception e) {
+ fTipManager.log(LogUtil.error(getClass(), e));
+ return null;
+ }
+ }
+ return fProviderImageCache.get(provider.getID());
+ }
+
+ private Image getUnreadOverlay(Composite providerButton, TipProvider provider) {
+ if (provider.getTips(true).isEmpty()) {
+ return getProviderImage(provider, selectProviderImage(provider));
+ }
+ GC gc2 = new GC(providerButton);
+ gc2.setAdvanced(true);
+ gc2.setFont(SWTResourceManager.getBoldFont(gc2.getFont()));
+ int tipCount = provider.getTips(true).size();
+ Point textExtent = gc2.textExtent(tipCount + "");
+ gc2.dispose();
+
+ Image image = null;
+ if (tipCount > 9) {
+ image = new Image(getDisplay(), textExtent.x + 8, textExtent.y + 5);
+ } else {
+ image = new Image(getDisplay(), textExtent.x + 15, textExtent.y + 5);
+ }
+ ImageData data = image.getImageData();
+ data.transparentPixel = data.getPixel(0, 0);
+ image.dispose();
+ image = new Image(getDisplay(), data);
+ GC gc = new GC(image);
+ gc.setAdvanced(true);
+// if (fTipManager.mustServeReadTips()) {
+// gc.setBackground(theme.getColor(TipTheme.COLOR_BADGE_TIPCOUNT_BACKGROUND));
+// } else {
+// gc.setBackground(theme.getColor(TipTheme.COLOR_BADGE_UNREAD_BACKGROUND));
+// }
+ gc.setAlpha(210);
+ gc.setTextAntialias(SWT.ON);
+// if (fTipManager.mustServeReadTips()) {
+// gc.setForeground(theme.getColor(TipTheme.COLOR_BADGE_TIPCOUNT_FOREGROUND));
+// } else {
+// gc.setForeground(theme.getColor(TipTheme.COLOR_BADGE_UNREAD_FOREGROUND));
+// }
+ if (tipCount > 9) {
+ gc.fillOval(0, 0, textExtent.x + 8, textExtent.y + 5);
+ gc.drawText(tipCount + "", 4, 2, true);
+ } else {
+ gc.fillOval(0, 0, textExtent.x + 15, textExtent.y + 5);
+ gc.drawText(tipCount + "", 8, 2, true);
+ }
+ Image result = ResourceManager.decorateImage(getProviderImage(provider, selectProviderImage(provider)), image,
+ SWTResourceManager.TOP_RIGHT);
+ image.dispose();
+ gc.dispose();
+ return result;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipComposite.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipComposite.java
new file mode 100644
index 000000000..2262da568
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipComposite.java
@@ -0,0 +1,580 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.custom.StackLayout;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.internal.DPIUtil;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.tips.core.IHtmlTip;
+import org.eclipse.tips.core.IUrlTip;
+import org.eclipse.tips.core.Tip;
+import org.eclipse.tips.core.TipAction;
+import org.eclipse.tips.core.TipImage;
+import org.eclipse.tips.core.TipManager;
+import org.eclipse.tips.core.TipProvider;
+import org.eclipse.tips.core.internal.LogUtil;
+import org.eclipse.tips.ui.ISwtTip;
+import org.eclipse.tips.ui.internal.util.ImageUtil;
+import org.eclipse.tips.ui.internal.util.ResourceManager;
+
+@SuppressWarnings("restriction")
+public class TipComposite extends Composite implements ProviderSelectionListener {
+ private static final int READ_TIMER = 2000;
+ private TipProvider fProvider;
+ private Browser fBrowser;
+ private Slider fSlider;
+ private TipManager fTipManager;
+ private Tip fCurrentTip;
+ private Button fShowAtStart;
+ private Button fUnreadOnly;
+ private Button fPreviousTipButton;
+ private Pattern fGtkHackPattern = Pattern.compile("(.*?)([0-9]+)(.*?)([0-9]+)(.*?)");
+ private Composite fSWTComposite;
+ private Composite fBrowserComposite;
+ private StackLayout fContentStack;
+ private Button fMultiActionMenuButton;
+ private Composite fNavigationBar;
+ private StackLayout fActionStack;
+ private Composite fEmptyActionComposite;
+ private Composite fSingleActionComposite;
+ private Composite fMultiActionComposite;
+ private Button fSingleActionButton;
+ private Button fMultiActionButton;
+ private Composite fContentComposite;
+ private List<Image> fActionImages = new ArrayList<>();
+ private Menu fActionMenu;
+
+ /**
+ * Constructor.
+ *
+ * @param parent
+ * the parent
+ * @param style
+ * the style
+ */
+ public TipComposite(Composite parent, int style) {
+ super(parent, style);
+ GridLayout gridLayout_1 = new GridLayout(1, false);
+ gridLayout_1.marginWidth = 2;
+ gridLayout_1.marginHeight = 2;
+ setLayout(gridLayout_1);
+
+ fContentComposite = new Composite(this, SWT.NONE);
+ fContentStack = new StackLayout();
+ fContentComposite.setLayout(fContentStack);
+ GridData gd_gridComposite = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
+ gd_gridComposite.widthHint = 900;
+ gd_gridComposite.heightHint = 600;
+ // gd_gridComposite.minimumWidth = -1;
+ // gd_gridComposite.minimumHeight = -1;
+ fContentComposite.setLayoutData(gd_gridComposite);
+
+ fBrowserComposite = new Composite(fContentComposite, SWT.NONE);
+ fBrowserComposite.setLayout(new FillLayout(SWT.HORIZONTAL));
+
+ fBrowser = new Browser(fBrowserComposite, SWT.NONE);
+ fBrowser.setJavascriptEnabled(true);
+
+ fSWTComposite = new Composite(fContentComposite, SWT.NONE);
+ fSWTComposite.setLayout(new FillLayout(SWT.HORIZONTAL));
+
+ fNavigationBar = new Composite(this, SWT.NONE);
+ fNavigationBar.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ GridLayout gl_NavigationBar = new GridLayout(2, false);
+ gl_NavigationBar.horizontalSpacing = 0;
+ gl_NavigationBar.marginHeight = 0;
+ gl_NavigationBar.verticalSpacing = 0;
+ gl_NavigationBar.marginWidth = 0;
+ fNavigationBar.setLayout(gl_NavigationBar);
+
+ Composite preferenceBar = new Composite(fNavigationBar, SWT.NONE);
+ FillLayout fl_composite_3 = new FillLayout(SWT.HORIZONTAL);
+ fl_composite_3.marginWidth = 5;
+ fl_composite_3.spacing = 5;
+ preferenceBar.setLayout(fl_composite_3);
+
+ fShowAtStart = new Button(preferenceBar, SWT.CHECK);
+ fShowAtStart.setText("Show tips at startup");
+ fShowAtStart.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ fTipManager.setRunAtStartup(fShowAtStart.getSelection());
+ }
+ });
+
+ fUnreadOnly = new Button(preferenceBar, SWT.CHECK);
+ fUnreadOnly.setText("Unread only");
+ fUnreadOnly.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ fTipManager.setServeReadTips(!fUnreadOnly.getSelection());
+ fPreviousTipButton.setEnabled(fTipManager.mustServeReadTips());
+ fSlider.load();
+ getNextTip();
+ }
+ });
+
+ Composite buttonBar = new Composite(fNavigationBar, SWT.NONE);
+ buttonBar.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1));
+ GridLayout gl_buttonBar = new GridLayout(4, true);
+ gl_buttonBar.marginHeight = 0;
+ buttonBar.setLayout(gl_buttonBar);
+
+ Composite actionComposite = new Composite(buttonBar, SWT.NONE);
+ fActionStack = new StackLayout();
+ actionComposite.setLayout(fActionStack);
+ actionComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+
+ fSingleActionComposite = new Composite(actionComposite, SWT.NONE);
+ GridLayout gl_SingleActionComposite = new GridLayout(1, false);
+ gl_SingleActionComposite.marginWidth = 0;
+ fSingleActionComposite.setLayout(gl_SingleActionComposite);
+
+ fSingleActionButton = new Button(fSingleActionComposite, SWT.NONE);
+ fSingleActionButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
+ fSingleActionButton.setText("More...");
+ fSingleActionButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ runTipAction(fCurrentTip.getActions().get(0));
+ }
+ });
+
+ fMultiActionComposite = new Composite(actionComposite, SWT.NONE);
+ GridLayout gl_MultiActionComposite = new GridLayout(2, false);
+ gl_MultiActionComposite.marginWidth = 0;
+ gl_MultiActionComposite.verticalSpacing = 0;
+ gl_MultiActionComposite.horizontalSpacing = 0;
+ fMultiActionComposite.setLayout(gl_MultiActionComposite);
+
+ fMultiActionButton = new Button(fMultiActionComposite, SWT.NONE);
+ fMultiActionButton.setText("New Button");
+ fMultiActionButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ runTipAction(fCurrentTip.getActions().get(0));
+ }
+ });
+
+ fMultiActionMenuButton = new Button(fMultiActionComposite, SWT.NONE);
+ fMultiActionMenuButton.setImage(ResourceManager.getPluginImage("org.eclipse.tips.ui", "icons/popup_menu.gif"));
+ fMultiActionMenuButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ showActionMenu();
+ }
+ });
+
+ fEmptyActionComposite = new Composite(actionComposite, SWT.NONE);
+
+ fPreviousTipButton = new Button(buttonBar, SWT.NONE);
+ fPreviousTipButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ fPreviousTipButton.setText("Previous Tip");
+ fPreviousTipButton.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ getPreviousTip();
+ }
+ });
+ fPreviousTipButton.setEnabled(false);
+
+ Button btnNextTip = new Button(buttonBar, SWT.NONE);
+ btnNextTip.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ btnNextTip.setText("Next Tip");
+ btnNextTip.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ getNextTip();
+ }
+ });
+
+ Button btnClose = new Button(buttonBar, SWT.NONE);
+ btnClose.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ getParent().dispose();
+ }
+ });
+ btnClose.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+ btnClose.setText("Close");
+
+ Label label_1 = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL);
+ label_1.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
+
+ fSlider = new Slider(this, SWT.NONE);
+ GridLayout gridLayout = (GridLayout) fSlider.getLayout();
+ gridLayout.verticalSpacing = 0;
+ gridLayout.marginWidth = 0;
+ gridLayout.horizontalSpacing = 0;
+ fSlider.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1));
+ fContentStack.topControl = fBrowserComposite;
+ fSlider.addTipProviderListener(this);
+ }
+
+ private void showActionMenu() {
+ Rectangle rect = fMultiActionButton.getBounds();
+ Point pt = new Point(rect.x - 1, rect.y + rect.height);
+ pt = fMultiActionButton.toDisplay(pt);
+ fActionMenu.setLocation(pt.x, pt.y);
+ fActionMenu.setVisible(true);
+ }
+
+ private void runTipAction(TipAction tipAction) {
+ Job job = new Job("Running " + tipAction.getTooltip()) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ tipAction.getRunner().run();
+ } catch (Exception e) {
+ IStatus status = LogUtil.error(getClass(), e);
+ fTipManager.log(status);
+ return status;
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ job.setUser(true);
+ job.schedule();
+ }
+
+ /**
+ * Sets the selected provider.
+ *
+ * @param provider
+ * the {@link TipProvider}
+ */
+ public void setProvider(TipProvider provider) {
+ fProvider = provider;
+ fSlider.setTipProvider(provider);
+ getCurrentTip();
+ }
+
+ /**
+ * Schedules a TimerTask that is executed after {@value #READ_TIMER}
+ * milliseconds after which the tip is marked as read.
+ */
+ private void hitTimer() {
+ Tip timerTip = fCurrentTip;
+ Timer timer = new Timer("Tip read timer");
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ if (timerTip == fCurrentTip) {
+ fTipManager.setAsRead(timerTip);
+ fSlider.updateButtons();
+ }
+ timer.cancel();
+ }
+ }, READ_TIMER);
+ }
+
+ private void getPreviousTip() {
+ processTip(fProvider.getPreviousTip());
+ }
+
+ private void getNextTip() {
+ if (fProvider.getTips(true).isEmpty() && !fTipManager.getProviders().isEmpty()) {
+ fProvider.getNextTip(); // advance current tip
+ for (TipProvider provider : fTipManager.getProviders()) {
+ if (!provider.getTips(true).isEmpty()) {
+ setProvider(provider);
+ break;
+ }
+ }
+ }
+ processTip(fProvider.getNextTip());
+ }
+
+ private void getCurrentTip() {
+ processTip(fProvider.getCurrentTip());
+ }
+
+ private void processTip(Tip tip) {
+ fCurrentTip = tip;
+ hitTimer();
+ enableActionButtons(tip);
+ prepareForHTML();
+ loadContent(tip);
+ }
+
+ private void loadContent(Tip tip) {
+ if (tip instanceof ISwtTip) {
+ loadContentSWT(tip);
+ } else if (tip instanceof IHtmlTip) {
+ loadContentHtml((IHtmlTip) tip);
+ } else if (tip instanceof IUrlTip) {
+ loadContentUrl((IUrlTip) tip);
+ } else {
+ fTipManager.log(LogUtil.error(getClass(), "Unknown Tip implementation: " + tip)) ;
+ }
+ fContentComposite.requestLayout();
+ }
+
+ private void loadContentHtml(IHtmlTip tip) {
+ try {
+ fBrowser.setText(getScaling() + getHTML(tip).trim());
+ } catch (IOException e) {
+ fTipManager.log(LogUtil.error(getClass(), e));
+ }
+ }
+
+ private void loadContentUrl(IUrlTip tip) {
+ try {
+ fBrowser.setUrl(FileLocator.resolve(tip.getURL()).toString());
+ } catch (IOException e) {
+ fTipManager.log(LogUtil.error(getClass(), e));
+ }
+ }
+
+ private void loadContentSWT(Tip tip) {
+ for (Control control : fSWTComposite.getChildren()) {
+ control.dispose();
+ }
+ fContentStack.topControl = fSWTComposite;
+ ((ISwtTip) tip).createControl(fSWTComposite);
+ fSWTComposite.requestLayout();
+ }
+
+ private void prepareForHTML() {
+ fContentStack.topControl = fBrowserComposite;
+ loadTimeOutScript();
+ fBrowserComposite.requestLayout();
+ }
+
+ /**
+ * Sets content in the browser that displays a message after 500ms if the Tip
+ * could not load fast enough.
+ */
+ private void loadTimeOutScript() {
+ fBrowser.setText(getScaling() + getLoadingScript(500));
+ while (!getShell().isDisposed()) {
+ if (!getDisplay().readAndDispatch()) {
+ break;
+ }
+ }
+ }
+
+ private void enableActionButtons(Tip tip) {
+ disposeActionImages();
+ if (tip.getActions().isEmpty()) {
+ fActionStack.topControl = fEmptyActionComposite;
+ } else if (tip.getActions().size() == 1) {
+ TipAction action = tip.getActions().get(0);
+ fActionStack.topControl = fSingleActionComposite;
+ fSingleActionButton.setImage(getActionImage(action.getTipImage()));
+ fSingleActionButton.setText(action.getText());
+ fSingleActionButton.setToolTipText(action.getTooltip());
+ blinkActionComposite(fSingleActionComposite);
+ } else {
+ TipAction action = tip.getActions().get(0);
+ fActionStack.topControl = fMultiActionComposite;
+ fMultiActionButton.setImage(getActionImage(tip.getActions().get(0).getTipImage()));
+ fMultiActionButton.setText(action.getText());
+ fMultiActionButton.setToolTipText(action.getTooltip());
+ loadActionMenu(tip);
+ blinkActionComposite(fMultiActionComposite);
+ }
+ fEmptyActionComposite.getParent().requestLayout();
+ fNavigationBar.requestLayout();
+ }
+
+ private void disposeActionImages() {
+ fActionImages.forEach(img -> img.dispose());
+ }
+
+ private void loadActionMenu(Tip pTip) {
+ if (fActionMenu != null) {
+ fActionMenu.dispose();
+ }
+ fActionMenu = new Menu(fContentComposite.getShell(), SWT.POP_UP);
+ pTip.getActions().subList(1, pTip.getActions().size()).forEach(action -> {
+ MenuItem item = new MenuItem(fActionMenu, SWT.PUSH);
+ item.setText(action.getText());
+ item.setToolTipText(action.getTooltip());
+ item.setText(action.getText());
+ item.setImage(getActionImage(action.getTipImage()));
+ item.addListener(SWT.Selection, e -> runTipAction(action));
+ });
+ }
+
+ private void blinkActionComposite(Composite composite) {
+ Color bg = composite.getParent().getBackground();
+ Color red = getDisplay().getSystemColor(SWT.COLOR_RED);
+ boolean flip = false;
+ for (int i = 1; i < 6; i++) {
+ boolean flop = flip;
+ if (!composite.isDisposed()) {
+ getDisplay().timerExec(i * 200, () -> {
+ if (!composite.isDisposed()) {
+ composite.getParent().setBackground(flop ? bg : red);
+ }
+ });
+ }
+ flip = !flip;
+ }
+ }
+
+ private Image getActionImage(TipImage tipImage) {
+ if (tipImage == null) {
+ return null;
+ }
+ try {
+ Image image = new Image(getDisplay(), ImageUtil.decodeToImage(tipImage.getBase64Image()));
+ if (image != null) {
+ fActionImages.add(image);
+ return image;
+ }
+ } catch (IOException e) {
+ fTipManager.log(LogUtil.error(getClass(), e));
+ }
+ return null;
+ }
+
+ /**
+ * Get the timeout script in case the tips takes too to load.
+ *
+ * @param timeout
+ * the timeout in milliseconds
+ * @return the script
+ */
+ private static String getLoadingScript(int timeout) {
+ return "<style>div{position:fixed;top:50%;left:40%}</style>" + "<div id=\"txt\"></div>"
+ + "<script>var wss=function(){document.getElementById(\"txt\").innerHTML=\"Loading next Tip...\"};window.setTimeout(wss,"
+ + timeout + ");</script>";
+ }
+
+ private String getHTML(IHtmlTip tip) throws IOException {
+ String encodedImage = encodeImage(tip);
+ return tip.getHTML() + encodedImage;
+ }
+
+ private static String getScaling() {
+ if (Platform.isRunning() && Platform.getWS().startsWith("gtk")) {
+ int deviceZoom = DPIUtil.getDeviceZoom();
+ int zoom = deviceZoom;
+ return "<style>" + "body {" + " zoom: " + zoom + "%;" + "}</style> ";
+ }
+ return "";
+ }
+
+ private String encodeImage(IHtmlTip tip) throws IOException {
+ TipImage image = tip.getImage();
+ if (image == null) {
+ return "";
+ }
+ return encodeImageFromBase64(image);
+ }
+
+ private String encodeImageFromBase64(TipImage image) throws IOException {
+ int width = fBrowser.getClientArea().width;
+ int height = Math.min(fBrowser.getClientArea().height / 2, (2 * (width / 3)));
+ String attributes = gtkHack(image.getIMGAttributes(width, height).trim());
+ String encoded = "" //
+ + "<center> <img " //
+ + attributes //
+ + " src=\"" //
+ + image.getBase64Image() //
+ + "\"></center><br/>";
+ return encoded;
+ }
+
+ private String gtkHack(String imageAttribute) {
+ if (!Platform.isRunning()) {
+ return imageAttribute;
+ }
+ if (!Platform.getWS().startsWith("gtk")) {
+ return imageAttribute;
+ }
+ Matcher m = fGtkHackPattern.matcher(imageAttribute);
+ if (!m.matches()) {
+ return imageAttribute;
+ }
+ return m.group(1) + (Integer.parseInt(m.group(2)) * 120 / 100) + m.group(3)
+ + (Integer.parseInt(m.group(4)) * 120 / 100) + m.group(5);
+ }
+
+ @Override
+ protected void checkSubclass() {
+ }
+
+ /**
+ * @return the {@link Browser} widget
+ */
+ public Browser getBrowser() {
+ return fBrowser;
+ }
+
+ /**
+ * @return the {@link Slider} widget
+ */
+ public Slider getSlider() {
+ return fSlider;
+ }
+
+ @Override
+ public void selected(TipProvider provider) {
+ setProvider(provider);
+ }
+
+ /**
+ * Sets the {@link TipManager}
+ *
+ * @param tipManager
+ * the {@link TipManager} that opened the dialog.
+ */
+ public void setTipManager(TipManager tipManager) {
+ fTipManager = tipManager;
+
+ getDisplay().syncExec(() -> {
+ fSlider.setTipManager(fTipManager);
+ fShowAtStart.setSelection(fTipManager.isRunAtStartup());
+ fUnreadOnly.setSelection(!fTipManager.mustServeReadTips());
+ fPreviousTipButton.setEnabled(fTipManager.mustServeReadTips());
+ });
+ }
+
+ @Override
+ public void dispose() {
+ disposeActionImages();
+ super.dispose();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipDialog.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipDialog.java
new file mode 100644
index 000000000..a31398a8d
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/TipDialog.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.tips.core.TipManager;
+
+public class TipDialog extends Shell {
+
+ /**
+ * When passed as style, the default style will be used.
+ */
+ public static final int DEFAULT_STYLE = -1;
+ private TipManager fTipManager;
+ private TipComposite fTipComposite;
+
+ /**
+ * Creates the Tip Dialog.
+ *
+ * @param display
+ * the {@link Display}
+ * @param tipManager
+ * the {@link TipManager}
+ * @param style
+ * the {@link Shell} style or {@link #DEFAULT_STYLE} for
+ * <code>SWT.DIALOG_TRIM | SWT.RESIZE | SWT.CLOSE</code>
+ */
+ public TipDialog(Shell display, TipManager tipManager, int style) {
+ super(display, style == DEFAULT_STYLE ? SWT.DIALOG_TRIM | SWT.RESIZE | SWT.CLOSE : style);
+ fTipManager = tipManager;
+ setLayout(new FillLayout(SWT.HORIZONTAL));
+ fTipComposite = new TipComposite(this, SWT.NONE);
+ pack();
+ setLocation(getShell().getMonitor().getClientArea().width / 2 - getSize().x / 2,
+ getShell().getMonitor().getClientArea().height / 2 - getSize().y / 2);
+ setText("Tip of the Day");
+ }
+
+ @Override
+ public void open() {
+ super.open();
+ fTipComposite.setTipManager(fTipManager);
+ }
+
+ @Override
+ protected void checkSubclass() {
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/package-info.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/package-info.java
new file mode 100644
index 000000000..ba28b4609
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/package-info.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Internal classes of the SWT Tip Dialog. You can use these classes as an
+ * example on how to build your own Tip Dialog.
+ *
+ */
+package org.eclipse.tips.ui.internal; \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/DateUtil.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/DateUtil.java
new file mode 100644
index 000000000..46ff7d643
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/DateUtil.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Date utilities.
+ *
+ */
+public class DateUtil {
+
+ /**
+ * Convenience method that creates a date from a dd/mm/yy string.
+ *
+ * @param dateYYMMDD
+ * the date in a dd/mm/yy format, e.g. "01/01/2017"
+ * @return the date
+ * @throws RuntimeException
+ * if the date is not correct
+ */
+ public static Date getDateFromYYMMDD(String dateYYMMDD) {
+ SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
+ try {
+ return sdf.parse(dateYYMMDD);
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ImageUtil.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ImageUtil.java
new file mode 100644
index 000000000..7598d0956
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ImageUtil.java
@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Base64;
+import java.util.Base64.Decoder;
+import java.util.Base64.Encoder;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+
+/**
+ * Image helper class.
+ *
+ */
+public class ImageUtil {
+
+ private static final String COMMA = ",";
+
+ /**
+ * Calculate a height that will fit in the proposed rectangle given the passed
+ * aspect ratio.
+ *
+ * @param aspectRatio
+ * the aspect ration, e.g. 1.5 for a 3/2 ratio (width/height).
+ * @param widthHint
+ * the available width in the viewport
+ * @param heightHint
+ * the available height in the viewport
+ * @return the maximum height that will fit in the passed rectangle.
+ */
+ public static int getHeight(double aspectRatio, int widthHint, int heightHint) {
+ return (int) Math.min(heightHint, widthHint / aspectRatio);
+ }
+
+ /**
+ * Calculate a width that will fit in the proposed rectangle given the passed
+ * aspect ratio.
+ *
+ * @param aspectRatio
+ * the aspect ration, e.g. 1.5 for a 3/2 ratio (width/height).
+ * @param widthHint
+ * the available width in the viewport
+ * @param heightHint
+ * the available height in the viewport
+ * @return the maximum width that will fit in the rectangle.
+ */
+ public static int getWidth(double aspectRatio, int widthHint, int heightHint) {
+ return (int) Math.min(widthHint, heightHint * aspectRatio);
+ }
+
+ /**
+ * Converts a base 64 encoded image into and ImageData object.
+ *
+ * @param base64Image
+ * the images as a base64 string.
+ * @return the image decoded as base64
+ * @throws IOException
+ * in case of IO problems
+ */
+ public static ImageData decodeToImage(String base64Image) throws IOException {
+ if (base64Image == null) {
+ return null;
+ }
+ String image = processB64String(base64Image);
+ Decoder decoder = Base64.getDecoder();
+ byte[] imageByte = decoder.decode(image);
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(imageByte)) {
+ ImageData imageData = new ImageData(bis);
+ return imageData;
+ }
+ }
+
+ /**
+ * Transforms the passed image to a Base64 String.
+ *
+ * @param image
+ * the image to convert, null in is null out.
+ * @param format
+ * see {@link ImageLoader#save(java.io.OutputStream, int)}
+ * @return The encoded image in base 64 or null if the passed image was null.
+ */
+ public static String decodeFromImage(Image image, int format) {
+ if (image == null) {
+ return null;
+ }
+ ImageLoader loader = new ImageLoader();
+ loader.data = new ImageData[] { image.getImageData() };
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ loader.save(bos, format);
+ Encoder encoder = Base64.getEncoder();
+ return getHeader(format) + encoder.encodeToString(bos.toByteArray());
+ }
+
+ private static String getHeader(int format) {
+ return "data:image/" + getExtension(format) + ";base64,";
+ }
+
+ private static String getExtension(int format) {
+ switch (format) {
+ case SWT.IMAGE_BMP:
+ return "bmp";
+ case SWT.IMAGE_BMP_RLE:
+ return "bmp";
+ case SWT.IMAGE_GIF:
+ return "gif";
+ case SWT.IMAGE_ICO:
+ return "ico";
+ case SWT.IMAGE_JPEG:
+ return "jpeg";
+ default:
+ return "png";
+ }
+ }
+
+ private static String processB64String(String base64Image) {
+ int index = base64Image.substring(0, 50).indexOf(COMMA) + 1;
+ return base64Image.substring(index);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ResourceManager.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ResourceManager.java
new file mode 100644
index 000000000..31e4919cb
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/ResourceManager.java
@@ -0,0 +1,435 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2018 Google, 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
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.osgi.framework.Bundle;
+
+/**
+ * Utility class for managing OS resources associated with SWT/JFace controls
+ * such as colors, fonts, images, etc.
+ *
+ * !!! IMPORTANT !!! Application code must explicitly invoke the
+ * <code>dispose()</code> method to release the operating system resources
+ * managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ *
+ * This class may be freely distributed as part of any application or plugin.
+ * <p>
+ *
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class ResourceManager extends SWTResourceManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static Map<ImageDescriptor, Image> m_descriptorImageMap = new HashMap<ImageDescriptor, Image>();
+
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path
+ * relative to the specified class.
+ *
+ * @param clazz
+ * the {@link Class} relative to which to find the image descriptor.
+ * @param path
+ * the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(Class<?> clazz, String path) {
+ return ImageDescriptor.createFromFile(clazz, path);
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @param path
+ * the path to the image file.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ */
+ public static ImageDescriptor getImageDescriptor(String path) {
+ try {
+ return ImageDescriptor.createFromURL(new File(path).toURI().toURL());
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns an {@link Image} based on the specified {@link ImageDescriptor}.
+ *
+ * @param descriptor
+ * the {@link ImageDescriptor} for the {@link Image}.
+ * @return the {@link Image} based on the specified {@link ImageDescriptor}.
+ */
+ public static Image getImage(ImageDescriptor descriptor) {
+ if (descriptor == null) {
+ return null;
+ }
+ Image image = m_descriptorImageMap.get(descriptor);
+ if (image == null) {
+ image = descriptor.createImage();
+ m_descriptorImageMap.put(descriptor, image);
+ }
+ return image;
+ }
+
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated.
+ * @param decorator
+ * the {@link Image} to decorate the base image.
+ * @return {@link Image} The resulting decorated image.
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated.
+ * @param decorator
+ * the {@link Image} to decorate the base image.
+ * @param corner
+ * the corner to place decorator image.
+ * @return the resulting decorated {@link Image}.
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap<Image, Image>();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ final Rectangle bib = baseImage.getBounds();
+ final Rectangle dib = decorator.getBounds();
+ final Point baseImageSize = new Point(bib.width, bib.height);
+ CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() {
+ @Override
+ protected void drawCompositeImage(int width, int height) {
+ drawImage(createCachedImageDataProvider(baseImage), 0, 0);
+ if (corner == TOP_LEFT) {
+ drawImage(createCachedImageDataProvider(decorator), 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ drawImage(createCachedImageDataProvider(decorator), bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ drawImage(createCachedImageDataProvider(decorator), 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ drawImage(createCachedImageDataProvider(decorator), bib.width - dib.width,
+ bib.height - dib.height);
+ }
+ }
+
+ @Override
+ protected Point getSize() {
+ return baseImageSize;
+ }
+ };
+ //
+ result = compositImageDesc.createImage();
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+
+ /**
+ * Dispose all of the cached images.
+ */
+ public static void disposeImages() {
+ SWTResourceManager.disposeImages();
+ // dispose ImageDescriptor images
+ {
+ for (Image image : m_descriptorImageMap.values()) {
+ image.dispose();
+ }
+ m_descriptorImageMap.clear();
+ }
+ // dispose decorated images
+ for (Map<Image, Map<Image, Image>> cornerDecoratedImageMap : m_decoratedImageMap) {
+ if (cornerDecoratedImageMap != null) {
+ for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ // dispose plugin images
+ {
+ for (Image image : m_URLImageMap.values()) {
+ image.dispose();
+ }
+ m_URLImageMap.clear();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Plugin images support
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps URL to images.
+ */
+ private static Map<String, Image> m_URLImageMap = new HashMap<String, Image>();
+
+ /**
+ * Provider for plugin resources, used by WindowBuilder at design time.
+ */
+ public interface PluginResourceProvider {
+ URL getEntry(String symbolicName, String path);
+ }
+
+ /**
+ * Instance of {@link PluginResourceProvider}, used by WindowBuilder at design
+ * time.
+ */
+ private static PluginResourceProvider m_designTimePluginResourceProvider = null;
+
+ /**
+ * Returns an {@link Image} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the image
+ * @param name
+ * the path to the image within the plugin
+ * @return the {@link Image} stored in the file at the specified path
+ *
+ * @deprecated Use {@link #getPluginImage(String, String)} instead.
+ */
+ @Deprecated
+ public static Image getPluginImage(Object plugin, String name) {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link Image} based on a {@link Bundle} and resource entry path.
+ *
+ * @param symbolicName
+ * the symbolic name of the {@link Bundle}.
+ * @param path
+ * the path of the resource entry.
+ * @return the {@link Image} stored in the file at the specified path.
+ */
+ public static Image getPluginImage(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return getPluginImageFromUrl(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link Image} based on given {@link URL}.
+ */
+ private static Image getPluginImageFromUrl(URL url) {
+ try {
+ try {
+ String key = url.toExternalForm();
+ Image image = m_URLImageMap.get(key);
+ if (image == null) {
+ try (InputStream stream = url.openStream()) {
+ image = getImage(stream);
+ m_URLImageMap.put(key, image);
+ }
+ }
+ return image;
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the image.
+ * @param name
+ * the path to th eimage within the plugin.
+ * @return the {@link ImageDescriptor} stored in the file at the specified path.
+ *
+ * @deprecated Use {@link #getPluginImageDescriptor(String, String)} instead.
+ */
+ @Deprecated
+ public static ImageDescriptor getPluginImageDescriptor(Object plugin, String name) {
+ try {
+ try {
+ URL url = getPluginImageURL(plugin, name);
+ return ImageDescriptor.createFromURL(url);
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource
+ * entry path.
+ *
+ * @param symbolicName
+ * the symbolic name of the {@link Bundle}.
+ * @param path
+ * the path of the resource entry.
+ * @return the {@link ImageDescriptor} based on a {@link Bundle} and resource
+ * entry path.
+ */
+ public static ImageDescriptor getPluginImageDescriptor(String symbolicName, String path) {
+ try {
+ URL url = getPluginImageURL(symbolicName, path);
+ if (url != null) {
+ return ImageDescriptor.createFromURL(url);
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ return null;
+ }
+
+ /**
+ * Returns an {@link URL} based on a {@link Bundle} and resource entry path.
+ */
+ private static URL getPluginImageURL(String symbolicName, String path) {
+ // try runtime plugins
+ {
+ Bundle bundle = Platform.getBundle(symbolicName);
+ if (bundle != null) {
+ return bundle.getEntry(path);
+ }
+ }
+ // try design time provider
+ if (m_designTimePluginResourceProvider != null) {
+ return m_designTimePluginResourceProvider.getEntry(symbolicName, path);
+ }
+ // no such resource
+ return null;
+ }
+
+ /**
+ * Returns an {@link URL} based on a plugin and file path.
+ *
+ * @param plugin
+ * the plugin {@link Object} containing the file path.
+ * @param name
+ * the file path.
+ * @return the {@link URL} representing the file at the specified path.
+ * @throws Exception
+ */
+ private static URL getPluginImageURL(Object plugin, String name) throws Exception {
+ // try to work with 'plugin' as with OSGI BundleContext
+ try {
+ Class<?> BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$
+ Class<?> BundleContextClass = Class.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$
+ if (BundleContextClass.isAssignableFrom(plugin.getClass())) {
+ Method getBundleMethod = BundleContextClass.getMethod("getBundle", new Class[0]); //$NON-NLS-1$
+ Object bundle = getBundleMethod.invoke(plugin, new Object[0]);
+ //
+ Class<?> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor<?> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
+ Object path = pathConstructor.newInstance(new Object[] { name });
+ //
+ Class<?> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Class<?> PlatformClass = Class.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$
+ Method findMethod = PlatformClass.getMethod("find", new Class[] { BundleClass, IPathClass }); //$NON-NLS-1$
+ return (URL) findMethod.invoke(null, new Object[] { bundle, path });
+ }
+ } catch (Throwable e) {
+ // Ignore any exceptions
+ }
+ // else work with 'plugin' as with usual Eclipse plugin
+ {
+ Class<?> PluginClass = Class.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$
+ if (PluginClass.isAssignableFrom(plugin.getClass())) {
+ //
+ Class<?> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
+ Constructor<?> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
+ Object path = pathConstructor.newInstance(new Object[] { name });
+ //
+ Class<?> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
+ Method findMethod = PluginClass.getMethod("find", new Class[] { IPathClass }); //$NON-NLS-1$
+ return (URL) findMethod.invoke(plugin, new Object[] { path });
+ }
+ }
+ return null;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only
+ * be called when the cached objects are no longer needed (e.g. on application
+ * shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeFonts();
+ disposeImages();
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/SWTResourceManager.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/SWTResourceManager.java
new file mode 100644
index 000000000..e3f0cd524
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/SWTResourceManager.java
@@ -0,0 +1,488 @@
+/*******************************************************************************
+ * Copyright (c) 2011, 2018 Google, 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
+ *
+ * Contributors:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tips.ui.internal.util;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Cursor;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Utility class for managing OS resources associated with SWT controls such as
+ * colors, fonts, images, etc.
+ * <p>
+ * !!! IMPORTANT !!! Application code must explicitly invoke the
+ * <code>dispose()</code> method to release the operating system resources
+ * managed by cached objects when those objects and OS resources are no longer
+ * needed (e.g. on application shutdown)
+ * <p>
+ * This class may be freely distributed as part of any application or plugin.
+ * <p>
+ *
+ * @author scheglov_ke
+ * @author Dan Rubel
+ */
+public class SWTResourceManager {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Color
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
+
+ /**
+ * Returns the system {@link Color} matching the specific ID.
+ *
+ * @param systemColorID
+ * the ID value for the color
+ * @return the system {@link Color} matching the specific ID
+ */
+ public static Color getColor(int systemColorID) {
+ Display display = Display.getCurrent();
+ return display.getSystemColor(systemColorID);
+ }
+
+ /**
+ * Returns a {@link Color} given its red, green and blue component values.
+ *
+ * @param r
+ * the red component of the color
+ * @param g
+ * the green component of the color
+ * @param b
+ * the blue component of the color
+ * @return the {@link Color} matching the given red, green and blue component
+ * values
+ */
+ public static Color getColor(int r, int g, int b) {
+ return getColor(new RGB(r, g, b));
+ }
+
+ /**
+ * Returns a {@link Color} given its RGB value.
+ *
+ * @param rgb
+ * the {@link RGB} value of the color
+ * @return the {@link Color} matching the RGB value
+ */
+ public static Color getColor(RGB rgb) {
+ Color color = m_colorMap.get(rgb);
+ if (color == null) {
+ Display display = Display.getCurrent();
+ color = new Color(display, rgb);
+ m_colorMap.put(rgb, color);
+ }
+ return color;
+ }
+
+ /**
+ * Dispose of all the cached {@link Color}'s.
+ */
+ public static void disposeColors() {
+ for (Color color : m_colorMap.values()) {
+ color.dispose();
+ }
+ m_colorMap.clear();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps image paths to images.
+ */
+ private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
+
+ /**
+ * Returns an {@link Image} encoded by the specified {@link InputStream}.
+ *
+ * @param stream
+ * the {@link InputStream} encoding the image data
+ * @return the {@link Image} encoded by the specified input stream
+ */
+ protected static Image getImage(InputStream stream) throws IOException {
+ try {
+ Display display = Display.getCurrent();
+ ImageData data = new ImageData(stream);
+ if (data.transparentPixel > 0) {
+ return new Image(display, data, data.getTransparencyMask());
+ }
+ return new Image(display, data);
+ } finally {
+ stream.close();
+ }
+ }
+
+ /**
+ * Returns an {@link Image} stored in the file at the specified path.
+ *
+ * @param path
+ * the path to the image file
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(String path) {
+ Image image = m_imageMap.get(path);
+ if (image == null) {
+ try {
+ image = getImage(new FileInputStream(path));
+ m_imageMap.put(path, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(path, image);
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Returns an {@link Image} stored in the file at the specified path relative to
+ * the specified class.
+ *
+ * @param clazz
+ * the {@link Class} relative to which to find the image
+ * @param path
+ * the path to the image file, if starts with <code>'/'</code>
+ * @return the {@link Image} stored in the file at the specified path
+ */
+ public static Image getImage(Class<?> clazz, String path) {
+ String key = clazz.getName() + '|' + path;
+ Image image = m_imageMap.get(key);
+ if (image == null) {
+ try {
+ image = getImage(clazz.getResourceAsStream(path));
+ m_imageMap.put(key, image);
+ } catch (Exception e) {
+ image = getMissingImage();
+ m_imageMap.put(key, image);
+ }
+ }
+ return image;
+ }
+
+ private static final int MISSING_IMAGE_SIZE = 10;
+
+ /**
+ * @return the small {@link Image} that can be used as placeholder for missing
+ * image.
+ */
+ private static Image getMissingImage() {
+ Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ //
+ GC gc = new GC(image);
+ gc.setBackground(getColor(SWT.COLOR_RED));
+ gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
+ gc.dispose();
+ //
+ return image;
+ }
+
+ /**
+ * Style constant for placing decorator image in top left corner of base image.
+ */
+ public static final int TOP_LEFT = 1;
+ /**
+ * Style constant for placing decorator image in top right corner of base image.
+ */
+ public static final int TOP_RIGHT = 2;
+ /**
+ * Style constant for placing decorator image in bottom left corner of base
+ * image.
+ */
+ public static final int BOTTOM_LEFT = 3;
+ /**
+ * Style constant for placing decorator image in bottom right corner of base
+ * image.
+ */
+ public static final int BOTTOM_RIGHT = 4;
+ /**
+ * Internal value.
+ */
+ protected static final int LAST_CORNER_KEY = 5;
+ /**
+ * Maps images to decorated images.
+ */
+ @SuppressWarnings("unchecked")
+ private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @return {@link Image} The resulting decorated image
+ */
+ public static Image decorateImage(Image baseImage, Image decorator) {
+ return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
+ }
+
+ /**
+ * Returns an {@link Image} composed of a base image decorated by another image.
+ *
+ * @param baseImage
+ * the base {@link Image} that should be decorated
+ * @param decorator
+ * the {@link Image} to decorate the base image
+ * @param corner
+ * the corner to place decorator image
+ * @return the resulting decorated {@link Image}
+ */
+ public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
+ if (corner <= 0 || corner >= LAST_CORNER_KEY) {
+ throw new IllegalArgumentException("Wrong decorate corner");
+ }
+ Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
+ if (cornerDecoratedImageMap == null) {
+ cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
+ m_decoratedImageMap[corner] = cornerDecoratedImageMap;
+ }
+ Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
+ if (decoratedMap == null) {
+ decoratedMap = new HashMap<Image, Image>();
+ cornerDecoratedImageMap.put(baseImage, decoratedMap);
+ }
+ //
+ Image result = decoratedMap.get(decorator);
+ if (result == null) {
+ Rectangle bib = baseImage.getBounds();
+ Rectangle dib = decorator.getBounds();
+ //
+ result = new Image(Display.getCurrent(), bib.width, bib.height);
+ //
+ GC gc = new GC(result);
+ gc.drawImage(baseImage, 0, 0);
+ if (corner == TOP_LEFT) {
+ gc.drawImage(decorator, 0, 0);
+ } else if (corner == TOP_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, 0);
+ } else if (corner == BOTTOM_LEFT) {
+ gc.drawImage(decorator, 0, bib.height - dib.height);
+ } else if (corner == BOTTOM_RIGHT) {
+ gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);
+ }
+ gc.dispose();
+ //
+ decoratedMap.put(decorator, result);
+ }
+ return result;
+ }
+
+ /**
+ * Dispose all of the cached {@link Image}'s.
+ */
+ public static void disposeImages() {
+ // dispose loaded images
+ {
+ for (Image image : m_imageMap.values()) {
+ image.dispose();
+ }
+ m_imageMap.clear();
+ }
+ // dispose decorated images
+ for (Map<Image, Map<Image, Image>> cornerDecoratedImageMap : m_decoratedImageMap) {
+ if (cornerDecoratedImageMap != null) {
+ for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
+ for (Image image : decoratedMap.values()) {
+ image.dispose();
+ }
+ decoratedMap.clear();
+ }
+ cornerDecoratedImageMap.clear();
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Font
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps font names to fonts.
+ */
+ private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
+ /**
+ * Maps fonts to their bold versions.
+ */
+ private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
+
+ /**
+ * Returns a {@link Font} based on its name, height and style.
+ *
+ * @param name
+ * the name of the font
+ * @param height
+ * the height of the font
+ * @param style
+ * the style of the font
+ * @return {@link Font} The font matching the name, height and style
+ */
+ public static Font getFont(String name, int height, int style) {
+ return getFont(name, height, style, false, false);
+ }
+
+ /**
+ * Returns a {@link Font} based on its name, height and style. Windows-specific
+ * strikeout and underline flags are also supported.
+ *
+ * @param name
+ * the name of the font
+ * @param size
+ * the size of the font
+ * @param style
+ * the style of the font
+ * @param strikeout
+ * the strikeout flag (warning: Windows only)
+ * @param underline
+ * the underline flag (warning: Windows only)
+ * @return {@link Font} The font matching the name, height, style, strikeout and
+ * underline
+ */
+ public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) {
+ String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline;
+ Font font = m_fontMap.get(fontName);
+ if (font == null) {
+ FontData fontData = new FontData(name, size, style);
+ if (strikeout || underline) {
+ try {
+ Class<?> logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$
+ Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$
+ if (logFont != null && logFontClass != null) {
+ if (strikeout) {
+ logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ if (underline) {
+ logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
+ }
+ }
+ } catch (Throwable e) {
+ System.err.println(
+ "Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ font = new Font(Display.getCurrent(), fontData);
+ m_fontMap.put(fontName, font);
+ }
+ return font;
+ }
+
+ /**
+ * Returns a bold version of the given {@link Font}.
+ *
+ * @param baseFont
+ * the {@link Font} for which a bold version is desired
+ * @return the bold version of the given {@link Font}
+ */
+ public static Font getBoldFont(Font baseFont) {
+ Font font = m_fontToBoldFontMap.get(baseFont);
+ if (font == null) {
+ FontData fontDatas[] = baseFont.getFontData();
+ FontData data = fontDatas[0];
+ font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
+ m_fontToBoldFontMap.put(baseFont, font);
+ }
+ return font;
+ }
+
+ /**
+ * Dispose all of the cached {@link Font}'s.
+ */
+ public static void disposeFonts() {
+ // clear fonts
+ for (Font font : m_fontMap.values()) {
+ font.dispose();
+ }
+ m_fontMap.clear();
+ // clear bold fonts
+ for (Font font : m_fontToBoldFontMap.values()) {
+ font.dispose();
+ }
+ m_fontToBoldFontMap.clear();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Cursor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Maps IDs to cursors.
+ */
+ private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
+
+ /**
+ * Returns the system cursor matching the specific ID.
+ *
+ * @param id
+ * int The ID value for the cursor
+ * @return Cursor The system cursor matching the specific ID
+ */
+ public static Cursor getCursor(int id) {
+ Integer key = Integer.valueOf(id);
+ Cursor cursor = m_idToCursorMap.get(key);
+ if (cursor == null) {
+ cursor = new Cursor(Display.getDefault(), id);
+ m_idToCursorMap.put(key, cursor);
+ }
+ return cursor;
+ }
+
+ /**
+ * Dispose all of the cached cursors.
+ */
+ public static void disposeCursors() {
+ for (Cursor cursor : m_idToCursorMap.values()) {
+ cursor.dispose();
+ }
+ m_idToCursorMap.clear();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // General
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Dispose of cached objects and their underlying OS resources. This should only
+ * be called when the cached objects are no longer needed (e.g. on application
+ * shutdown).
+ */
+ public static void dispose() {
+ disposeColors();
+ disposeImages();
+ disposeFonts();
+ disposeCursors();
+ }
+
+ public static void report() {
+ System.out.println("Colormap : " + m_colorMap.size());
+ System.out.println("DecoratedImageMap : " + m_decoratedImageMap.length);
+ System.out.println("Fontmap : " + m_fontMap.size());
+ System.out.println("CursorMap : " + m_idToCursorMap.size());
+ System.out.println("ImageMap : " + m_imageMap.size());
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/package-info.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/package-info.java
new file mode 100644
index 000000000..b90369b25
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/internal/util/package-info.java
@@ -0,0 +1,14 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Provides some classes for internal use.
+ */
+package org.eclipse.tips.ui.internal.util; \ No newline at end of file
diff --git a/org.eclipse.tips.ui/src/org/eclipse/tips/ui/package-info.java b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/package-info.java
new file mode 100644
index 000000000..73b68a021
--- /dev/null
+++ b/org.eclipse.tips.ui/src/org/eclipse/tips/ui/package-info.java
@@ -0,0 +1,15 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Remain Software
+ * 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:
+ * wim.jongman@remainsoftware.com - initial API and implementation
+ *******************************************************************************/
+/**
+ * Provides tip UI classes. You should be able to reuse the TipComposite outside
+ * of the tips framework.
+ */
+package org.eclipse.tips.ui; \ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 28b6b165e..542d606c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,5 +65,9 @@
<module>org.eclipse.ui.intro.quicklinks</module>
<module>org.eclipse.ui.intro.quicklinks.examples</module>
<module>org.eclipse.ui.intro.solstice.examples</module>
+ <module>org.eclipse.tips.core</module>
+ <module>org.eclipse.tips.ui</module>
+ <module>org.eclipse.tips.ide</module>
+
</modules>
</project>

Back to the top