Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.tips.core')
-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
25 files changed, 2108 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

Back to the top