diff options
author | George Suaridze | 2020-02-08 16:53:13 +0000 |
---|---|---|
committer | Alexander Fedorov | 2020-02-16 17:48:23 +0000 |
commit | 6467b78d26822b22215ab2c87409a9ced3abcaa4 (patch) | |
tree | 9f376758faf5f140ccb0a6652e503a4284077037 | |
parent | 078759650d652ca8952c8ba61e9522dff00156b8 (diff) | |
download | eclipse.platform.ua-6467b78d26822b22215ab2c87409a9ced3abcaa4.tar.gz eclipse.platform.ua-6467b78d26822b22215ab2c87409a9ced3abcaa4.tar.xz eclipse.platform.ua-6467b78d26822b22215ab2c87409a9ced3abcaa4.zip |
Bug 559885 - Hyperlink support in cheat sheet itemI20200217-0600I20200216-1800
description
Added support for hyperlinks in cheat sheet item description.
Hyperlinks are expressed using <a href="URL">diplay link</a>, where URL
is a valid URL to an external web site or an CheatSheet URL that
represents an CheatSheat action. All cheat sheet URLs have the following
form: http://org.eclipse.ui.cheatsheet/<action_name>?param1=value1&param2=value2
and will be processed by the cheat sheet framework.
The following predefined actions are included in the cheat sheet
framework:
- showView - activates view with given view id
- execute - executes the specified command
Change-Id: I8b2500e734e45d2b00e93d4795316347a2ff4ccd
Signed-off-by: George Suaridze <suag@1c.ru>
12 files changed, 488 insertions, 16 deletions
diff --git a/org.eclipse.ua.tests/cheatsheet/org/eclipse/ua/tests/cheatsheet/parser/ValidTest.java b/org.eclipse.ua.tests/cheatsheet/org/eclipse/ua/tests/cheatsheet/parser/ValidTest.java index 0a53c13aa..ea774894b 100644 --- a/org.eclipse.ua.tests/cheatsheet/org/eclipse/ua/tests/cheatsheet/parser/ValidTest.java +++ b/org.eclipse.ua.tests/cheatsheet/org/eclipse/ua/tests/cheatsheet/parser/ValidTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2016 IBM Corporation and others. + * Copyright (c) 2005, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 *******************************************************************************/ package org.eclipse.ua.tests.cheatsheet.parser; @@ -105,4 +106,9 @@ public class ValidTest { parseCheatsheet("HelloWorld.xml"); } + @Test + public void testHyperlinks() throws IOException { + parseCheatsheet("TestCSHyperlinks.xml"); + } + } diff --git a/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks.xml b/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks.xml new file mode 100644 index 000000000..811cfcd13 --- /dev/null +++ b/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<!-- + Copyright (c) 2020 1C-Soft LLC and others. + + This program and the accompanying materials + are made available under the terms of the Eclipse Public License 2.0 + which accompanies this distribution, and is available at + https://www.eclipse.org/legal/epl-2.0/ + + SPDX-License-Identifier: EPL-2.0 + + Contributors: + George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 + --> +<cheatsheet title="Hyperlinks Demo"> + <intro> + <description> + Welcome to the hyperlinks cheat sheet demo. + <br/> + Click the <a href="http://org.eclipse.ui.cheatsheet/execute?command=org.eclipse.search.ui.openSearchDialog">link</a> to open <b>Search</b> dialog. + <br/> + Open <a href="http://org.eclipse.ui.cheatsheet/execute?command=org.eclipse.ui.newWizard%28newWizardId%3Dorg.eclipse.ui.wizards.new.project%29">new project</a> wizard. + <br/> + Activate <a href="http://org.eclipse.ui.cheatsheet/showView?id=org.eclipse.pde.runtime.LogView">Error Log</a> view. + </description> + </intro> + <item + title="Step 1"> + <description> + Open <a href="https://eclipse.org">eclipse.org</a> in external browser. + </description> + </item> +</cheatsheet> diff --git a/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks_expected.txt b/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks_expected.txt new file mode 100644 index 000000000..1550a7926 --- /dev/null +++ b/org.eclipse.ua.tests/data/cheatsheet/valid/TestCSHyperlinks_expected.txt @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<cheatsheet + title="Hyperlinks Demo"> + <item + title="Introduction" + description="<form><p>Welcome to the hyperlinks cheat sheet demo. + <br/>Click the <a href="http://org.eclipse.ui.cheatsheet/execute?command=org.eclipse.search.ui.openSearchDialog">link</a> to open <b>Search</b> dialog. + <br/>Open <a href="http://org.eclipse.ui.cheatsheet/execute?command=org.eclipse.ui.newWizard%28newWizardId%3Dorg.eclipse.ui.wizards.new.project%29">new project</a> wizard. + <br/>Activate <a href="http://org.eclipse.ui.cheatsheet/showView?id=org.eclipse.pde.runtime.LogView">Error Log</a> view. + </p></form>" + Href="null" + contextId="null"> + <nullAction/> + <nullList/> + <nullPerformWhen/> + <nullList/> + </item> + <list> + <item + title="Step 1" + description="<form><p>Open <a href="https://eclipse.org">eclipse.org</a> in external browser. + </p></form>" + Href="null" + contextId="null"> + <nullAction/> + <list/> + <nullPerformWhen/> + <nullList/> + </item> + </list> +</cheatsheet> diff --git a/org.eclipse.ua.tests/plugin.xml b/org.eclipse.ua.tests/plugin.xml index eb8c6378d..6b0dbee4a 100644 --- a/org.eclipse.ua.tests/plugin.xml +++ b/org.eclipse.ua.tests/plugin.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <!-- - Copyright (c) 2005, 2011 IBM Corporation and others. + Copyright (c) 2005, 2020 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 @@ -12,6 +12,7 @@ Contributors: IBM Corporation - initial API and implementation + George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 --> <plugin> @@ -205,6 +206,15 @@ id="org.eclipse.ua.tests.cheatsheet.subcategory.qualified" name="Cheatsheet in subcategory, qualified name"> </cheatsheet> + <cheatsheet + category="org.eclipse.ua.tests.cheatsheet.cheatSheetsTestCat" + contentFile="data/cheatsheet/valid/TestCSHyperlinks.xml" + id="org.eclipse.ua.tests.cheatsheet.cheatsheetsHyperlinks" + name="Testing Cheat Sheets hyperlinks"> + <description> + This cheat sheet has items to test the framework + </description> + </cheatsheet> </extension> <!-- diff --git a/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.exsd b/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.exsd index af736e2d6..91a1bdf06 100644 --- a/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.exsd +++ b/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.exsd @@ -69,7 +69,17 @@ DTD fragments (<a href="cheatSheetContentFileSpec.exsd">machine <documentation> The &lt;description&gt; element holds the description of a cheat sheet or of a cheat sheet item. The description consists of text interspersed with <a href="../../../org.eclipse.platform.doc.isv/guide/forms_controls_text_markup.htm">form text markup</a>. The cheat sheet automatically formats and lays out the text to -make it show up reasonably in the UI. Within the text, balanced <b>&lt;b&gt;</b>...<b>&lt;/b&gt;</b> +make it show up reasonably in the UI. Hyperlinks are expressed using <b>&lt;a href="URL"&gt;text&lt;/a&gt;</b>, where URL is a valid URL to an external web site or a CheatSheet URL that represents a CheatSheat action. All cheat sheet URLs have the following form: http://org.eclipse.ui.cheatsheet/&lt;action name&gt;?param1=value1&amp;param2=value2 and will be processed by the cheat sheet framework. +<br> +The following predefined actions are included in the cheat sheet framework: +<p style="margin-left:15px;"> +<b>showView</b> - activates view with given view id<br> +<i>id</i> - a view id<br> +<br> +<b>execute</b> - executes the specified command. See the <code>serialize()</code> method on <code>org.eclipse.core.command.ParameterizedCommand</code> for details of the command serialization format. Since 3.2.<br> +<i>command</i> - a serialized <code>ParameterizedCommand</code><br> +</p> +Within the text, balanced <b>&lt;b&gt;</b>...<b>&lt;/b&gt;</b> tags cause the enclosed text to be rendered in a bold font, and the <b>&lt;br/&gt;</b> element can be used to force a line break. These are the only formatting tags supported at this time (however, others may be added in the future). Certain diff --git a/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.html b/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.html index e396f8f09..cd1de3a1a 100644 --- a/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.html +++ b/org.eclipse.ui.cheatsheets/schema/cheatSheetContentFileSpec.html @@ -44,7 +44,18 @@ The <intro> element is used to define the introductory text to be displaye <p class="ConfigMarkupElementDesc"> The <description> element holds the description of a cheat sheet or of a cheat sheet item. The description consists of text interspersed with <a href="../../../org.eclipse.platform.doc.isv/guide/forms_controls_text_markup.htm">form text markup</a>. The cheat sheet automatically formats and lays out the text to -make it show up reasonably in the UI. Within the text, balanced <b><b></b>...<b></b></b> +make it show up reasonably in the UI. +Hyperlinks are expressed using <b><a href="URL">text<a></b>, where URL is a valid URL to an external web site or a CheatSheet URL that represents a CheatSheat action. All cheat sheet URLs have the following form: http://org.eclipse.ui.cheatsheet/<action name>?param1=value1&param2=value2 and will be processed by the cheat sheet framework. +<br> +The following predefined actions are included in the cheat sheet framework: +<p style="margin-left:15px;"> +<b>showView</b> - activates view with given view id<br> +<i>id</i> - a view id<br> +<br> +<b>execute</b> - executes the specified command. See the <code>serialize()</code> method on <code>org.eclipse.core.command.ParameterizedCommand</code> for details of the command serialization format. Since 3.2.<br> +<i>command</i> - a serialized <code>ParameterizedCommand</code><br> +</p> +Within the text, balanced <b><b></b>...<b></b></b> tags cause the enclosed text to be rendered in a bold font, and the <b><br/></b> element can be used to force a line break. These are the only formatting tags supported at this time (however, others may be added in the future). Certain diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.java b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.java index a39476041..3d4a12955 100644 --- a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.java +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets; @@ -27,6 +28,7 @@ public final class Messages extends NLS { public static String ERROR_WRITING_STATE_FILE; public static String CHEAT_SHEET_SELECTION_DIALOG_TITLE; public static String CHEAT_SHEET_SELECTION_DIALOG_MSG; + public static String CHEAT_SHEET_UNSUPPORTED_LINK_ACTIVATION_MESSAGE; public static String COLLAPSE_ALL_BUT_CURRENT_TOOLTIP; public static String CATEGORY_OTHER; public static String RESTORE_ALL_TOOLTIP; diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.properties b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.properties index f03fbe80e..9a55e158a 100644 --- a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.properties +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/Messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2003, 2007 IBM Corporation and others. +# Copyright (c) 2003, 2020 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ # # Contributors: # IBM Corporation - initial API and implementation +# George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 ############################################################################### # ---------------------------------------------------------------------- # Properties for org.eclipse.ui.cheatsheets @@ -61,6 +62,7 @@ RESTART_CHEATSHEET_TOOLTIP = Click to Restart # ViewItem HELP_BUTTON_TOOLTIP = Open Related Help +CHEAT_SHEET_UNSUPPORTED_LINK_ACTIVATION_MESSAGE = URL of this link is: ''{0}'' # CheatSheetViewer ERROR_RUNNING_ACTION = The action could not be run. diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/CheatSheetParser.java b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/CheatSheetParser.java index 18f4bb3f0..acbc55503 100644 --- a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/CheatSheetParser.java +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/CheatSheetParser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2002, 2018 IBM Corporation and others. + * Copyright (c) 2002, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets.data; @@ -119,7 +120,7 @@ public class CheatSheetParser implements IStatusContainer { * } * </pre> * - * Tags that will be ignored {@literal <b>, </b> and <br/> + * Tags that will be ignored {@literal <b>, </b>, <a>, </a> and <br/> * }. * * @param text @@ -137,6 +138,8 @@ public class CheatSheetParser implements IStatusContainer { // Create the buffer to store the resulting string StringBuilder result = new StringBuilder(length); + boolean hyperlinkContext = false; + // Loop for the characters of the original string for(int i=0; i<length; i++) { // Grab the next character and determine how to handle it @@ -152,7 +155,11 @@ public class CheatSheetParser implements IStatusContainer { else { tmp = text.substring(i, length).toLowerCase(); } - if(tmp.startsWith(IParserTags.BOLD_START_TAG) || tmp.startsWith(IParserTags.BOLD_END_TAG) || tmp.startsWith(IParserTags.BREAK_TAG)) { + if (hyperlinkContext || tmp.startsWith(IParserTags.HYPERLINK_START_TAG)) { + result.append(c); + hyperlinkContext = true; + } else if (tmp.startsWith(IParserTags.BOLD_START_TAG) || tmp.startsWith(IParserTags.BOLD_END_TAG) + || tmp.startsWith(IParserTags.BREAK_TAG)) { // We have a tag to ignore so just emit the character result.append(c); } else { @@ -171,6 +178,15 @@ public class CheatSheetParser implements IStatusContainer { } else { tmp = text.substring(0, i+1).toLowerCase(); } + if (tmp.endsWith(IParserTags.HYPERLINK_END_TAG)) { + result.append(c); + hyperlinkContext = false; + break; + } + if (hyperlinkContext) { + result.append(c); + break; + } if(tmp.endsWith(IParserTags.BOLD_START_TAG) || tmp.endsWith(IParserTags.BOLD_END_TAG) || tmp.endsWith(IParserTags.BREAK_TAG)) { // We have a tag to ignore so just emit the character result.append(c); @@ -190,7 +206,7 @@ public class CheatSheetParser implements IStatusContainer { break; case '"': // We have a quote so emit the XML escaped counterpart - result.append(IParserTags.QUOTE); + result.append(hyperlinkContext ? c : IParserTags.QUOTE); break; case '\t': // We have a tab, replace with a space @@ -442,7 +458,7 @@ public class CheatSheetParser implements IStatusContainer { text.append(nodeValue); isLeadingTrimRequired = false; } else if(node.getNodeType() == Node.ELEMENT_NODE) { - // handle <b></b> and <br/> + // handle <b></b>, <br/> and <a></a> switch (node.getNodeName()) { case IParserTags.BOLD: containsMarkup = true; @@ -456,6 +472,19 @@ public class CheatSheetParser implements IStatusContainer { text.append(IParserTags.BREAK_TAG); isLeadingTrimRequired = true; break; + case IParserTags.HYPERLINK: + containsMarkup = true; + if (node.getAttributes().getNamedItem(IParserTags.HREF) == null) { + text.append(NLS.bind(IParserTags.HYPERLINK_PLACEHOLDER, "")); //$NON-NLS-1$ + } else { + String href = node.getAttributes().getNamedItem(IParserTags.HREF).getNodeValue(); + String link = NLS.bind(IParserTags.HYPERLINK_PLACEHOLDER, href); + text.append(link); + } + text.append(node.getTextContent()); + text.append(IParserTags.HYPERLINK_END_TAG); + isLeadingTrimRequired = false; + break; default: warnUnknownMarkupElement(startNode, nodeName, node); break; diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/IParserTags.java b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/IParserTags.java index 0dc27f4e0..46a982a22 100644 --- a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/IParserTags.java +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/data/IParserTags.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2002, 2015 IBM Corporation and others. + * Copyright (c) 2002, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -10,6 +10,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets.data; @@ -47,9 +48,13 @@ public interface IParserTags { public static final String DESCRIPTION = "description"; //$NON-NLS-1$ public static final String BOLD = "b"; //$NON-NLS-1$ public static final String BREAK = "br"; //$NON-NLS-1$ + public static final String HYPERLINK = "a"; //$NON-NLS-1$ public static final String BOLD_START_TAG = "<b>"; //$NON-NLS-1$ public static final String BOLD_END_TAG = "</b>"; //$NON-NLS-1$ public static final String BREAK_TAG = "<br/>"; //$NON-NLS-1$ + public static final String HYPERLINK_START_TAG = "<a"; //$NON-NLS-1$ + public static final String HYPERLINK_PLACEHOLDER = "<a href=\"{0}\">"; //$NON-NLS-1$ + public static final String HYPERLINK_END_TAG = "</a>"; //$NON-NLS-1$ public static final String FORM_START_TAG = "<form><p>"; //$NON-NLS-1$ public static final String FORM_END_TAG = "</p></form>"; //$NON-NLS-1$ diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/CheatSheetHyperlinkActionFactory.java b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/CheatSheetHyperlinkActionFactory.java new file mode 100644 index 000000000..b34be5e08 --- /dev/null +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/CheatSheetHyperlinkActionFactory.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2020 1C-Soft LLC and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * George Suaridze (1C-Soft LLC) - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.internal.cheatsheets.views; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; +import java.util.Properties; + +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.core.commands.common.CommandException; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.browser.IWorkbenchBrowserSupport; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.internal.cheatsheets.CheatSheetPlugin; +import org.eclipse.ui.internal.cheatsheets.Messages; + +/** + * A factory that knows how to create cheatsheet URL actions. + * <p> + * A cheatsheet URL is a valid http url, with org.eclipse.ui.cheatsheet as a + * host. + * </p> + * <p> + * A cheatsheet url instance is created by parsing the url and retrieving the + * embedded "command" and parameters. For example, the following urls are valid + * cheatsheet urls: + * <p> + * + * <pre> + * http://org.eclipse.ui.cheatsheet/showView?id=org.eclipse.pde.runtime.LogView + * http://org.eclipse.ui.cheatsheet/execute?command=org.eclipse.ui.newWizard%28newWizardId%3Dorg.eclipse.ui.wizards.new.project%29" + * </pre> + * <p> + * When parsed, the first url has "showView" as a command, and "id" parameter. + * While the second "execute" as a command and "command" as parameter with + * "newWizardId" as command parameter. + * </p> + * <p> + * For now it supports two commands: + * <li>showView - to activate given view by its id</li> + * <li>execute - to execute eclipse command</li> + */ +public class CheatSheetHyperlinkActionFactory { + + private static final String CHEAT_SHEET_PROTOCOL = "http"; //$NON-NLS-1$ + private static final String CHEAT_SHEET_HOST_ID = "org.eclipse.ui.cheatsheet"; //$NON-NLS-1$ + + private static final String EXECUTE = "execute"; //$NON-NLS-1$ + private static final String SHOW_VIEW = "showView"; //$NON-NLS-1$ + + private static final String KEY_ID = "id"; //$NON-NLS-1$ + private static final String KEY_COMAND = "command"; //$NON-NLS-1$ + private static final String KEY_DECODE = "decode"; //$NON-NLS-1$ + + private static final String VALUE_TRUE = "true"; //$NON-NLS-1$ + + /** + * Creates {@link CheatSheetHyperlinkAction} for given url string + * + * @param urlString + * - url string representation, cannot be {@code null} + * @return appropriate cheatsheet action {@link CheatSheetHyperlinkAction} + */ + public CheatSheetHyperlinkAction create(String urlString) { + if (urlString == null) { + return new FallbackAction(urlString); + } + try { + URL url = new URL(urlString); + if (isCheatSheetHyperlink(url)) { + String action = getPathAsAction(url); + Properties parameters = getQueryParameters(url); + switch (action) { + case EXECUTE: + return new CommandAction(getParameter(parameters, KEY_COMAND)); + case SHOW_VIEW: + return new ShowViewAction(getParameter(parameters, KEY_ID)); + default: + CheatSheetPlugin.getPlugin().getLog().error("Unsupported action: " + action, null); //$NON-NLS-1$ + } + return new FallbackAction(urlString); + } else if (url.getProtocol() != null) { + return new OpenInBrowserAction(url); + } else { + return new FallbackAction(urlString); + } + } catch (MalformedURLException e) { + CheatSheetPlugin.getPlugin().getLog().error("Malformed URL: " + urlString, e); //$NON-NLS-1$ + return new FallbackAction(urlString); + } + } + + private boolean isCheatSheetHyperlink(URL url) { + if (!url.getProtocol().equalsIgnoreCase(CHEAT_SHEET_PROTOCOL)) { + return false; + } + if (url.getHost().equalsIgnoreCase(CHEAT_SHEET_HOST_ID)) { + return true; + } + return false; + } + + private Properties getQueryParameters(URL url) { + Properties properties = new Properties(); + String query = url.getQuery(); + if (query == null) { + return properties; + } + String[] params = query.split("&"); //$NON-NLS-1$ + for (int i = 0; i < params.length; i++) { + String[] keyValuePair = params[i].split("="); //$NON-NLS-1$ + if (keyValuePair.length != 2) { + CheatSheetPlugin.getPlugin().getLog() + .warn(MessageFormat.format("Ignoring the following Cheatsheet URL parameter: {0}", params[i])); //$NON-NLS-1$ + continue; + } + + String key = urlDecode(keyValuePair[0]); + if (key == null) { + CheatSheetPlugin.getPlugin().getLog() + .warn(MessageFormat.format("Failed to URL decode key: {0}", keyValuePair[0])); //$NON-NLS-1$ + continue; + } + + String value = urlDecode(keyValuePair[1]); + if (value == null) { + CheatSheetPlugin.getPlugin().getLog() + .warn(MessageFormat.format("Failed to URL decode value: {0}", keyValuePair[1])); //$NON-NLS-1$ + continue; + } + + properties.setProperty(key, value); + } + return properties; + } + + private String urlDecode(String encodedURL) { + int len = encodedURL.length(); + ByteArrayOutputStream os = new ByteArrayOutputStream(len); + + for (int i = 0; i < len;) { + switch (encodedURL.charAt(i)) { + case '%': + if (len >= i + 3) { + os.write(Integer.parseInt(encodedURL.substring(i + 1, i + 3), 16)); + } + i += 3; + break; + case '+': // exception from standard + os.write(' '); + i++; + break; + default: + os.write(encodedURL.charAt(i++)); + break; + } + } + return new String(os.toByteArray(), StandardCharsets.UTF_8); + } + + private String getPathAsAction(URL url) { + String action = url.getPath(); + if (action != null) { + action = action.substring(1); + } + return action; + } + + private String getParameter(Properties parameters, String parameterId) { + String value = parameters.getProperty(parameterId); + String decode = parameters.getProperty(KEY_DECODE); + + if (value != null) { + try { + if (decode != null && decode.equalsIgnoreCase(VALUE_TRUE)) { + return decode(value, "UTF-8"); //$NON-NLS-1$ + } + return value; + } catch (Exception e) { + CheatSheetPlugin.getPlugin().getLog().error("Failed to decode URL: " + parameterId, e); //$NON-NLS-1$ + } + } + return value; + } + + private String decode(String s, String enc) throws UnsupportedEncodingException { + try { + return URLDecoder.decode(s, enc); + } catch (Exception ex) { + return s; + } + } + + public static abstract class CheatSheetHyperlinkAction { + + /** + * Executes action. + */ + public final void execute() { + Display display = Display.getDefault(); + BusyIndicator.showWhile(display, () -> { + doExecute(display); + }); + + } + + protected abstract void doExecute(Display display); + } + + private static class FallbackAction extends CheatSheetHyperlinkAction { + + private final String url; + + public FallbackAction(String url) { + this.url = url; + } + + @Override + public void doExecute(Display display) { + MessageDialog.openInformation(display.getActiveShell(), + null, + MessageFormat.format(Messages.CHEAT_SHEET_UNSUPPORTED_LINK_ACTIVATION_MESSAGE, url)); + } + } + + private static class OpenInBrowserAction extends CheatSheetHyperlinkAction { + + private final URL url; + + public OpenInBrowserAction(URL url) { + this.url = url; + } + + @Override + public void doExecute(Display display) { + try { + IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport(); + support.getExternalBrowser().openURL(url); + } catch (PartInitException e) { + CheatSheetPlugin.getPlugin().getLog().error("Cheatsheet failed to get Browser support.", e); //$NON-NLS-1$ + } + } + } + + private static class ShowViewAction extends CheatSheetHyperlinkAction { + + private final String viewId; + + public ShowViewAction(String viewId) { + this.viewId = viewId; + } + + @Override + protected void doExecute(Display display) { + IWorkbench workbench = PlatformUI.getWorkbench(); + IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow(); + if (activeWorkbenchWindow != null) { + try { + activeWorkbenchWindow.getActivePage().showView(viewId, null, IWorkbenchPage.VIEW_ACTIVATE); + } catch (PartInitException e) { + CheatSheetPlugin.getPlugin().getLog().error("Error while activating view: " + viewId, e); //$NON-NLS-1$ + } + } + } + } + + private static class CommandAction extends CheatSheetHyperlinkAction { + + private final String command; + + public CommandAction(String command) { + this.command = command; + } + + @Override + protected void doExecute(Display display) { + ICommandService commandService = PlatformUI.getWorkbench().getService(ICommandService.class); + IHandlerService handlerService = PlatformUI.getWorkbench().getService(IHandlerService.class); + if (commandService == null || handlerService == null) { + CheatSheetPlugin.getPlugin().getLog().error( + "Could not get ICommandService or IHandlerService while trying to execute: " + command, null); //$NON-NLS-1$ + return; + } + try { + ParameterizedCommand pCommand = commandService.deserialize(command); + handlerService.executeCommand(pCommand, null); + } catch (CommandException e) { + CheatSheetPlugin.getPlugin().getLog().error("Could not execute command: " + command, e); //$NON-NLS-1$ + } + } + } +} diff --git a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/ViewItem.java b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/ViewItem.java index bc98a0ca2..d0de8758b 100644 --- a/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/ViewItem.java +++ b/org.eclipse.ui.cheatsheets/src/org/eclipse/ui/internal/cheatsheets/views/ViewItem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2002, 2019 IBM Corporation and others. + * Copyright (c) 2002, 2020 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Asma Smaoui - CEA LIST - https://bugs.eclipse.org/bugs/show_bug.cgi?id=517379 + * George Suaridze <suag@1c.ru> (1C-Soft LLC) - Bug 559885 *******************************************************************************/ package org.eclipse.ui.internal.cheatsheets.views; @@ -54,6 +55,7 @@ import org.eclipse.ui.internal.cheatsheets.ICheatSheetResource; import org.eclipse.ui.internal.cheatsheets.Messages; import org.eclipse.ui.internal.cheatsheets.data.IParserTags; import org.eclipse.ui.internal.cheatsheets.data.Item; +import org.eclipse.ui.internal.cheatsheets.views.CheatSheetHyperlinkActionFactory.CheatSheetHyperlinkAction; public abstract class ViewItem { @@ -199,24 +201,38 @@ public abstract class ViewItem { @Override public void widgetSelected(SelectionEvent e) { Action copyAction = viewer.getCopyAction(); - if (copyAction!=null) + if (copyAction != null) { copyAction.setEnabled(bodyText.canCopy()); + } } }); bodyText.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { Action copyAction = viewer.getCopyAction(); - if (copyAction!=null) + if (copyAction != null) { copyAction.setEnabled(bodyText.canCopy()); + } } @Override public void focusLost(FocusEvent e) { Action copyAction = viewer.getCopyAction(); - if (copyAction!=null) + if (copyAction != null) { copyAction.setEnabled(false); + } + } + }); + + bodyText.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent event) { + String url = (String) event.getHref(); + CheatSheetHyperlinkActionFactory actionFactory = new CheatSheetHyperlinkActionFactory(); + CheatSheetHyperlinkAction cheatSheetHyperlink = actionFactory.create(url); + cheatSheetHyperlink.execute(); } }); + // bodyText = toolkit.createLabel(bodyWrapperComposite, item.getDescription(), SWT.WRAP); bodyText.setText(item.getDescription(), item.getDescription().startsWith(IParserTags.FORM_START_TAG), false); |