Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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