diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipAndTrickBean.java b/platform/platform-impl/src/com/intellij/ide/util/TipAndTrickBean.java new file mode 100644 index 000000000000..fcaa09c22ae0 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/util/TipAndTrickBean.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.util; + +import com.intellij.openapi.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.util.Comparing; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.Nullable; + +/** + * @author gregsh + */ +public class TipAndTrickBean extends AbstractExtensionPointBean { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.tipAndTrick"); + + @Attribute("file") + public String myFileName; + + @Attribute("feature-id") + public String myFeatureId; + + public String getFileName() { + return myFileName; + } + + public String getFeatureId() { + return myFeatureId; + } + + @Nullable + public static TipAndTrickBean findByFileName(String tipFileName) { + for (TipAndTrickBean tip : Extensions.getExtensions(EP_NAME)) { + if (Comparing.equal(tipFileName, tip.getFileName())) { + return tip; + } + } + return null; + } +} + diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java b/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java index 8ccdda223cb6..e44be06c8b41 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipPanel.java @@ -15,18 +15,17 @@ */ package com.intellij.ide.util; -import com.intellij.featureStatistics.FeatureDescriptor; -import com.intellij.featureStatistics.ProductivityFeaturesProvider; import com.intellij.icons.AllIcons; +import com.intellij.ide.BrowserUtil; import com.intellij.ide.GeneralSettings; import com.intellij.ide.IdeBundle; import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.extensions.Extensions; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.components.JBLabel; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; -import org.jdom.Document; -import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import javax.swing.*; import javax.swing.event.HyperlinkEvent; @@ -35,20 +34,16 @@ import javax.swing.text.html.HTMLEditorKit; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; +import java.util.List; public class TipPanel extends JPanel { private static final int DEFAULT_WIDTH = 400; private static final int DEFAULT_HEIGHT = 200; - private final JCheckBox myCheckBox; - private final JEditorPane browser; - private final ArrayList myTipPaths = new ArrayList(); - private final HashMap> myPathsToProviderMap = new HashMap>(); - @NonNls - private static final String ELEMENT_TIP = "tip"; - @NonNls - private static final String ATTRIBUTE_FILE = "file"; + + private final JEditorPane myBrowserPanel; + private final JLabel myPoweredByLabel; + private final List myTips = ContainerUtil.newArrayList(); public TipPanel() { setLayout(new BorderLayout()); @@ -63,45 +58,42 @@ public class TipPanel extends JPanel { jpanel.add(jlabel1, BorderLayout.CENTER); jpanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0)); add(jpanel, BorderLayout.NORTH); - browser = new JEditorPane(); - browser.setEditable(false); - browser.setEditorKit(new HTMLEditorKit()); - browser.setBackground(UIUtil.getTextFieldBackground()); - browser.addHyperlinkListener( + myBrowserPanel = new JEditorPane(); + myBrowserPanel.setEditable(false); + myBrowserPanel.setEditorKit(new HTMLEditorKit()); + myBrowserPanel.setBackground(UIUtil.getTextFieldBackground()); + myBrowserPanel.addHyperlinkListener( new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - //TODO: Open url in browser + BrowserUtil.browse(e.getURL()); } } } ); - JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(browser); + JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myBrowserPanel); add(scrollPane, BorderLayout.CENTER); - myCheckBox = new JCheckBox(IdeBundle.message("checkbox.show.tips.on.startup"), true); - myCheckBox.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); + + JPanel southPanel = new JPanel(new BorderLayout()); + JCheckBox showOnStartCheckBox = new JCheckBox(IdeBundle.message("checkbox.show.tips.on.startup"), true); + showOnStartCheckBox.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0)); final GeneralSettings settings = GeneralSettings.getInstance(); - myCheckBox.setSelected(settings.showTipsOnStartup()); - myCheckBox.addItemListener(new ItemListener() { + showOnStartCheckBox.setSelected(settings.showTipsOnStartup()); + showOnStartCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { settings.setShowTipsOnStartup(e.getStateChange() == ItemEvent.SELECTED); } }); - add(myCheckBox, BorderLayout.SOUTH); - try { - readTips("/tips/tips.xml"); - readTips("/tips/IdeSpecificTips.xml"); - } - catch (Exception exception) {// - } + southPanel.add(showOnStartCheckBox, BorderLayout.WEST); - for (ProductivityFeaturesProvider provider : ProductivityFeaturesProvider.EP_NAME.getExtensions()) { - final FeatureDescriptor[] descriptors = provider.getFeatureDescriptors(); - for (int j = 0; descriptors != null && j < descriptors.length; j++) { - FeatureDescriptor descriptor = descriptors[j]; - myPathsToProviderMap.put(descriptor.getTipFileName(), descriptor.getProvider()); - } - } + myPoweredByLabel = new JBLabel(); + myPoweredByLabel.setHorizontalAlignment(SwingConstants.RIGHT); + myPoweredByLabel.setForeground(SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES.getFgColor()); + + southPanel.add(myPoweredByLabel, BorderLayout.EAST); + add(southPanel, BorderLayout.SOUTH); + + Collections.addAll(myTips, Extensions.getExtensions(TipAndTrickBean.EP_NAME)); } public Dimension getPreferredSize() { @@ -109,57 +101,49 @@ public class TipPanel extends JPanel { } public void prevTip() { - if (myTipPaths.size() == 0) { - browser.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName())); + if (myTips.size() == 0) { + myBrowserPanel.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName())); return; } final GeneralSettings settings = GeneralSettings.getInstance(); int lastTip = settings.getLastTip(); - final String path; + final TipAndTrickBean tip; lastTip--; if (lastTip <= 0) { - path = myTipPaths.get(myTipPaths.size() - 1); - lastTip = myTipPaths.size(); + tip = myTips.get(myTips.size() - 1); + lastTip = myTips.size(); } else { - path = myTipPaths.get(lastTip - 1); + tip = myTips.get(lastTip - 1); } - setTip(path, lastTip, browser, settings); + setTip(tip, lastTip, myBrowserPanel, settings); } - private void setTip (String path, int lastTip, JEditorPane browser, GeneralSettings settings) { - TipUIUtil.openTipInBrowser(path, browser, myPathsToProviderMap.get(path)); + private void setTip (TipAndTrickBean tip, int lastTip, JEditorPane browser, GeneralSettings settings) { + TipUIUtil.openTipInBrowser(tip, browser); + myPoweredByLabel.setText(TipUIUtil.getPoweredByText(tip)); settings.setLastTip(lastTip); } public void nextTip() { - if (myTipPaths.size() == 0) { - browser.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName())); + if (myTips.size() == 0) { + myBrowserPanel.setText(IdeBundle.message("error.tips.not.found", ApplicationNamesInfo.getInstance().getFullProductName())); return; } GeneralSettings settings = GeneralSettings.getInstance(); int lastTip = settings.getLastTip(); - String path; + TipAndTrickBean tip; lastTip++; - if (lastTip - 1 >= myTipPaths.size()) { - path = myTipPaths.get(0); + if (lastTip - 1 >= myTips.size()) { + tip = myTips.get(0); lastTip = 1; } else { - path = myTipPaths.get(lastTip - 1); + tip = myTips.get(lastTip - 1); } - setTip(path, lastTip, browser, settings); - } - - private void readTips(String tipsURL) throws Exception { - final Document document = JDOMUtil.loadDocument(getClass().getResource(tipsURL).openStream()); - - for (Object o : document.getRootElement().getChildren(ELEMENT_TIP)) { - Element tip = (Element)o; - myTipPaths.add(tip.getAttributeValue(ATTRIBUTE_FILE)); - } + setTip(tip, lastTip, myBrowserPanel, settings); } } diff --git a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java index 0e531bfc1fc9..afa6a2b5f219 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/util/TipUIUtil.java @@ -16,17 +16,22 @@ package com.intellij.ide.util; import com.intellij.ide.IdeBundle; +import com.intellij.ide.plugins.IdeaPluginDescriptor; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.actionSystem.KeyboardShortcut; import com.intellij.openapi.actionSystem.Shortcut; import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationNamesInfo; +import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.keymap.impl.DefaultKeymap; +import com.intellij.util.ObjectUtils; import com.intellij.util.ResourceUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.imageio.ImageIO; @@ -46,7 +51,20 @@ public class TipUIUtil { private TipUIUtil() { } - public static void openTipInBrowser(String tipPath, JEditorPane browser, Class providerClass) { + @NotNull + public static String getPoweredByText(@NotNull TipAndTrickBean tip) { + PluginDescriptor descriptor = tip.getPluginDescriptor(); + return descriptor instanceof IdeaPluginDescriptor && + !PluginManagerCore.CORE_PLUGIN_ID.equals(descriptor.getPluginId().getIdString()) ? + ((IdeaPluginDescriptor)descriptor).getName() : ""; + } + + public static void openTipInBrowser(String tipFileName, JEditorPane browser, Class providerClass) { + openTipInBrowser(TipAndTrickBean.findByFileName(tipFileName), browser); + } + + public static void openTipInBrowser(@Nullable TipAndTrickBean tip, JEditorPane browser) { + if (tip == null) return; /* TODO: detect that file is not present if (!file.exists()) { browser.read(new StringReader("Tips for '" + feature.getDisplayName() + "' not found. Make sure you installed IntelliJ IDEA correctly."), null); @@ -54,18 +72,18 @@ public class TipUIUtil { } */ try { - if (tipPath == null) return; - if (providerClass == null) providerClass = TipUIUtil.class; - URL url = ResourceUtil.getResource(providerClass, "/tips/", tipPath); + ClassLoader tipLoader = ObjectUtils.notNull(tip.getPluginDescriptor().getPluginClassLoader(), TipUIUtil.class.getClassLoader()); + + URL url = ResourceUtil.getResource(tipLoader, "/tips/", tip.getFileName()); if (url == null) { - setCantReadText(browser, tipPath); + setCantReadText(browser, tip); return; } StringBuffer text = new StringBuffer(ResourceUtil.loadText(url)); updateShortcuts(text); - updateImages(text, providerClass); + updateImages(text, tipLoader); String replaced = text.toString().replace("&productName;", ApplicationNamesInfo.getInstance().getFullProductName()); replaced = replaced.replace("&majorVersion;", ApplicationInfo.getInstance().getMajorVersion()); replaced = replaced.replace("&minorVersion;", ApplicationInfo.getInstance().getMinorVersion()); @@ -75,11 +93,11 @@ public class TipUIUtil { browser.read(new StringReader(replaced), url); } catch (IOException e) { - setCantReadText(browser, tipPath); + setCantReadText(browser, tip); } } - private static void setCantReadText(JEditorPane browser, String missingFile) { + private static void setCantReadText(JEditorPane browser, TipAndTrickBean missingFile) { try { browser.read(new StringReader( IdeBundle.message("error.unable.to.read.tip.of.the.day", missingFile, ApplicationNamesInfo.getInstance().getFullProductName())), null); @@ -88,7 +106,7 @@ public class TipUIUtil { } } - private static void updateImages(StringBuffer text, Class providerClass) { + private static void updateImages(StringBuffer text, ClassLoader tipLoader) { final boolean dark = UIUtil.isUnderDarcula(); final boolean retina = UIUtil.isRetina(); // if (!dark && !retina) { @@ -109,7 +127,7 @@ public class TipUIUtil { String path = img.substring(srcIndex + 5, endIndex); if (!path.endsWith("_dark") && !path.endsWith("@2x")) { path += suffix + ".png"; - URL url = ResourceUtil.getResource(providerClass, "/tips/", path); + URL url = ResourceUtil.getResource(tipLoader, "/tips/", path); if (url != null) { String newImgTag = " + diff --git a/platform/util/src/com/intellij/util/ResourceUtil.java b/platform/util/src/com/intellij/util/ResourceUtil.java index 469e79d8b7e9..19aba6368d78 100644 --- a/platform/util/src/com/intellij/util/ResourceUtil.java +++ b/platform/util/src/com/intellij/util/ResourceUtil.java @@ -38,6 +38,10 @@ public class ResourceUtil { } public static URL getResource(@NotNull Class loaderClass, @NonNls @NotNull String basePath, @NonNls @NotNull String fileName) { + return getResource(loaderClass.getClassLoader(), basePath, fileName); + } + + public static URL getResource(@NotNull ClassLoader loaderClass, @NonNls @NotNull String basePath, @NonNls @NotNull String fileName) { if (basePath.endsWith("/")) basePath = basePath.substring(0, basePath.length() - 1); final List bundles = calculateBundleNames(basePath, Locale.getDefault()); diff --git a/python/resources/tips/tips.xml b/python/resources/tips/tips.xml deleted file mode 100644 index 5ef93412ac62..000000000000 --- a/python/resources/tips/tips.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/src/META-INF/IdeTipsAndTricks.xml b/python/src/META-INF/IdeTipsAndTricks.xml new file mode 100644 index 000000000000..41a5febdab94 --- /dev/null +++ b/python/src/META-INF/IdeTipsAndTricks.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml index a232079090a0..dc95bd030198 100644 --- a/python/src/META-INF/pycharm-core.xml +++ b/python/src/META-INF/pycharm-core.xml @@ -1,5 +1,6 @@ + diff --git a/resources-en/src/tips/UmlClassDiagram.html b/resources-en/src/tips/UmlClassDiagram.html deleted file mode 100644 index fba09a6258d8..000000000000 Binary files a/resources-en/src/tips/UmlClassDiagram.html and /dev/null differ diff --git a/resources-en/src/tips/images/umlClassDiagram.png b/resources-en/src/tips/images/umlClassDiagram.png deleted file mode 100644 index b5d759e0c2a8..000000000000 Binary files a/resources-en/src/tips/images/umlClassDiagram.png and /dev/null differ diff --git a/resources-en/src/tips/tips.xml b/resources-en/src/tips/tips.xml deleted file mode 100644 index 503f38594771..000000000000 --- a/resources-en/src/tips/tips.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/src/META-INF/IdeTipsAndTricks.xml b/resources/src/META-INF/IdeTipsAndTricks.xml new file mode 100644 index 000000000000..c70dba109cf4 --- /dev/null +++ b/resources/src/META-INF/IdeTipsAndTricks.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 88c827c78da6..e6d8f987a166 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -2,6 +2,7 @@ com.intellij IDEA CORE +