diff --git a/.idea/modules.xml b/.idea/modules.xml index 064010d8399b..44693dcf252d 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -238,7 +238,6 @@ - diff --git a/build/groovy/ApplicationInfoProperties.groovy b/build/groovy/ApplicationInfoProperties.groovy new file mode 100644 index 000000000000..23542cb6b3bd --- /dev/null +++ b/build/groovy/ApplicationInfoProperties.groovy @@ -0,0 +1,43 @@ +import groovy.transform.Immutable + +/* + * Copyright 2000-2015 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. + */ + +/** + * @author nik + */ +class ApplicationInfoProperties { + final String majorVersion + final String minorVersion + final boolean isEAP + final InstallOverProperties installOver + + ApplicationInfoProperties(String appInfoXmlPath) { + def root = new XmlParser().parse(new File(appInfoXmlPath)) + majorVersion = root.version.first().@major + minorVersion = root.version.first().@minor + isEAP = Boolean.parseBoolean(root.version.first().@eap) + def installOverTag = root."install-over".first() + installOver = new InstallOverProperties(minBuild: installOverTag.@minbuild, maxBuild: installOverTag.@maxbuild, version: installOverTag.@version) + } +} + +@Immutable +class InstallOverProperties { + final String minBuild + final String maxBuild + final String version +} \ No newline at end of file diff --git a/build/groovy/MacDistributionBuilder.groovy b/build/groovy/MacDistributionBuilder.groovy index 3a49d686a29a..30f1809cd55d 100644 --- a/build/groovy/MacDistributionBuilder.groovy +++ b/build/groovy/MacDistributionBuilder.groovy @@ -23,7 +23,7 @@ class MacDistributionBuilder { JpsGantProjectBuilder projectBuilder MacHostProperties macHostProperties /** - * Directory where IntelliJ IDEA community sources are located + * Path to a directory where IntelliJ IDEA community sources are located */ String communityHome /** diff --git a/build/groovy/WinInstallerBuilder.groovy b/build/groovy/WinInstallerBuilder.groovy new file mode 100644 index 000000000000..f4b8b2f4c8a1 --- /dev/null +++ b/build/groovy/WinInstallerBuilder.groovy @@ -0,0 +1,184 @@ +import com.intellij.openapi.util.SystemInfoRt +import org.codehaus.gant.GantBuilder +import org.jetbrains.jps.gant.JpsGantProjectBuilder +/* + * Copyright 2000-2015 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. + */ + +/** + * @author nik + */ +class WinInstallerBuilder { + GantBuilder ant + JpsGantProjectBuilder projectBuilder + ApplicationInfoProperties applicationInfo + /** + * Path to a directory where project sources are located. It will be used to replace 'BASE_DIR' in *.nsi files. + */ + String baseDirectory + /** + * Path to a directory where IntelliJ IDEA community sources are located + */ + String communityHome + /** + * Path to a directory where artifacts are stored + */ + String artifactsPath + /** + * Path to a directory where temporary files can be stored + */ + String sandboxPath + /** + * Prefix for the output file name. The value of buildNumber will be appended to this prefix. + */ + String outNamePrefix + /** + * Short build number without product code (e.g. 142.239) + */ + String buildNumber + /** + * Name which will be used for default config/system paths, should include the product name and version. E.g. for IntelliJ Ultimate 16 it is + * 'IntelliJIdea16', so the settings and system files will be stored in $USER_HOME/.IntelliJIdea16 by default. + */ + String systemSelector + /** + * Determines whether tools.jar should be included to the bundled JDK + */ + boolean includeToolsJar = true + boolean associateIpr = true + /** + * Path to a JDK 8 zip file which should be bundled with the application + */ + String winJDKZipPath + + /** + * Builds .exe installer. If build/lib/jet-sign.jar exists in baseDirectory it will be used to sign the created .exe file. + * + * @param pathsToInclude list of paths to directories which contents should be included to the product distribution + * @param stringsFile path to *.nsi file where installers variables are defined (use build/conf/nsis/stringsCE.nsi as a reference) + * @param pathsFile path to *.nsi file where installers path variables are defined (use build/conf/nsis/pathsCE.nsi as a reference) + * @return path to the created installer file + */ + def buildInstaller(List pathsToInclude, String stringsFile, String pathsFile) { + if (!SystemInfoRt.isWindows) { + projectBuilder.warning("Windows installer can be built only under Windows") + return null + } + + projectBuilder.stage("Building Windows installer") + String outFileName = "${outNamePrefix}${buildNumber}" + ant.taskdef(name: "nsis", classname: "com.intellij.internalUtilities.ant.NsiFiles", classpath: "$communityHome/build/lib/NsiFiles.jar") + + def box = sandboxPath + ant.mkdir(dir: "$box/bin") + ant.mkdir(dir: "$box/nsiconf") + + if (winJDKZipPath != null) { + ant.mkdir(dir: "$box/jre") + ant.unzip(dest: "$box/jre", src: winJDKZipPath) + + ant.copy(todir: "$box/bin") { + fileset(dir: "$box/jre/jre/bin") { + include(name: "msvcr71.dll") + } + } + } + + ant.copy(todir: "$box/nsiconf") { + fileset(dir: "$communityHome/build/conf/nsis") { + include(name: "*") + exclude(name: "version*") + exclude(name: "strings*") + exclude(name: "paths*") + } + } + + if (applicationInfo.isEAP) { + ant.copy(file: "$communityHome/build/conf/nsis/version.eap.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + else { + ant.copy(file: "$communityHome/build/conf/nsis/version.nsi", + tofile: "$box/nsiconf/version.nsi", overwrite: true) + } + ant.copy(file: pathsFile, toFile: "$box/nsiconf/paths.nsi", overwrite: true) + + ant.nsis(instfile: "$box/nsiconf/idea_win.nsh", uninstfile: "$box/nsiconf/unidea_win.nsh") { + pathsToInclude.each { + ant.fileset(dir: it, includes: "**/*") { + exclude(name: "**/idea.properties") + exclude(name: "**/*.vmoptions") + } + } + ant.fileset(dir: box, includes: "bin/msvcr71.dll") + if (winJDKZipPath != null) { + ant.fileset(dir: box, includes: "jre/**/*") + if (includeToolsJar) { + ant.fileset(dir: box) { + include(name: "jre/lib/tools.jar") + } + } + } + } + + ant.copy(file: stringsFile, toFile: "$box/nsiconf/strings.nsi", overwrite: true) + ant.replace(file: "$box/nsiconf/strings.nsi") { + replacefilter(token: "__VERSION_MAJOR__", value: applicationInfo.majorVersion) + replacefilter(token: "__VERSION_MINOR__", value: applicationInfo.minorVersion) + } + + ant.replace(file: "$box/nsiconf/version.nsi") { + replacefilter(token: "__BUILD_NUMBER__", value: buildNumber) + replacefilter(token: "__VERSION_MAJOR__", value: applicationInfo.majorVersion) + replacefilter(token: "__VERSION_MINOR__", value: applicationInfo.minorVersion) + replacefilter(token: "__MIN_UPGRADE_BUILD__", value: applicationInfo.installOver.minBuild) + replacefilter(token: "__MAX_UPGRADE_BUILD__", value: applicationInfo.installOver.maxBuild) + replacefilter(token: "__UPGRADE_VERSION__", value: applicationInfo.installOver.version) + replacefilter(token: "__PRODUCT_PATHS_SELECTOR__", value: systemSelector) + } + + ant.unzip(src: "$communityHome/build/tools/NSIS.zip", dest: box) + ant.exec(command: "\"${box}/NSIS/makensis.exe\"" + + " /DBASE_DIR=\"$baseDirectory\"" + + " /DCOMMUNITY_DIR=\"$communityHome\"" + + " /DIPR=\"${associateIpr}\"" + + " /DOUT_FILE=\"${outFileName}\"" + + " /DOUT_DIR=\"$artifactsPath\"" + + " \"${box}/nsiconf/idea.nsi\"") + + def installerPath = "$artifactsPath/${outFileName}.exe" + if (!new File(installerPath).exists()) { + projectBuilder.error("Installer wasn't created.") + } + + def signJarPath = "$baseDirectory/build/lib/jet-sign.jar" + if (new File(signJarPath).exists()) { + projectBuilder.stage("Signing $installerPath") + ant.taskdef(name: "jet-sign", classname: "jetbrains.sign.JetSignTask") { + classpath(path: signJarPath) + } + ant."jet-sign"() { + ant.fileset(dir: artifactsPath) { + include(name: "${outFileName}.exe") + } + } + projectBuilder.stage("Signing done") + } + else { + projectBuilder.warning("$signJarPath not found, installer won't be signed") + } + return installerPath + } +} \ No newline at end of file diff --git a/build/tools/NSIS.zip b/build/tools/NSIS.zip new file mode 100644 index 000000000000..aa076cfa464f Binary files /dev/null and b/build/tools/NSIS.zip differ diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java index 0cfc492b4a9f..d440617da834 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/PurityAnalysis.java @@ -30,6 +30,7 @@ import java.util.*; /** * Produces equations for inference of @Contract(pure=true) annotations. * Scala source at https://github.com/ilya-klyuchnikov/faba + * Algorithm: https://github.com/ilya-klyuchnikov/faba/blob/ef1c15b4758517652e939f67099bbec0260e9e68/notes/purity.md */ public class PurityAnalysis { static final Set topEffect = Collections.singleton(EffectQuantum.TopEffectQuantum); @@ -444,7 +445,11 @@ final class HardCodedPurity { } static Set getHardCodedSolution(HKey key) { - // TODO + // TODO: implement the logic as in https://github.com/ilya-klyuchnikov/faba/blob/2ffab410416e0a9f8e35d5071df50bcf27b1e149/src/main/scala/asm/purity.scala#L238 + // The problem with porting logic from Scala version "as is" is that in Scala version original keys (Key) are used. + // Here (in IDEA) the hashed keys (HKey) are used. In a general hashed keys may lead to collisions. + // So in order to port the logic, hardcoded solutions should be used with stable keys, + // that is - during analysis - com.intellij.codeInspection.bytecodeAnalysis.DataInterpreter.naryOperation return null; } } @@ -493,7 +498,7 @@ final class PuritySolver { } else { propagateKeys = new HKey[]{key.mkStable(), key}; - propagateEffects = new Set[]{effects, mkUnstableEffects(key, effects)}; + propagateEffects = new Set[]{effects, mkUnstableEffects(key)}; } for (int i = 0; i < propagateKeys.length; i++) { HKey pKey = propagateKeys[i]; @@ -592,17 +597,8 @@ final class PuritySolver { } } - private static Set mkUnstableEffects(HKey key, Set effects) { - // TODO - return PurityAnalysis.topHEffect; - /* - Set effects1 = HardCodedPurity.getHardCodedSolution(key); - if (effects1 != null) { - return effects; - } - else { - return PurityAnalysis.topHEffect; - } - */ + private static Set mkUnstableEffects(HKey key) { + Set hardcodedEffects = HardCodedPurity.getHardCodedSolution(key); + return hardcodedEffects == null ? PurityAnalysis.topHEffect : hardcodedEffects; } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java index 619a604dc8fc..ce827c34353c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java @@ -22,7 +22,6 @@ import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.actions.OptimizeImportsProcessor; -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.hint.QuestionAction; import com.intellij.ide.util.DefaultPsiElementCellRenderer; @@ -267,17 +266,6 @@ public class AddImportAction implements QuestionAction { myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(pos2.line, newCol)); myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (!myProject.isDisposed() && myProject.isOpen()) { - DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(myProject); - if (daemonCodeAnalyzer != null) { - daemonCodeAnalyzer.updateVisibleHighlighters(myEditor); - } - } - } - }); } protected void bindReference(PsiReference ref, PsiClass targetClass) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java index 2f0c016d0f75..4db2bbc90154 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodFix.java @@ -19,42 +19,32 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.JavaProjectCodeInsightSettings; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.daemon.QuickFixBundle; -import com.intellij.codeInsight.daemon.impl.actions.AddImportAction; +import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass; +import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction; -import com.intellij.ide.util.MethodCellRenderer; +import com.intellij.codeInspection.HintAction; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.PopupStep; -import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.*; import com.intellij.psi.util.proximity.PsiProximityComparator; -import com.intellij.ui.popup.list.ListPopupImpl; -import com.intellij.ui.popup.list.PopupListElementRenderer; import com.intellij.util.ArrayUtilRt; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.ObjectUtils; import com.intellij.util.Processor; import com.intellij.util.containers.LinkedMultiMap; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import java.awt.*; import java.util.*; -import java.util.List; -public class StaticImportMethodFix implements IntentionAction { +public class StaticImportMethodFix implements IntentionAction, HintAction { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.StaticImportMethodFix"); private final SmartPsiElementPointer myMethodCall; private List candidates; @@ -68,9 +58,7 @@ public class StaticImportMethodFix implements IntentionAction { public String getText() { String text = QuickFixBundle.message("static.import.method.text"); if (candidates != null && candidates.size() == 1) { - text += " '" + PsiFormatUtil.formatMethod(candidates.get(0), PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | - PsiFormatUtilBase.SHOW_CONTAINING_CLASS | - PsiFormatUtilBase.SHOW_FQ_NAME, 0)+"'"; + text += " '" + getMethodPresentableText() + "'"; } else { text += "..."; @@ -78,6 +66,13 @@ public class StaticImportMethodFix implements IntentionAction { return text; } + @NotNull + private String getMethodPresentableText() { + return PsiFormatUtil.formatMethod(candidates.get(0), PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | + PsiFormatUtilBase.SHOW_CONTAINING_CLASS | + PsiFormatUtilBase.SHOW_FQ_NAME, 0); + } + @Override @NotNull public String getFamilyName() { @@ -91,6 +86,7 @@ public class StaticImportMethodFix implements IntentionAction { && myMethodCall.getElement() != null && myMethodCall.getElement().isValid() && myMethodCall.getElement().getMethodExpression().getQualifierExpression() == null + && myMethodCall.getElement().resolveMethod() == null && file.getManager().isInProject(file) && !(candidates == null ? candidates = getMethodsToImport() : candidates).isEmpty() ; @@ -263,146 +259,61 @@ public class StaticImportMethodFix implements IntentionAction { @Override public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) { if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; - if (candidates.size() == 1) { - final PsiMethod toImport = candidates.get(0); - doImport(toImport); - } - else { - chooseAndImport(editor, project); - } - } - - private void doImport(final PsiMethod toImport) { - CommandProcessor.getInstance().executeCommand(toImport.getProject(), new Runnable(){ + ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - try { - PsiMethodCallExpression element = myMethodCall.getElement(); - if (element != null) { - AddSingleMemberStaticImportAction.bindAllClassRefs(element.getContainingFile(), toImport, toImport.getName(), toImport.getContainingClass()); - } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - } - }); - + final List methodsToImport = getMethodsToImport(); + if (methodsToImport.isEmpty()) return; + createQuestionAction(methodsToImport, project, editor).execute(); } - }, getText(), this); - + }); } - private void chooseAndImport(Editor editor, final Project project) { - if (ApplicationManager.getApplication().isUnitTestMode()) { - doImport(candidates.get(0)); - return; + @NotNull + private StaticImportMethodQuestionAction createQuestionAction(List methodsToImport, @NotNull Project project, Editor editor) { + return new StaticImportMethodQuestionAction(project, editor, methodsToImport, myMethodCall); + } + + private ImportClassFixBase.Result doFix(Editor editor) { + if (candidates.isEmpty()) { + return ImportClassFixBase.Result.POPUP_NOT_SHOWN; } - final BaseListPopupStep step = - new BaseListPopupStep(QuickFixBundle.message("class.to.import.chooser.title"), candidates) { - + + final StaticImportMethodQuestionAction action = createQuestionAction(candidates, myMethodCall.getProject(), editor); + + if (candidates.size() == 1) { + CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() { @Override - public PopupStep onChosen(PsiMethod selectedValue, boolean finalChoice) { - if (selectedValue == null) { - return FINAL_CHOICE; - } - - if (finalChoice) { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - LOG.assertTrue(selectedValue.isValid()); - doImport(selectedValue); - return FINAL_CHOICE; - } - - String qname = PsiUtil.getMemberQualifiedName(selectedValue); - if (qname == null) return FINAL_CHOICE; - List excludableStrings = AddImportAction.getAllExcludableStrings(qname); - return new BaseListPopupStep(null, excludableStrings) { - @NotNull - @Override - public String getTextFor(String value) { - return "Exclude '" + value + "' from auto-import"; - } - - @Override - public PopupStep onChosen(String selectedValue, boolean finalChoice) { - if (finalChoice) { - AddImportAction.excludeFromImport(project, selectedValue); - } - - return super.onChosen(selectedValue, finalChoice); - } - }; + public void run() { + action.execute(); } + }); + return ImportClassFixBase.Result.CLASS_AUTO_IMPORTED; + } - @Override - public boolean hasSubstep(PsiMethod selectedValue) { - return true; - } - - @NotNull - @Override - public String getTextFor(PsiMethod value) { - return ObjectUtils.assertNotNull(value.getName()); - } - - @Override - public Icon getIconFor(PsiMethod aValue) { - return aValue.getIcon(0); - } - }; - - final ListPopupImpl popup = new ListPopupImpl(step) { - final PopupListElementRenderer rightArrow = new PopupListElementRenderer(this); - @Override - protected ListCellRenderer getListElementRenderer() { - return new MethodCellRenderer(true, PsiFormatUtilBase.SHOW_NAME){ - - @Nullable - @Override - protected TextAttributes getNavigationItemAttributes(Object value) { - TextAttributes attrs = super.getNavigationItemAttributes(value); - if (value instanceof PsiMethod && !((PsiMethod)value).isDeprecated()) { - PsiClass psiClass = ((PsiMethod)value).getContainingClass(); - if (psiClass != null && psiClass.isDeprecated()) { - return TextAttributes.merge(attrs, super.getNavigationItemAttributes(psiClass)); - } - } - return attrs; - } - - @Override - protected DefaultListCellRenderer getRightCellRenderer(final Object value) { - final DefaultListCellRenderer moduleRenderer = super.getRightCellRenderer(value); - return new DefaultListCellRenderer(){ - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - JPanel panel = new JPanel(new BorderLayout()); - if (moduleRenderer != null) { - Component moduleComponent = moduleRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - if (!isSelected) { - moduleComponent.setBackground(getBackgroundColor(value)); - } - panel.add(moduleComponent, BorderLayout.CENTER); - } - rightArrow.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - Component rightArrowComponent = rightArrow.getNextStepLabel(); - panel.add(rightArrowComponent, BorderLayout.EAST); - return panel; - } - }; - } - }; - } - }; - popup.showInBestPositionFor(editor); + String hintText = ShowAutoImportPass.getMessage(candidates.size() > 1, getMethodPresentableText()); + if (!ApplicationManager.getApplication().isUnitTestMode() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) { + final PsiMethodCallExpression element = myMethodCall.getElement(); + final TextRange textRange = element.getTextRange(); + HintManager.getInstance().showQuestionHint(editor, hintText, + textRange.getStartOffset(), + textRange.getEndOffset(), action); + } + return ImportClassFixBase.Result.POPUP_SHOWN; } @Override public boolean startInWriteAction() { - return true; + return false; + } + + @Override + public boolean showHint(@NotNull Editor editor) { + final PsiMethodCallExpression callExpression = myMethodCall.getElement(); + if (callExpression == null || callExpression.getMethodExpression().getQualifierExpression() != null) { + return false; + } + ImportClassFixBase.Result result = doFix(editor); + return result == ImportClassFixBase.Result.POPUP_SHOWN || result == ImportClassFixBase.Result.CLASS_AUTO_IMPORTED; } } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodQuestionAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodQuestionAction.java new file mode 100644 index 000000000000..4f83e31e79ed --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/StaticImportMethodQuestionAction.java @@ -0,0 +1,217 @@ +/* + * Copyright 2000-2015 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.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInsight.daemon.impl.actions.AddImportAction; +import com.intellij.codeInsight.hint.QuestionAction; +import com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction; +import com.intellij.ide.util.MethodCellRenderer; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiFormatUtilBase; +import com.intellij.psi.util.PsiUtil; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.ui.popup.list.PopupListElementRenderer; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class StaticImportMethodQuestionAction implements QuestionAction { + private static final Logger LOG = Logger.getInstance("#" + StaticImportMethodQuestionAction.class.getName()); + private final Project myProject; + private final Editor myEditor; + private List myCandidates; + private final SmartPsiElementPointer myMethodCall; + + public StaticImportMethodQuestionAction(Project project, + Editor editor, + List candidates, + SmartPsiElementPointer methodCall) { + myProject = project; + myEditor = editor; + myCandidates = candidates; + myMethodCall = methodCall; + } + + @Override + public boolean execute() { + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + + final PsiMethodCallExpression element = myMethodCall.getElement(); + if (element == null || !element.isValid()){ + return false; + } + + for (PsiMethod targetMethod : myCandidates) { + if (!targetMethod.isValid()) { + return false; + } + } + + if (myCandidates.size() == 1){ + doImport(myCandidates.get(0)); + } + else{ + chooseAndImport(myEditor, myProject); + } + return true; + } + + private void doImport(final PsiMethod toImport) { + final Project project = toImport.getProject(); + CommandProcessor.getInstance().executeCommand(project, new Runnable(){ + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + PsiMethodCallExpression element = myMethodCall.getElement(); + if (element != null) { + AddSingleMemberStaticImportAction.bindAllClassRefs(element.getContainingFile(), toImport, toImport.getName(), toImport.getContainingClass()); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + }); + + } + }, QuickFixBundle.message("add.import"), this); + + } + + private void chooseAndImport(final Editor editor, final Project project) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + doImport(myCandidates.get(0)); + return; + } + final BaseListPopupStep step = + new BaseListPopupStep(QuickFixBundle.message("method.to.import.chooser.title"), myCandidates) { + + @Override + public PopupStep onChosen(PsiMethod selectedValue, boolean finalChoice) { + if (selectedValue == null) { + return FINAL_CHOICE; + } + + if (finalChoice) { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + LOG.assertTrue(selectedValue.isValid()); + doImport(selectedValue); + return FINAL_CHOICE; + } + + String qname = PsiUtil.getMemberQualifiedName(selectedValue); + if (qname == null) return FINAL_CHOICE; + List excludableStrings = AddImportAction.getAllExcludableStrings(qname); + return new BaseListPopupStep(null, excludableStrings) { + @NotNull + @Override + public String getTextFor(String value) { + return "Exclude '" + value + "' from auto-import"; + } + + @Override + public PopupStep onChosen(String selectedValue, boolean finalChoice) { + if (finalChoice) { + AddImportAction.excludeFromImport(project, selectedValue); + } + + return super.onChosen(selectedValue, finalChoice); + } + }; + } + + @Override + public boolean hasSubstep(PsiMethod selectedValue) { + return true; + } + + @NotNull + @Override + public String getTextFor(PsiMethod value) { + return ObjectUtils.assertNotNull(value.getName()); + } + + @Override + public Icon getIconFor(PsiMethod aValue) { + return aValue.getIcon(0); + } + }; + + final ListPopupImpl popup = new ListPopupImpl(step) { + final PopupListElementRenderer rightArrow = new PopupListElementRenderer(this); + @Override + protected ListCellRenderer getListElementRenderer() { + return new MethodCellRenderer(true, PsiFormatUtilBase.SHOW_NAME){ + + @Nullable + @Override + protected TextAttributes getNavigationItemAttributes(Object value) { + TextAttributes attrs = super.getNavigationItemAttributes(value); + if (value instanceof PsiMethod && !((PsiMethod)value).isDeprecated()) { + PsiClass psiClass = ((PsiMethod)value).getContainingClass(); + if (psiClass != null && psiClass.isDeprecated()) { + return TextAttributes.merge(attrs, super.getNavigationItemAttributes(psiClass)); + } + } + return attrs; + } + + @Override + protected DefaultListCellRenderer getRightCellRenderer(final Object value) { + final DefaultListCellRenderer moduleRenderer = super.getRightCellRenderer(value); + return new DefaultListCellRenderer(){ + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + JPanel panel = new JPanel(new BorderLayout()); + if (moduleRenderer != null) { + Component moduleComponent = moduleRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + if (!isSelected) { + moduleComponent.setBackground(getBackgroundColor(value)); + } + panel.add(moduleComponent, BorderLayout.CENTER); + } + rightArrow.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + Component rightArrowComponent = rightArrow.getNextStepLabel(); + panel.add(rightArrowComponent, BorderLayout.EAST); + return panel; + } + }; + } + }; + } + }; + popup.showInBestPositionFor(editor); + } +} + + diff --git a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java index 0d570d1c02fc..373f352eeef5 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiMethodReferenceUtil.java @@ -122,7 +122,13 @@ public class PsiMethodReferenceUtil { PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; final PsiExpression expression = methodReferenceExpression.getQualifierExpression(); if (expression != null) { - final PsiType expressionType = replaceArrayType(expression.getType(), expression); + PsiType expressionType = expression.getType(); + if (expressionType instanceof PsiCapturedWildcardType) { + expressionType = ((PsiCapturedWildcardType)expressionType).getUpperBound(); + } + else { + expressionType = replaceArrayType(expressionType, expression); + } PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(expressionType); containingClass = result.getElement(); if (containingClass != null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java index a12f6a21801b..f233311928fd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java @@ -26,6 +26,7 @@ import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -259,7 +260,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm return psiSubstitutor; } - public static PsiSubstitutor getParameterizedTypeSubstitutor(PsiClass qContainingClass, PsiType pType) { + public static PsiSubstitutor getParameterizedTypeSubstitutor(PsiClass qContainingClass, @NotNull PsiType pType) { if (pType instanceof PsiIntersectionType) { for (PsiType type : ((PsiIntersectionType)pType).getConjuncts()) { PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(type); @@ -271,7 +272,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(pType); PsiClass paramClass = resolveResult.getElement(); - LOG.assertTrue(paramClass != null); + LOG.assertTrue(paramClass != null, pType.getCanonicalText()); PsiSubstitutor psiSubstitutor = TypeConversionUtil.getClassSubstitutor(qContainingClass, paramClass, resolveResult.getSubstitutor()); LOG.assertTrue(psiSubstitutor != null); return psiSubstitutor; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/QualifierOfCapturedWildcardType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/QualifierOfCapturedWildcardType.java new file mode 100644 index 000000000000..b4a7cee47b30 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/QualifierOfCapturedWildcardType.java @@ -0,0 +1,11 @@ + +final class Test { + static T create(Iterable extends T> iterable) { + return null; + } + + public static void testError(Iterable extends T> iterable) { + Runnable r = create(iterable)::toString; + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index ddd9485aafe7..735bc541b664 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -482,6 +482,10 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testQualifierOfCapturedWildcardType() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } diff --git a/platform/built-in-server/built-in-server-tests.iml b/platform/built-in-server/built-in-server-tests.iml index af9d31a88f4a..1ee2004f04e2 100644 --- a/platform/built-in-server/built-in-server-tests.iml +++ b/platform/built-in-server/built-in-server-tests.iml @@ -22,6 +22,5 @@ - \ No newline at end of file diff --git a/platform/configuration-store-impl/testSrc/configuration-store-tests.iml b/platform/configuration-store-impl/testSrc/configuration-store-tests.iml index d563be49ab2b..cc0f357793cc 100644 --- a/platform/configuration-store-impl/testSrc/configuration-store-tests.iml +++ b/platform/configuration-store-impl/testSrc/configuration-store-tests.iml @@ -18,6 +18,5 @@ - \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/util/PathUtil.java b/platform/core-api/src/com/intellij/util/PathUtil.java index 7968ff7fe7fd..bc1870143e19 100644 --- a/platform/core-api/src/com/intellij/util/PathUtil.java +++ b/platform/core-api/src/com/intellij/util/PathUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -32,8 +32,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; public class PathUtil { - private PathUtil() { - } + private PathUtil() { } @Nullable public static String getLocalPath(@Nullable VirtualFile file) { @@ -112,12 +111,15 @@ public class PathUtil { return PathUtilRt.isValidFileName(fileName); } + public static boolean isValidFileName(@NotNull String fileName, boolean strict) { + return PathUtilRt.isValidFileName(fileName, strict); + } + @Contract("null -> null; !null -> !null") public static String toSystemIndependentName(@Nullable String path) { return path == null ? null : FileUtilRt.toSystemIndependentName(path); } - @Contract("null -> null; !null -> !null") public static String toSystemDependentName(@Nullable String path) { return path == null ? null : FileUtilRt.toSystemDependentName(path); @@ -136,6 +138,6 @@ public class PathUtil { @NotNull public static String makeFileName(@NotNull String name, @Nullable String extension) { - return name + (StringUtil.isEmpty(extension) ? "" : "." + extension); + return StringUtil.isEmpty(extension) ? name : name + '.' + extension; } -} +} \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/refactoring/util/CommonRefactoringUtil.java b/platform/lang-api/src/com/intellij/refactoring/util/CommonRefactoringUtil.java index ac2606665d72..22cc24d0cf53 100644 --- a/platform/lang-api/src/com/intellij/refactoring/util/CommonRefactoringUtil.java +++ b/platform/lang-api/src/com/intellij/refactoring/util/CommonRefactoringUtil.java @@ -110,7 +110,7 @@ public class CommonRefactoringUtil { return JBIterable.from(elements).transform(new Function() { @Override public PsiElement fun(PsiElement e) { - return e instanceof PsiFileSystemItem ? e.getParent() : e; + return e instanceof PsiFileSystemItem && e.getParent() != null ? e.getParent() : e; } }).toSet(); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/copy/CopyFilesOrDirectoriesDialog.java b/platform/lang-impl/src/com/intellij/refactoring/copy/CopyFilesOrDirectoriesDialog.java index e8dde3f7be7a..91aa9e8bb194 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/copy/CopyFilesOrDirectoriesDialog.java +++ b/platform/lang-impl/src/com/intellij/refactoring/copy/CopyFilesOrDirectoriesDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -23,7 +23,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; -import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.Project; @@ -31,6 +31,8 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.DialogWrapperPeer; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextComponentAccessor; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; @@ -51,7 +53,6 @@ import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import java.awt.*; -import java.io.File; import java.util.List; public class CopyFilesOrDirectoriesDialog extends DialogWrapper { @@ -102,29 +103,25 @@ public class CopyFilesOrDirectoriesDialog extends DialogWrapper { String text; if (elements[0] instanceof PsiFile) { PsiFile file = (PsiFile)elements[0]; - VirtualFile virtualFile = file.getVirtualFile(); - String url = shortenPath(virtualFile); - text = RefactoringBundle.message(doClone ? "copy.files.clone.file.0" : "copy.files.copy.file.0", url); - // keep extensions (dots) and spaces, e.g. fragment file name will be "HTML Fragment (my.sql_61).html" - // and leave ordinary file name AS IS - String fileName = PathUtil.suggestFileName(file.getName(), true, true); - if (StringUtil.isEmpty(virtualFile.getExtension())) { - LanguageFileType type = file.getLanguage().getAssociatedFileType(); - fileName = PathUtil.makeFileName(fileName, ObjectUtils.notNull(type, file.getFileType()).getDefaultExtension()); + VirtualFile vFile = file.getVirtualFile(); + text = RefactoringBundle.message(doClone ? "copy.files.clone.file.0" : "copy.files.copy.file.0", shortenPath(vFile)); + String fileName = vFile.isInLocalFileSystem() ? vFile.getName() : PathUtil.suggestFileName(file.getName(), true, true); + if (StringUtil.isEmpty(vFile.getExtension())) { + FileType type = ObjectUtils.notNull(file.getLanguage().getAssociatedFileType(), file.getFileType()); + fileName = PathUtil.makeFileName(fileName, type.getDefaultExtension()); } myNewNameField.setText(fileName); - final int dotIdx = fileName.lastIndexOf("."); - if (dotIdx > -1) { + int dotIdx = fileName.lastIndexOf('.'); + if (dotIdx > 0) { myNewNameField.select(0, dotIdx); myNewNameField.putClientProperty(DialogWrapperPeer.HAVE_INITIAL_SELECTION, true); } myFileCopy = true; } else { - PsiDirectory directory = (PsiDirectory)elements[0]; - String url = shortenPath(directory.getVirtualFile()); - text = RefactoringBundle.message(doClone ? "copy.files.clone.directory.0" : "copy.files.copy.directory.0", url); - myNewNameField.setText(directory.getName()); + VirtualFile vFile = ((PsiDirectory)elements[0]).getVirtualFile(); + text = RefactoringBundle.message(doClone ? "copy.files.clone.directory.0" : "copy.files.copy.directory.0", shortenPath(vFile)); + myNewNameField.setText(vFile.getName()); } myInformationLabel.setText(text); } @@ -258,7 +255,7 @@ public class CopyFilesOrDirectoriesDialog extends DialogWrapper { return; } - if (myFileCopy && !PathUtil.isValidFileName(newName)) { + if (myFileCopy && !PathUtil.isValidFileName(newName, SystemInfo.isWindows)) { Messages.showErrorDialog(myNewNameField, "Name is not a valid file name"); return; } @@ -283,8 +280,8 @@ public class CopyFilesOrDirectoriesDialog extends DialogWrapper { @Override public void run() { try { - myTargetDirectory = - DirectoryUtil.mkdirs(PsiManager.getInstance(myProject), targetDirectoryName.replace(File.separatorChar, '/')); + String path = FileUtil.toSystemIndependentName(targetDirectoryName); + myTargetDirectory = DirectoryUtil.mkdirs(PsiManager.getInstance(myProject), path); } catch (IncorrectOperationException ignored) { } } @@ -302,15 +299,13 @@ public class CopyFilesOrDirectoriesDialog extends DialogWrapper { } private void validateOKButton() { - if (myShowDirectoryField) { - if (myTargetDirectoryField.getChildComponent().getText().length() == 0) { - setOKActionEnabled(false); - return; - } + if (myShowDirectoryField && myTargetDirectoryField.getChildComponent().getText().length() == 0) { + setOKActionEnabled(false); + return; } if (myShowNewNameField) { - final String newName = getNewName(); - if (newName.length() == 0 || myFileCopy && !PathUtil.isValidFileName(newName)) { + String newName = getNewName(); + if (newName.length() == 0 || myFileCopy && !PathUtil.isValidFileName(newName, SystemInfo.isWindows)) { setOKActionEnabled(false); return; } @@ -322,4 +317,4 @@ public class CopyFilesOrDirectoriesDialog extends DialogWrapper { protected void doHelpAction() { HelpManager.getInstance().invokeHelp("refactoring.copyClass"); } -} +} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/components/AbstractScrollBarUI.java b/platform/platform-api/src/com/intellij/ui/components/AbstractScrollBarUI.java new file mode 100644 index 000000000000..10ce0f93ccf9 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/AbstractScrollBarUI.java @@ -0,0 +1,509 @@ +/* + * Copyright 2000-2015 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.ui.components; + +import com.intellij.openapi.util.Key; +import com.intellij.ui.JBColor; +import com.intellij.ui.components.JBScrollBar.Alignment; +import com.intellij.util.ui.JBInsets; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.RegionPainter; +import com.intellij.util.ui.UIUtil; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ScrollBarUI; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import static java.awt.Adjustable.VERTICAL; + +/** + * @author Sergey.Malenkov + */ +abstract class AbstractScrollBarUI extends ScrollBarUI { + static final Key> LEADING_AREA = Key.create("PLAIN_SCROLL_BAR_UI_LEADING_AREA");//TODO:support + + private static final JBColor TRACK_BACKGROUND = new JBColor(0xF5F5F5, 0x3c3f41); + private static final JBColor TRACK_FOREGROUND = new JBColor(0xE6E6E6, 0x3C3F41); + + private final Listener myListener = new Listener(); + private final Timer myScrollTimer = new Timer(60, myListener); + + private final Rectangle myThumbBounds = new Rectangle(); + private final Rectangle myTrackBounds = new Rectangle(); + private final Rectangle myLeadingBounds = new Rectangle(); + + private JScrollBar myScrollBar; + + private boolean isTrackVisible; + private boolean isValueCached; + private int myCachedValue; + + abstract int getThickness(); + + abstract int getMinimalThickness(); + + abstract void onTrackHover(boolean hover); + + abstract void onThumbHover(boolean hover); + + abstract void paintTrack(Graphics2D g, int x, int y, int width, int height, JComponent c); + + abstract void paintThumb(Graphics2D g, int x, int y, int width, int height, JComponent c); + + void setTrackVisible(boolean trackVisible) { + if (isTrackVisible != trackVisible) { + isTrackVisible = trackVisible; + repaint(); + } + } + + void repaint() { + if (myScrollBar != null) myScrollBar.repaint(); + } + + void repaint(int x, int y, int width, int height) { + if (myScrollBar != null) myScrollBar.repaint(x, y, width, height); + } + + int scale(int value) { + value = JBUI.scale(value); + //noinspection EnumSwitchStatementWhichMissesCases + switch (UIUtil.getComponentStyle(myScrollBar)) { + case LARGE: + return (int)(value * 1.15); + case SMALL: + return (int)(value * 0.857); + case MINI: + return (int)(value * 0.714); + } + return value; + } + + @Override + public void installUI(JComponent c) { + myScrollBar = (JScrollBar)c; + myScrollBar.setBackground(TRACK_BACKGROUND); + myScrollBar.setForeground(TRACK_FOREGROUND); + myScrollBar.setFocusable(false); + myScrollBar.addMouseListener(myListener); + myScrollBar.addMouseMotionListener(myListener); + myScrollBar.getModel().addChangeListener(myListener); + myScrollBar.addPropertyChangeListener(myListener); + myScrollBar.addFocusListener(myListener); + myScrollTimer.setInitialDelay(300); + } + + @Override + public void uninstallUI(JComponent c) { + myScrollTimer.stop(); + myScrollBar.removeFocusListener(myListener); + myScrollBar.removePropertyChangeListener(myListener); + myScrollBar.getModel().removeChangeListener(myListener); + myScrollBar.removeMouseMotionListener(myListener); + myScrollBar.removeMouseListener(myListener); + myScrollBar = null; + } + + @Override + public Dimension getPreferredSize(JComponent c) { + int thickness = getThickness(); + Alignment alignment = Alignment.get(c); + return alignment == Alignment.LEFT || alignment == Alignment.RIGHT + ? new Dimension(thickness, thickness * 3) + : new Dimension(thickness * 3, thickness); + } + + @Override + public void paint(Graphics g, JComponent c) { + Alignment alignment = Alignment.get(c); + if (alignment != null && g instanceof Graphics2D) { + Rectangle bounds = new Rectangle(c.getSize()); + JBInsets.removeFrom(bounds, c.getInsets()); + if (c.isOpaque() && c.getParent() instanceof JScrollPane) { + g.setColor(c.getForeground()); + switch (alignment) { + case TOP: + bounds.height--; + g.drawLine(bounds.x, bounds.y + bounds.height, bounds.x + bounds.width, bounds.y + bounds.height); + break; + case LEFT: + bounds.width--; + g.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height); + break; + case RIGHT: + g.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height); + bounds.width--; + bounds.x++; + break; + case BOTTOM: + g.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y); + bounds.height--; + bounds.y++; + break; + } + } + else if (isTrackVisible) { + paintTrack((Graphics2D)g, bounds.x, bounds.y, bounds.width, bounds.height, c); + } + // process a square area before the track + RegionPainter leading = UIUtil.getClientProperty(c, LEADING_AREA); + if (leading == null) { + myLeadingBounds.setSize(0, 0); + } + else if (alignment == Alignment.LEFT || alignment == Alignment.RIGHT) { + int size = bounds.width; + myLeadingBounds.setBounds(bounds.x, bounds.y, size, size); + leading.paint((Graphics2D)g, bounds.x, bounds.y, size, size, null); + bounds.height -= size; + bounds.y += size; + } + else { + int size = bounds.height; + myLeadingBounds.setBounds(bounds.x, bounds.y, size, size); + leading.paint((Graphics2D)g, bounds.x, bounds.y, size, size, null); + bounds.width -= size; + bounds.x += size; + } + // process additional drawing on the track + myTrackBounds.setBounds(bounds); + RegionPainter track = UIUtil.getClientProperty(c, JBScrollBar.TRACK); + if (track != null && myTrackBounds.width > 0 && myTrackBounds.height > 0) { + track.paint((Graphics2D)g, myTrackBounds.x, myTrackBounds.y, myTrackBounds.width, myTrackBounds.height, null); + } + // process drawing the thumb + updateThumbBounds(); + if (myThumbBounds.width > 0 && myThumbBounds.height > 0) { + paintThumb((Graphics2D)g, myThumbBounds.x, myThumbBounds.y, myThumbBounds.width, myThumbBounds.height, c); + } + } + } + + private void updateThumbBounds() { + int min = myScrollBar.getMinimum(); + int max = myScrollBar.getMaximum(); + int range = max - min; + if (range <= 0) { + myThumbBounds.setBounds(0, 0, 0, 0); + } + else if (VERTICAL == myScrollBar.getOrientation()) { + int extent = myScrollBar.getVisibleAmount(); + int height = Math.max(myTrackBounds.height * extent / range, 2 * getThickness()); + if (myTrackBounds.height <= height) { + myThumbBounds.setBounds(0, 0, 0, 0); + } + else { + int value = getValue(); + int maxY = myTrackBounds.y + myTrackBounds.height - height; + int y = (value < max - extent) ? (myTrackBounds.height - height) * (value - min) / (range - extent) : maxY; + myThumbBounds.setBounds(myTrackBounds.x, adjust(y, myTrackBounds.y, maxY), myTrackBounds.width, height); + } + } + else { + int extent = myScrollBar.getVisibleAmount(); + int width = Math.max(myTrackBounds.width * extent / range, 2 * getThickness()); + if (myTrackBounds.width <= width) { + myThumbBounds.setBounds(0, 0, 0, 0); + } + else { + int value = getValue(); + int maxX = myTrackBounds.x + myTrackBounds.width - width; + int x = (value < max - extent) ? (myTrackBounds.width - width) * (value - min) / (range - extent) : maxX; + myThumbBounds.setBounds(adjust(x, myTrackBounds.x, maxX), myTrackBounds.y, width, myTrackBounds.height); + } + } + } + + private int getValue() { + return isValueCached ? myCachedValue : myScrollBar.getValue(); + } + + private static int adjust(int value, int min, int max) { + return value < min ? min : value > max ? max : value; + } + + private final class Listener extends MouseAdapter implements ActionListener, FocusListener, ChangeListener, PropertyChangeListener { + private int myOffset; + private int myMouseX, myMouseY; + private boolean isReversed; + private boolean isDragging; + private boolean isOverTrack; + private boolean isOverThumb; + + private void updateMouse(int x, int y) { + if (myTrackBounds.contains(x, y)) { + if (!isOverTrack) onTrackHover(isOverTrack = true); + boolean hover = myThumbBounds.contains(x, y); + if (isOverThumb != hover) onThumbHover(isOverThumb = hover); + } + else { + updateMouseExit(); + } + } + + private void updateMouseExit() { + if (isOverThumb) onThumbHover(isOverThumb = false); + if (isOverTrack) onTrackHover(isOverTrack = false); + } + + @Override + public void mousePressed(MouseEvent event) { + if (myScrollBar == null || !myScrollBar.isEnabled()) return; + if (SwingUtilities.isRightMouseButton(event)) return; + + isValueCached = true; + myCachedValue = myScrollBar.getValue(); + myScrollBar.setValueIsAdjusting(true); + + myMouseX = event.getX(); + myMouseY = event.getY(); + + boolean vertical = VERTICAL == myScrollBar.getOrientation(); + if (myThumbBounds.contains(myMouseX, myMouseY)) { + // pressed on the thumb + myOffset = vertical ? (myMouseY - myThumbBounds.y) : (myMouseX - myThumbBounds.x); + isDragging = true; + } + else if (isTrackVisible && myTrackBounds.contains(myMouseX, myMouseY)) { + // pressed on the track + if (SwingUtilities.isMiddleMouseButton(event)) { + myOffset = (vertical ? myThumbBounds.height : myThumbBounds.width) / 2; + isDragging = true; + setValueFrom(event); + } + else { + myScrollTimer.stop(); + isDragging = false; + if (VERTICAL == myScrollBar.getOrientation()) { + int y = myThumbBounds.isEmpty() ? myScrollBar.getHeight() / 2 : myThumbBounds.y; + isReversed = myMouseY < y; + } + else { + int x = myThumbBounds.isEmpty() ? myScrollBar.getWidth() / 2 : myThumbBounds.x; + isReversed = myMouseX < x; + if (!myScrollBar.getComponentOrientation().isLeftToRight()) { + isReversed = !isReversed; + } + } + scroll(isReversed); + startScrollTimerIfNecessary(); + } + } + } + + @Override + public void mouseReleased(MouseEvent event) { + if (isDragging) updateMouse(event.getX(), event.getY()); + if (myScrollBar == null || !myScrollBar.isEnabled()) return; + if (SwingUtilities.isRightMouseButton(event)) return; + isDragging = false; + myOffset = 0; + myScrollTimer.stop(); + isValueCached = true; + myCachedValue = myScrollBar.getValue(); + myScrollBar.setValueIsAdjusting(false); + repaint(); + } + + @Override + public void mouseDragged(MouseEvent event) { + if (myScrollBar == null || !myScrollBar.isEnabled()) return; + if (myThumbBounds.isEmpty() || SwingUtilities.isRightMouseButton(event)) return; + if (isDragging) { + setValueFrom(event); + } + else { + myMouseX = event.getX(); + myMouseY = event.getY(); + updateMouse(myMouseX, myMouseY); + startScrollTimerIfNecessary(); + } + } + + @Override + public void mouseMoved(MouseEvent event) { + if (myScrollBar == null || !myScrollBar.isEnabled()) return; + if (!isDragging) updateMouse(event.getX(), event.getY()); + } + + @Override + public void mouseExited(MouseEvent event) { + if (myScrollBar == null || !myScrollBar.isEnabled()) return; + if (!isDragging) updateMouseExit(); + } + + @Override + public void actionPerformed(ActionEvent event) { + if (myScrollBar == null) { + myScrollTimer.stop(); + } + else { + scroll(isReversed); + if (!myThumbBounds.isEmpty()) { + if (isReversed ? !isMouseBeforeThumb() : !isMouseAfterThumb()) { + myScrollTimer.stop(); + } + } + int value = myScrollBar.getValue(); + if (isReversed ? value <= myScrollBar.getMinimum() : value >= myScrollBar.getMaximum() - myScrollBar.getVisibleAmount()) { + myScrollTimer.stop(); + } + } + } + + @Override + public void focusGained(FocusEvent event) { + repaint(); + } + + @Override + public void focusLost(FocusEvent event) { + repaint(); + } + + @Override + public void stateChanged(ChangeEvent event) { + updateThumbBounds(); + // TODO: update mouse + isValueCached = false; + repaint(); + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + String name = event.getPropertyName(); + if ("model" == name) { + BoundedRangeModel oldModel = (BoundedRangeModel)event.getOldValue(); + BoundedRangeModel newModel = (BoundedRangeModel)event.getNewValue(); + oldModel.removeChangeListener(this); + newModel.addChangeListener(this); + } + if ("model" == name || "orientation" == name || "componentOrientation" == name) { + repaint(); + } + } + + private void setValueFrom(MouseEvent event) { + int x = event.getX(); + int y = event.getY(); + + int thumbMin, thumbMax, thumbPos; + if (VERTICAL == myScrollBar.getOrientation()) { + thumbMin = myTrackBounds.y; + thumbMax = myTrackBounds.y + myTrackBounds.height - myThumbBounds.height; + thumbPos = Math.min(thumbMax, Math.max(thumbMin, (y - myOffset))); + if (myThumbBounds.y != thumbPos) { + int minY = Math.min(myThumbBounds.y, thumbPos); + int maxY = Math.max(myThumbBounds.y, thumbPos) + myThumbBounds.height; + myThumbBounds.y = thumbPos; + repaint(myThumbBounds.x, minY, myThumbBounds.width, maxY - minY); + } + } + else { + thumbMin = myTrackBounds.x; + thumbMax = myTrackBounds.x + myTrackBounds.width - myThumbBounds.width; + thumbPos = Math.min(thumbMax, Math.max(thumbMin, (x - myOffset))); + if (myThumbBounds.x != thumbPos) { + int minX = Math.min(myThumbBounds.x, thumbPos); + int maxX = Math.max(myThumbBounds.x, thumbPos) + myThumbBounds.width; + myThumbBounds.x = thumbPos; + repaint(minX, myThumbBounds.y, maxX - minX, myThumbBounds.height); + } + } + int valueMin = myScrollBar.getMinimum(); + int valueMax = myScrollBar.getMaximum() - myScrollBar.getVisibleAmount(); + // If the thumb has reached the end of the scrollbar, then just set the value to its maximum. + // Otherwise compute the value as accurately as possible. + boolean isDefaultOrientation = VERTICAL == myScrollBar.getOrientation() || myScrollBar.getComponentOrientation().isLeftToRight(); + if (thumbPos == thumbMax) { + myScrollBar.setValue(isDefaultOrientation ? valueMax : valueMin); + } + else { + int valueRange = valueMax - valueMin; + int thumbRange = thumbMax - thumbMin; + int thumbValue = isDefaultOrientation + ? thumbPos - thumbMin + : thumbMax - thumbPos; + isValueCached = true; + myCachedValue = valueMin + valueRange * thumbValue / thumbRange; + myScrollBar.setValue(myCachedValue); + } + if (!isDragging) updateMouse(x, y); + } + + private void startScrollTimerIfNecessary() { + if (!myScrollTimer.isRunning()) { + if (isReversed ? isMouseBeforeThumb() : isMouseAfterThumb()) { + myScrollTimer.start(); + } + } + } + + private boolean isMouseBeforeThumb() { + return VERTICAL == myScrollBar.getOrientation() + ? isMouseOnTop() + : myScrollBar.getComponentOrientation().isLeftToRight() + ? isMouseOnLeft() + : isMouseOnRight(); + } + + private boolean isMouseAfterThumb() { + return VERTICAL == myScrollBar.getOrientation() + ? isMouseOnBottom() + : myScrollBar.getComponentOrientation().isLeftToRight() + ? isMouseOnRight() + : isMouseOnLeft(); + } + + private boolean isMouseOnTop() { + return myMouseY < myThumbBounds.y; + } + + private boolean isMouseOnLeft() { + return myMouseX < myThumbBounds.x; + } + + private boolean isMouseOnRight() { + return myMouseX > myThumbBounds.x + myThumbBounds.width; + } + + private boolean isMouseOnBottom() { + return myMouseY > myThumbBounds.y + myThumbBounds.height; + } + + private void scroll(boolean reversed) { + int delta = myScrollBar.getBlockIncrement(reversed ? -1 : 1); + if (reversed) delta = -delta; + + int oldValue = myScrollBar.getValue(); + int newValue = oldValue + delta; + + if (delta > 0 && newValue < oldValue) { + newValue = myScrollBar.getMaximum(); + } + else if (delta < 0 && newValue > oldValue) { + newValue = myScrollBar.getMinimum(); + } + if (oldValue != newValue) { + myScrollBar.setValue(newValue); + } + } + } +} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.java b/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.java new file mode 100644 index 000000000000..029c3b707d84 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/DefaultScrollBarUI.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2015 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.ui.components; + +import com.intellij.ui.Gray; +import com.intellij.ui.JBColor; +import com.intellij.ui.components.JBScrollBar.Alignment; + +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.awt.Graphics2D; +import javax.swing.JComponent; + +/** + * @author Sergey.Malenkov + */ +final class DefaultScrollBarUI extends AbstractScrollBarUI { + private static final JBColor THUMB_BACKGROUND = new JBColor(0x808080, 0x808080); + private static final JBColor THUMB_FOREGROUND = new JBColor(0x6E6E6E, 0x949494); + + private float myTrackValue; + private float myThumbValue; + + @Override + int getThickness() { + return scale(14); + } + + @Override + int getMinimalThickness() { + return scale(10); + } + + @Override + void onTrackHover(boolean hover) { + if (hover) { + myTrackAnimator.startForward(); + } + else { + myTrackAnimator.startBackward(); + } + } + + @Override + void onThumbHover(boolean hover) { + if (hover) { + myThumbAnimator.startForward(); + } + else { + myThumbAnimator.startBackward(); + } + } + + @Override + void paintTrack(Graphics2D g, int x, int y, int width, int height, JComponent c) { + Composite old = g.getComposite(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .1f * myTrackValue)); + g.setColor(Gray.x80); + g.fillRect(x, y, width, height); + g.setComposite(old); + } + + @Override + void paintThumb(Graphics2D g, int x, int y, int width, int height, JComponent c) { + Composite old = g.getComposite(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .4f + .1f * myThumbValue)); + if (!c.isOpaque()) { + Alignment alignment = Alignment.get(c); + if (alignment == Alignment.LEFT || alignment == Alignment.RIGHT) { + int offset = width - getMinimalThickness(); + if (offset > 0) { + offset *= 1 - myThumbValue; + width -= offset; + if (alignment == Alignment.RIGHT) x += offset; + } + } + else { + int offset = height - getMinimalThickness(); + if (offset > 0) { + offset *= 1 - myThumbValue; + height -= offset; + if (alignment == Alignment.BOTTOM) y += offset; + } + } + } + g.setColor(THUMB_BACKGROUND); + g.fillRect(x + 1, y + 1, width - 2, height - 2); + g.setColor(THUMB_FOREGROUND); + g.drawRect(x, y, width - 1, height - 1); + g.setComposite(old); + } + + private TwoWayAnimator myTrackAnimator = new TwoWayAnimator("ScrollBarTrack", 6, 300, 300, 1000) { + @Override + void onFrame(int frame, int maxFrame) { + myTrackValue = (float)frame / maxFrame; + setTrackVisible(frame > 0); + repaint(); + } + }; + + private TwoWayAnimator myThumbAnimator = new TwoWayAnimator("ScrollBarThumb", 5, 125, 300, 1000) { + @Override + void onFrame(int frame, int maxFrame) { + myThumbValue = (float)frame / maxFrame; + repaint(); + } + }; +} diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java index 0ae88e5efe9c..e9a4b1bd4861 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java @@ -15,15 +15,20 @@ */ package com.intellij.ui.components; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeGlassPane; import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBScrollBar.Alignment; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; import com.intellij.util.ui.ButtonlessScrollBarUI; +import com.intellij.util.ui.JBInsets; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.border.LineBorder; import javax.swing.plaf.ScrollBarUI; import javax.swing.plaf.ScrollPaneUI; @@ -67,7 +72,7 @@ public class JBScrollPane extends JScrollPane { Container vp = c.getParent(); if (vp instanceof JViewport) c = vp; } - + c = c.getParent(); if (!(c instanceof JScrollPane)) return null; @@ -77,10 +82,10 @@ public class JBScrollPane extends JScrollPane { private void init() { init(true); } - + private void init(boolean setupCorners) { - setLayout(new ScrollPaneLayout()); - + setLayout(Registry.is("ide.scroll.new.layout") ? new Layout() : new ScrollPaneLayout()); + if (setupCorners) { setupCorners(); } @@ -102,9 +107,21 @@ public class JBScrollPane extends JScrollPane { @Override public boolean isOptimizedDrawingEnabled() { + if (getLayout() instanceof Layout) { + return isOptimizedDrawingEnabledFor(getVerticalScrollBar()) && + isOptimizedDrawingEnabledFor(getHorizontalScrollBar()); + } return !myHasOverlayScrollbars; } + /** + * Returns {@code false} for visible translucent scroll bars, or {@code true} otherwise. + * It is needed to repaint translucent scroll bars on viewport repainting. + */ + private static boolean isOptimizedDrawingEnabledFor(JScrollBar bar) { + return bar == null || bar.isOpaque() || !bar.isVisible(); + } + private void updateViewportBorder() { setViewportBorder(new ViewportBorder(myViewportBorderWidth >= 0 ? myViewportBorderWidth : 1)); } @@ -138,8 +155,10 @@ public class JBScrollPane extends JScrollPane { // Now we let JScrollPane layout everything as necessary super.layout(); + if (layout instanceof Layout) return; + if (scrollLayout != null) { - // Now it's time to jump in and expand the viewport so it fits the whole area + // Now it's time to jump in and expand the viewport so it fits the whole area // (taking into consideration corners, headers and other stuff). myHasOverlayScrollbars = relayoutScrollbars( this, scrollLayout, @@ -154,7 +173,7 @@ public class JBScrollPane extends JScrollPane { private boolean relayoutScrollbars(@NotNull JComponent container, @NotNull ScrollPaneLayout layout, boolean forceRelayout) { JViewport viewport = layout.getViewport(); if (viewport == null) return false; - + JScrollBar vsb = layout.getVerticalScrollBar(); JScrollBar hsb = layout.getHorizontalScrollBar(); JViewport colHead = layout.getColumnHeader(); @@ -225,7 +244,7 @@ public class JBScrollPane extends JScrollPane { protected boolean isOverlaidScrollbar(@Nullable JScrollBar scrollbar) { if (!ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) return false; - + ScrollBarUI vsbUI = scrollbar == null ? null : scrollbar.getUI(); return vsbUI instanceof ButtonlessScrollBarUI && !((ButtonlessScrollBarUI)vsbUI).alwaysShowTrack(); } @@ -242,7 +261,9 @@ public class JBScrollPane extends JScrollPane { @Override public void updateUI() { - setUI(ButtonlessScrollBarUI.createNormal()); + setUI(!SystemInfo.isMac && Registry.is("ide.scroll.new.layout") + ? new DefaultScrollBarUI() + : ButtonlessScrollBarUI.createNormal()); } @Override @@ -327,4 +348,303 @@ public class JBScrollPane extends JScrollPane { lineColor = view.getBackground(); } } + + /** + * These client properties modify a scroll pane layout. + * Use the class object as a property key. + * + * @see #putClientProperty(Object, Object) + */ + public enum Flip { + NONE, VERTICAL, HORIZONTAL, BOTH + } + + /** + * ScrollPaneLayout implementation that supports + * ScrollBar flipping and non-opaque ScrollBars. + */ + private static class Layout extends ScrollPaneLayout { + private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); + + @Override + public void layoutContainer(Container parent) { + JScrollPane pane = (JScrollPane)parent; + // Calculate inner bounds of the scroll pane + Rectangle bounds = new Rectangle(pane.getWidth(), pane.getHeight()); + JBInsets.removeFrom(bounds, pane.getInsets()); + // Determine positions of scroll bars on the scroll pane + Object property = pane.getClientProperty(Flip.class); + Flip flip = property instanceof Flip ? (Flip)property : Flip.NONE; + boolean hsbOnTop = flip == Flip.BOTH || flip == Flip.VERTICAL; + boolean vsbOnLeft = pane.getComponentOrientation().isLeftToRight() + ? flip == Flip.BOTH || flip == Flip.HORIZONTAL + : flip == Flip.NONE || flip == Flip.VERTICAL; + // If there's a visible row header remove the space it needs. + // The row header is treated as if it were fixed width, arbitrary height. + Rectangle rowHeadBounds = new Rectangle(bounds.x, 0, 0, 0); + if (rowHead != null && rowHead.isVisible()) { + rowHeadBounds.width = min(bounds.width, rowHead.getPreferredSize().width); + bounds.width -= rowHeadBounds.width; + if (vsbOnLeft) { + rowHeadBounds.x += bounds.width; + } + else { + bounds.x += rowHeadBounds.width; + } + } + // If there's a visible column header remove the space it needs. + // The column header is treated as if it were fixed height, arbitrary width. + Rectangle colHeadBounds = new Rectangle(0, bounds.y, 0, 0); + if (colHead != null && colHead.isVisible()) { + colHeadBounds.height = min(bounds.height, colHead.getPreferredSize().height); + bounds.height -= colHeadBounds.height; + if (hsbOnTop) { + colHeadBounds.y += bounds.height; + } + else { + bounds.y += colHeadBounds.height; + } + } + // If there's a JScrollPane.viewportBorder, remove the space it occupies + Border border = pane.getViewportBorder(); + Insets insets = border == null ? null : border.getBorderInsets(parent); + JBInsets.removeFrom(bounds, insets); + if (insets == null) insets = EMPTY_INSETS; + // At this point: + // colHeadBounds is correct except for its width and x + // rowHeadBounds is correct except for its height and y + // bounds - the space available for the viewport and scroll bars + // Once we're through computing the dimensions of these three parts + // we can go back and set the bounds for the corners and the dimensions of + // colHeadBounds.x, colHeadBounds.width, rowHeadBounds.y, rowHeadBounds.height. + boolean isEmpty = bounds.width < 0 || bounds.height < 0; + Component view = viewport == null ? null : viewport.getView(); + Dimension viewPreferredSize = view == null ? new Dimension() : view.getPreferredSize(); + Dimension viewportExtentSize = viewport == null ? new Dimension() : viewport.toViewCoordinates(bounds.getSize()); + // If the view is tracking the viewports width we don't bother with a horizontal scrollbar. + // If the view is tracking the viewports height we don't bother with a vertical scrollbar. + Scrollable scrollable = null; + boolean viewTracksViewportWidth = false; + boolean viewTracksViewportHeight = false; + // Don't bother checking the Scrollable methods if there is no room for the viewport, + // we aren't going to show any scroll bars in this case anyway. + if (!isEmpty && view instanceof Scrollable) { + scrollable = (Scrollable)view; + viewTracksViewportWidth = scrollable.getScrollableTracksViewportWidth(); + viewTracksViewportHeight = scrollable.getScrollableTracksViewportHeight(); + } + // If there's a vertical scroll bar and we need one, allocate space for it. + // A vertical scroll bar is considered to be fixed width, arbitrary height. + boolean vsbOpaque = false; + boolean vsbNeeded = false; + int vsbPolicy = pane.getVerticalScrollBarPolicy(); + if (!isEmpty && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) { + vsbNeeded = vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS + || !viewTracksViewportHeight && viewPreferredSize.height > viewportExtentSize.height; + } + Rectangle vsbBounds = new Rectangle(0, bounds.y - insets.top, 0, 0); + if (vsb != null) { + vsbOpaque = vsb.isOpaque(); + if (vsbNeeded) { + adjustForVSB(bounds, insets, vsbBounds, vsbOpaque, vsbOnLeft); + if (vsbOpaque && viewport != null) { + viewportExtentSize = viewport.toViewCoordinates(bounds.getSize()); + } + } + } + // If there's a horizontal scroll bar and we need one, allocate space for it. + // A horizontal scroll bar is considered to be fixed height, arbitrary width. + boolean hsbOpaque = false; + boolean hsbNeeded = false; + int hsbPolicy = pane.getHorizontalScrollBarPolicy(); + if (!isEmpty && hsbPolicy != HORIZONTAL_SCROLLBAR_NEVER) { + hsbNeeded = hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS + || !viewTracksViewportWidth && viewPreferredSize.width > viewportExtentSize.width; + } + Rectangle hsbBounds = new Rectangle(bounds.x - insets.left, 0, 0, 0); + if (hsb != null) { + hsbOpaque = hsb.isOpaque(); + if (hsbNeeded) { + adjustForHSB(bounds, insets, hsbBounds, hsbOpaque, hsbOnTop); + if (hsbOpaque && viewport != null) { + // If we added the horizontal scrollbar and reduced the vertical space + // we may have to add the vertical scrollbar, if that hasn't been done so already. + if (vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) { + viewportExtentSize = viewport.toViewCoordinates(bounds.getSize()); + vsbNeeded = viewPreferredSize.height > viewportExtentSize.height; + if (vsbNeeded) adjustForVSB(bounds, insets, vsbBounds, vsbOpaque, vsbOnLeft); + } + } + } + } + // Set the size of the viewport first, and then recheck the Scrollable methods. + // Some components base their return values for the Scrollable methods on the size of the viewport, + // so that if we don't ask after resetting the bounds we may have gotten the wrong answer. + if (viewport != null) { + viewport.setBounds(bounds); + pane.setComponentZOrder(viewport, pane.getComponentCount() - 1); + if (scrollable != null && hsbOpaque && vsbOpaque) { + viewTracksViewportWidth = scrollable.getScrollableTracksViewportWidth(); + viewTracksViewportHeight = scrollable.getScrollableTracksViewportHeight(); + viewportExtentSize = viewport.toViewCoordinates(bounds.getSize()); + + boolean vsbNeededOld = vsbNeeded; + if (vsb != null && vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED) { + boolean vsbNeededNew = !viewTracksViewportHeight && viewPreferredSize.height > viewportExtentSize.height; + if (vsbNeeded != vsbNeededNew) { + vsbNeeded = vsbNeededNew; + if (vsbNeeded) { + adjustForVSB(bounds, insets, vsbBounds, vsbOpaque, vsbOnLeft); + } + else if (vsbOpaque) { + bounds.width += vsbBounds.width; + } + if (vsbOpaque) viewportExtentSize = viewport.toViewCoordinates(bounds.getSize()); + } + } + boolean hsbNeededOld = hsbNeeded; + if (hsb != null && hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED) { + boolean hsbNeededNew = !viewTracksViewportWidth && viewPreferredSize.width > viewportExtentSize.width; + if (hsbNeeded != hsbNeededNew) { + hsbNeeded = hsbNeededNew; + if (hsbNeeded) { + adjustForHSB(bounds, insets, hsbBounds, hsbOpaque, hsbOnTop); + } + else if (hsbOpaque) { + bounds.height += hsbBounds.height; + } + if (hsbOpaque && vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) { + viewportExtentSize = viewport.toViewCoordinates(bounds.getSize()); + vsbNeeded = viewPreferredSize.height > viewportExtentSize.height; + if (vsbNeeded) adjustForVSB(bounds, insets, vsbBounds, vsbOpaque, vsbOnLeft); + } + } + } + if (hsbNeededOld != hsbNeeded || vsbNeededOld != vsbNeeded) { + viewport.setBounds(bounds); + // You could argue that we should recheck the Scrollable methods again until they stop changing, + // but they might never stop changing, so we stop here and don't do any additional checks. + } + } + } + // Set the bounds of the row header. + rowHeadBounds.y = bounds.y - insets.top; + rowHeadBounds.height = bounds.height + insets.top + insets.bottom; + if (rowHead != null) { + rowHead.setBounds(rowHeadBounds); + rowHead.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.RIGHT : Alignment.LEFT); + } + // Set the bounds of the column header. + colHeadBounds.x = bounds.x - insets.left; + colHeadBounds.width = bounds.width + insets.left + insets.right; + if (colHead != null) { + colHead.setBounds(colHeadBounds); + colHead.putClientProperty(Alignment.class, hsbOnTop ? Alignment.BOTTOM : Alignment.TOP); + } + // Set the bounds of the vertical scroll bar. + vsbBounds.y = bounds.y - insets.top; + vsbBounds.height = bounds.height + insets.top + insets.bottom; + if (vsb != null) { + vsb.setVisible(vsbNeeded); + if (vsbNeeded) { + if (vsbOpaque && colHead != null && UIManager.getBoolean("ScrollPane.fillUpperCorner")) { + if ((vsbOnLeft ? upperLeft : upperRight) == null) { + // This is used primarily for GTK L&F, which needs to extend + // the vertical scrollbar to fill the upper corner near the column header. + // Note that we skip this step (and use the default behavior) + // if the user has set a custom corner component. + if (!hsbOnTop) vsbBounds.y -= colHeadBounds.height; + vsbBounds.height += colHeadBounds.height; + } + } + vsb.setBounds(vsbBounds); + vsb.putClientProperty(Alignment.class, vsbOnLeft ? Alignment.LEFT : Alignment.RIGHT); + pane.setComponentZOrder(vsb, 0); + } + // Modify the bounds of the translucent scroll bar. + if (!vsbOpaque) { + if (!vsbOnLeft) vsbBounds.x += vsbBounds.width; + vsbBounds.width = 0; + } + } + // Set the bounds of the horizontal scroll bar. + hsbBounds.x = bounds.x - insets.left; + hsbBounds.width = bounds.width + insets.left + insets.right; + if (hsb != null) { + hsb.setVisible(hsbNeeded); + if (hsbNeeded) { + if (hsbOpaque && rowHead != null && UIManager.getBoolean("ScrollPane.fillLowerCorner")) { + if ((vsbOnLeft ? lowerRight : lowerLeft) == null) { + // This is used primarily for GTK L&F, which needs to extend + // the horizontal scrollbar to fill the lower corner near the row header. + // Note that we skip this step (and use the default behavior) + // if the user has set a custom corner component. + if (!vsbOnLeft) hsbBounds.x -= rowHeadBounds.width; + hsbBounds.width += rowHeadBounds.width; + } + } + hsb.setBounds(hsbBounds); + hsb.putClientProperty(Alignment.class, hsbOnTop ? Alignment.TOP : Alignment.BOTTOM); + } + // Modify the bounds of the translucent scroll bar. + if (!hsbOpaque) { + if (!hsbOnTop) hsbBounds.y += hsbBounds.height; + hsbBounds.height = 0; + } + } + // Set the bounds of the corners. + if (lowerLeft != null) { + lowerLeft.setBounds(vsbOnLeft ? vsbBounds.x : rowHeadBounds.x, + hsbOnTop ? colHeadBounds.y : hsbBounds.y, + vsbOnLeft ? vsbBounds.width : rowHeadBounds.width, + hsbOnTop ? colHeadBounds.height : hsbBounds.height); + } + if (lowerRight != null) { + lowerRight.setBounds(vsbOnLeft ? rowHeadBounds.x : vsbBounds.x, + hsbOnTop ? colHeadBounds.y : hsbBounds.y, + vsbOnLeft ? rowHeadBounds.width : vsbBounds.width, + hsbOnTop ? colHeadBounds.height : hsbBounds.height); + } + if (upperLeft != null) { + upperLeft.setBounds(vsbOnLeft ? vsbBounds.x : rowHeadBounds.x, + hsbOnTop ? hsbBounds.y : colHeadBounds.y, + vsbOnLeft ? vsbBounds.width : rowHeadBounds.width, + hsbOnTop ? hsbBounds.height : colHeadBounds.height); + } + if (upperRight != null) { + upperRight.setBounds(vsbOnLeft ? rowHeadBounds.x : vsbBounds.x, + hsbOnTop ? hsbBounds.y : colHeadBounds.y, + vsbOnLeft ? rowHeadBounds.width : vsbBounds.width, + hsbOnTop ? hsbBounds.height : colHeadBounds.height); + } + } + + private void adjustForVSB(Rectangle bounds, Insets insets, Rectangle vsbBounds, boolean vsbOpaque, boolean vsbOnLeft) { + vsbBounds.width = min(bounds.width, vsb.getPreferredSize().width); + if (vsbOnLeft) { + vsbBounds.x = bounds.x - insets.left/* + vsbBounds.width*/; + if (vsbOpaque) bounds.x += vsbBounds.width; + } + else { + vsbBounds.x = bounds.x + bounds.width + insets.right - vsbBounds.width; + } + if (vsbOpaque) bounds.width -= vsbBounds.width; + } + + private void adjustForHSB(Rectangle bounds, Insets insets, Rectangle hsbBounds, boolean hsbOpaque, boolean hsbOnTop) { + hsbBounds.height = min(bounds.height, hsb.getPreferredSize().height); + if (hsbOnTop) { + hsbBounds.y = bounds.y - insets.top/* + hsbBounds.height*/; + if (hsbOpaque) bounds.y += hsbBounds.height; + } + else { + hsbBounds.y = bounds.y + bounds.height + insets.bottom - hsbBounds.height; + } + if (hsbOpaque) bounds.height -= hsbBounds.height; + } + + private static int min(int one, int two) { + return Math.max(0, Math.min(one, two)); + } + } } diff --git a/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.java b/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.java new file mode 100644 index 000000000000..03b5172a80e2 --- /dev/null +++ b/platform/platform-api/src/com/intellij/ui/components/TwoWayAnimator.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2015 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.ui.components; + +import com.intellij.util.Alarm; +import com.intellij.util.ui.Animator; + +/** + * @author Sergey.Malenkov + */ +abstract class TwoWayAnimator { + private final Alarm myAlarm = new Alarm(); + private final MyAnimator myForwardAnimator; + private final MyAnimator myBackwardAnimator; + + private final int myMaxFrame; + private int myFrame; + + abstract void onFrame(int frame, int maxFrame); + + TwoWayAnimator(String name, int totalFrames, int cycleDuration, int pauseForward, int pauseBackward) { + myMaxFrame = totalFrames - 1; + myForwardAnimator = new MyAnimator(name + "ForwardAnimator", totalFrames, cycleDuration, pauseForward, true); + myBackwardAnimator = new MyAnimator(name + "BackwardAnimator", totalFrames, cycleDuration, pauseBackward, false); + } + + void startForward() { + stop(); + myForwardAnimator.start(); + } + + void startBackward() { + stop(); + myBackwardAnimator.start(); + } + + private void stop() { + myAlarm.cancelAllRequests(); + myForwardAnimator.suspend(); + myBackwardAnimator.suspend(); + } + + private final class MyAnimator extends Animator implements Runnable { + private final int myPause; + + private MyAnimator(String name, int totalFrames, int cycleDuration, int pause, boolean forward) { + super(name, totalFrames, cycleDuration, false, forward); + myPause = pause; + } + + private void start() { + if (isForward() ? myFrame > 0 : myFrame < myMaxFrame) { + run(); + } + else { + myAlarm.addRequest(this, myPause); + } + } + + @Override + public void run() { + reset(); + resume(); + } + + @Override + public void paintNow(int frame, int totalFrames, int cycle) { + if (isForward() ? (frame > myFrame) : (frame < myFrame)) { + myFrame = frame; + onFrame(myFrame, myMaxFrame); + } + } + } +} diff --git a/platform/platform-impl/src/com/intellij/util/UrlImpl.java b/platform/platform-impl/src/com/intellij/util/UrlImpl.java index 6876998e0a03..99b6e629415f 100644 --- a/platform/platform-impl/src/com/intellij/util/UrlImpl.java +++ b/platform/platform-impl/src/com/intellij/util/UrlImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -16,7 +16,6 @@ package com.intellij.util; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -71,7 +70,7 @@ public final class UrlImpl implements Url { @Override public boolean isInLocalFileSystem() { - return StandardFileSystems.FILE_PROTOCOL.equals(scheme); + return URLUtil.FILE_PROTOCOL.equals(scheme); } @Nullable diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 1b7cfa542cb1..b95cdcf16a26 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -95,7 +95,7 @@ file.already.exist=File {0} already exists file.already.exist.title=Extract Include File name.for.extracted.include.file=&Name for extracted include file (default extension: {0}) extract.to.directory=Extract to &directory: -select.target.directory=Select target directory +select.target.directory=Select Target Directory select.target.directory.description=The file will be created in this directory create.directory=Create directory replace.inheritance.from=&Replace with delegation inheritance from: diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java index 28cc4c0730d3..02f43bc8ba3b 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/ProjectFileIndexImpl.java @@ -163,7 +163,7 @@ public class ProjectFileIndexImpl extends FileIndexBase implements ProjectFileIn @Override public String getPackageNameByDirectory(@NotNull VirtualFile dir) { - LOG.assertTrue(dir.isDirectory()); + LOG.assertTrue(dir.isDirectory(), dir.getPresentableUrl()); return myDirectoryIndex.getPackageName(dir); } diff --git a/platform/testFramework/test-framework-java8/FixtureRule.kt b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt similarity index 99% rename from platform/testFramework/test-framework-java8/FixtureRule.kt rename to platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt index 65ca4821c237..3d3a5e18c45c 100644 --- a/platform/testFramework/test-framework-java8/FixtureRule.kt +++ b/platform/testFramework/src/com/intellij/testFramework/FixtureRule.kt @@ -58,6 +58,7 @@ open class ApplicationRule : ExternalResource() { override public final fun before() { IdeaTestApplication.getInstance() TestRunnerUtil.replaceIdeEventQueueSafely() + (PersistentFS.getInstance() as PersistentFSImpl).cleanPersistedContents() } } diff --git a/platform/testFramework/test-framework-java8/TemporaryDirectory.kt b/platform/testFramework/src/com/intellij/testFramework/TemporaryDirectory.kt similarity index 100% rename from platform/testFramework/test-framework-java8/TemporaryDirectory.kt rename to platform/testFramework/src/com/intellij/testFramework/TemporaryDirectory.kt diff --git a/platform/testFramework/test-framework-java8/assertJ.kt b/platform/testFramework/src/com/intellij/testFramework/assertJ.kt similarity index 100% rename from platform/testFramework/test-framework-java8/assertJ.kt rename to platform/testFramework/src/com/intellij/testFramework/assertJ.kt diff --git a/platform/testFramework/src/com/intellij/testFramework/fsBuilder.kt b/platform/testFramework/src/com/intellij/testFramework/fsBuilder.kt new file mode 100644 index 000000000000..3488972488e8 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/fsBuilder.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2015 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.testFramework + +import java.nio.file.FileSystem + +fun FileSystem.file(path: String, data: ByteArray): FileSystem { + getPath(path).write(data) + return this +} + +fun FileSystem.file(path: String, data: String) = file(path, data.toByteArray()) \ No newline at end of file diff --git a/platform/testFramework/test-framework-java8/path.kt b/platform/testFramework/src/com/intellij/testFramework/path.kt similarity index 83% rename from platform/testFramework/test-framework-java8/path.kt rename to platform/testFramework/src/com/intellij/testFramework/path.kt index 0e354d5d822a..d2c5f95e763a 100644 --- a/platform/testFramework/test-framework-java8/path.kt +++ b/platform/testFramework/src/com/intellij/testFramework/path.kt @@ -1,3 +1,18 @@ +/* + * Copyright 2000-2015 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.testFramework import com.intellij.openapi.util.io.FileUtil diff --git a/platform/testFramework/test-framework-java8.iml b/platform/testFramework/test-framework-java8.iml deleted file mode 100644 index 05d5f2939193..000000000000 --- a/platform/testFramework/test-framework-java8.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/platform/testFramework/test-framework-java8/fsBuilder.kt b/platform/testFramework/test-framework-java8/fsBuilder.kt deleted file mode 100644 index ea32d71474af..000000000000 --- a/platform/testFramework/test-framework-java8/fsBuilder.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.intellij.testFramework - -import java.nio.file.FileSystem - -fun FileSystem.file(path: String, data: ByteArray): FileSystem { - getPath(path).write(data) - return this -} - -fun FileSystem.file(path: String, data: String) = file(path, data.toByteArray()) \ No newline at end of file diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index 3c86f81a1070..88a71e9c566b 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -1,6 +1,6 @@ - + @@ -25,5 +25,6 @@ + \ No newline at end of file diff --git a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java index b16f54667625..5fee98914ee5 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java @@ -140,7 +140,10 @@ public class UsagePreviewPanel extends UsageContextPanelBase { TextRange elementRange = psiElement.getTextRange(); TextRange infoRange = info.getRangeInElement(); - TextRange textRange = infoRange == null || infoRange.getStartOffset() > elementRange.getLength() ? null : elementRange.cutOut(infoRange); + TextRange textRange = infoRange == null + || infoRange.getStartOffset() > elementRange.getLength() + || infoRange.getEndOffset() > elementRange.getLength() ? null + : elementRange.cutOut(infoRange); if (textRange == null) textRange = elementRange; // hack to determine element range to highlight if (psiElement instanceof PsiNamedElement && !(psiElement instanceof PsiFile)) { diff --git a/platform/util-rt/src/com/intellij/util/PathUtilRt.java b/platform/util-rt/src/com/intellij/util/PathUtilRt.java index b8e9b17972c9..597e3d42b32d 100644 --- a/platform/util-rt/src/com/intellij/util/PathUtilRt.java +++ b/platform/util-rt/src/com/intellij/util/PathUtilRt.java @@ -50,11 +50,11 @@ public class PathUtilRt { } @NotNull - public static String suggestFileName(@NotNull String text, final boolean allowDots, final boolean allowSpaces) { + public static String suggestFileName(@NotNull String text, boolean allowDots, boolean allowSpaces) { StringBuilder result = new StringBuilder(); for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); - if (!isValidFileNameChar(c) || (!allowDots && c == '.') || (!allowSpaces && Character.isWhitespace(c))) { + if (!isValidFileNameChar(c, true) || (!allowDots && c == '.') || (!allowSpaces && Character.isWhitespace(c))) { result.append('_'); } else { @@ -65,19 +65,24 @@ public class PathUtilRt { } public static boolean isValidFileName(@NotNull String fileName) { + return isValidFileName(fileName, true); + } + + public static boolean isValidFileName(@NotNull String fileName, boolean strict) { if (fileName.length() == 0 || fileName.equals(".") || fileName.equals("..")) { return false; } for (int i = 0; i < fileName.length(); i++) { - if (!isValidFileNameChar(fileName.charAt(i))) { + if (!isValidFileNameChar(fileName.charAt(i), strict)) { return false; } } return true; } - private static boolean isValidFileNameChar(char c) { - return c != '/' && c != '\\' && c != '\t' && c != '\n' && c != '\r' && c != ':' && c != ';' && c != '*' && c != '?' && - c != '"' && c != '\'' && c != '<' && c != '>'; + private static boolean isValidFileNameChar(char c, boolean strict) { + if (c == '/' || c == '\\') return false; + if (!strict) return true; + return c != '\t' && c != '\n' && c != '\r' && c != ':' && c != ';' && c != '*' && c != '?' && c != '"' && c != '\'' && c != '<' && c != '>'; } -} +} \ No newline at end of file diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 47ced80e09f2..2272a8c1df7e 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -110,6 +110,7 @@ ide.tree.deferred.icon.invalidates.cache=true ide.splitter.mouseZone=6 ide.smart.horizontal.scrolling=true +ide.scroll.new.layout=false ide.tooltip.callout=true ide.tooltip.animationCycle=150 diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index 4053193a675e..137bf178cc02 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -168,7 +168,11 @@ public class UIUtil { if (isUnderAquaBasedLookAndFeel()) { c.putClientProperty("JComponent.sizeVariant", StringUtil.toLowerCase(componentStyle.name())); } - FontSize fontSize = componentStyle == ComponentStyle.REGULAR ? FontSize.NORMAL : componentStyle == ComponentStyle.SMALL ? FontSize.SMALL : FontSize.MINI; + FontSize fontSize = componentStyle == ComponentStyle.MINI + ? FontSize.MINI + : componentStyle == ComponentStyle.SMALL + ? FontSize.SMALL + : FontSize.NORMAL; c.setFont(getFont(fontSize, c.getFont())); Container p = c.getParent(); if (p != null) { @@ -210,7 +214,7 @@ public class UIUtil { public enum FontSize {NORMAL, SMALL, MINI} - public enum ComponentStyle {REGULAR, SMALL, MINI} + public enum ComponentStyle {LARGE, REGULAR, SMALL, MINI} public enum FontColor {NORMAL, BRIGHTER} @@ -1853,7 +1857,7 @@ public class UIUtil { //noinspection StatementWithEmptyBody while(dispatchInvocationEvent()); } - + @TestOnly public static boolean dispatchInvocationEvent() { assert EdtInvocationManager.getInstance().isEventDispatchThread() : Thread.currentThread() + "; EDT: "+getEventQueueThread(); @@ -1871,7 +1875,7 @@ public class UIUtil { } return true; } - + private static Thread getEventQueueThread() { EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue(); try { @@ -2128,7 +2132,7 @@ public class UIUtil { for (MouseWheelListener each : mouseWheelListeners) { c.removeMouseWheelListener(each); } - + if (c instanceof AbstractButton) { final ActionListener[] listeners = ((AbstractButton)c).getActionListeners(); for (ActionListener listener : listeners) { @@ -2240,7 +2244,7 @@ public class UIUtil { public static HTMLEditorKit getHTMLEditorKit() { return getHTMLEditorKit(true); } - + public static HTMLEditorKit getHTMLEditorKit(boolean noGapsBetweenParagraphs) { Font font = getLabelFont(); @NonNls String family = !SystemInfo.isWindows && font != null ? font.getFamily() : "Tahoma"; @@ -2940,7 +2944,7 @@ public class UIUtil { g.drawString(text, xOffset, yOffset[0]); if (!StringUtil.isEmpty(shortcut)) { Color oldColor = g.getColor(); - g.setColor(new JBColor(new Color(82, 99, 155), + g.setColor(new JBColor(new Color(82, 99, 155), new Color(88, 157, 246))); g.drawString(shortcut, xOffset + fm.stringWidth(text + (isUnderDarcula() ? " " : "")), yOffset[0]); g.setColor(oldColor); @@ -3063,9 +3067,9 @@ public class UIUtil { public static void setNotOpaqueRecursively(@NotNull Component component) { if (!isUnderAquaLookAndFeel()) return; - if (component.getBackground().equals(getPanelBackground()) - || component instanceof JScrollPane - || component instanceof JViewport + if (component.getBackground().equals(getPanelBackground()) + || component instanceof JScrollPane + || component instanceof JViewport || component instanceof JLayeredPane) { if (component instanceof JComponent) { ((JComponent)component).setOpaque(false); @@ -3550,4 +3554,21 @@ public class UIUtil { Component component = policy.getFirstComponent(container); return component instanceof JComponent ? (JComponent)component : null; } + + /** + * Calculates a component style from the corresponding client property. + * The key "JComponent.sizeVariant" is used by Apple's L&F to scale components. + * + * @param component a component to process + * @return a component style of the specified component + */ + public static ComponentStyle getComponentStyle(Component component) { + if (component instanceof JComponent) { + Object property = ((JComponent)component).getClientProperty("JComponent.sizeVariant"); + if ("large".equals(property)) return ComponentStyle.LARGE; + if ("small".equals(property)) return ComponentStyle.SMALL; + if ("mini".equals(property)) return ComponentStyle.MINI; + } + return ComponentStyle.REGULAR; + } } diff --git a/platform/util/testSrc/com/intellij/util/PathUtilTest.java b/platform/util/testSrc/com/intellij/util/PathUtilTest.java new file mode 100644 index 000000000000..db5a54c30d17 --- /dev/null +++ b/platform/util/testSrc/com/intellij/util/PathUtilTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2015 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.util; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PathUtilTest { + @Test + public void isValidFileName() throws Exception { + assertFalse(PathUtilRt.isValidFileName("", false)); + assertFalse(PathUtilRt.isValidFileName(".", false)); + assertFalse(PathUtilRt.isValidFileName("..", false)); + assertFalse(PathUtilRt.isValidFileName("a/b", false)); + assertFalse(PathUtilRt.isValidFileName("a\\b", false)); + + assertFalse(PathUtilRt.isValidFileName("a:b")); + assertTrue(PathUtilRt.isValidFileName("a:b", false)); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 12bb98488a5f..97450393ccb4 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -303,10 +303,10 @@ bundle="com.siyeh.InspectionGadgetsBundle" key="non.short.circuit.boolean.expression.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.probable.bugs" enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.bugs.NonShortCircuitBooleanInspection"/> - + @@ -319,7 +319,8 @@ - diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index bbd02d90902a..c65a8128fb0f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -128,10 +128,10 @@ misspelled.tostring.problem.descriptor=#ref() method should probabl non.short.circuit.boolean.expression.display.name=Non-short-circuit boolean expression non.short.circuit.boolean.expression.problem.descriptor=Non-short-circuit boolean expression #ref #loc non.short.circuit.boolean.expression.replace.quickfix=Replace with short circuit expression -null.argument.to.var.arg.method.display.name=Confusing 'null' argument to var-arg method -null.argument.to.var.arg.method.problem.descriptor=Confusing #ref argument to var-arg method #loc -primitive.array.argument.to.var.arg.method.display.name=Confusing primitive array argument to var-arg method -primitive.array.argument.to.var.arg.method.problem.descriptor=Confusing primitive array argument to var-arg method #loc +null.argument.to.var.arg.method.display.name=Confusing argument to varargs method +null.argument.to.var.arg.method.problem.descriptor=Confusing argument #ref, unclear if a varargs or non-varargs call is desired #loc +primitive.array.argument.to.var.arg.method.display.name=Confusing primitive array argument to varargs method +primitive.array.argument.to.var.arg.method.problem.descriptor=Confusing primitive array argument to varargs method #loc object.comparison.display.name=Object comparison using '==', instead of 'equals()' object.comparison.enumerated.ignore.option=Ignore '==' between enumerated types object.comparison.klass.ignore.option=Ignore '==' on 'java.lang.Class' objects @@ -1874,8 +1874,8 @@ array.hash.code.display.name='hashCode()' called on array array.hash.code.problem.descriptor=#ref() called on array should probably be 'Arrays.hashCode()' #loc arrays.deep.hash.code.quickfix=Replace with 'Arrays.deepHashCode()' arrays.hash.code.quickfix=Replace with 'Arrays.hashCode()' -method.can.be.variable.arity.method.display.name=Method can be variable arity method -method.can.be.variable.arity.method.problem.descriptor=#ref() can be converted to variable arity method #loc +method.can.be.variable.arity.method.display.name=Method can be varargs method +method.can.be.variable.arity.method.problem.descriptor=#ref() can be converted to varargs method #loc method.can.be.variable.arity.method.ignore.byte.short.option=Ignore methods with a last parameter of type byte[] or short[] method.can.be.variable.arity.method.ignore.multiple.arrays.option=Ignore methods with multiple array parameters convert.to.variable.arity.method.quickfix=Convert to varargs method diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java index 6466d73eb4b6..22b7f87f6673 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/NullArgumentToVariableArgMethodInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,29 +15,53 @@ */ package com.siyeh.ig.bugs; +import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeCastFix; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.DelegatingFix; +import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class NullArgumentToVariableArgMethodInspection - extends BaseInspection { +public class NullArgumentToVariableArgMethodInspection extends BaseInspection { + + @NotNull + @Override + public String getID() { + return "ConfusingArgumentToVarargsMethod"; + } + + @Nullable + @Override + public String getAlternativeID() { + return "NullArgumentToVariableArgMethod"; // old suppressions should keep working + } @Override @NotNull public String getDisplayName() { - return InspectionGadgetsBundle.message( - "null.argument.to.var.arg.method.display.name"); + return InspectionGadgetsBundle.message("null.argument.to.var.arg.method.display.name"); } @Override @NotNull public String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "null.argument.to.var.arg.method.problem.descriptor"); + return InspectionGadgetsBundle.message("null.argument.to.var.arg.method.problem.descriptor"); + } + + @NotNull + @Override + protected InspectionGadgetsFix[] buildFixes(Object... infos) { + final PsiExpression argument = (PsiExpression)infos[0]; + final PsiType type1 = (PsiType)infos[1]; + final PsiType type2 = (PsiType)infos[2]; + return new InspectionGadgetsFix[] { + new DelegatingFix(new AddTypeCastFix(type1, argument)), + new DelegatingFix(new AddTypeCastFix(type2, argument)) + }; } @Override @@ -55,21 +79,26 @@ public class NullArgumentToVariableArgMethodInspection return new NullArgumentToVariableArgVisitor(); } - private static class NullArgumentToVariableArgVisitor - extends BaseInspectionVisitor { + private static class NullArgumentToVariableArgVisitor extends BaseInspectionVisitor { @Override - public void visitMethodCallExpression( - @NotNull PsiMethodCallExpression call) { + public void visitMethodCallExpression(@NotNull PsiMethodCallExpression call) { super.visitMethodCallExpression(call); - final PsiExpressionList argumentList = call.getArgumentList(); - final PsiExpression[] args = argumentList.getExpressions(); - if (args.length == 0) { + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { return; } - final PsiExpression lastArg = args[args.length - 1]; - if (!ExpressionUtils.isNullLiteral(lastArg)) { + final PsiExpression lastArgument = arguments[arguments.length - 1]; + final PsiType type = lastArgument.getType(); + final boolean checkArray; + if (PsiType.NULL.equals(type)) { + checkArray = false; + } + else if (type instanceof PsiArrayType) { + checkArray = true; + } + else { return; } final PsiMethod method = call.resolveMethod(); @@ -77,16 +106,26 @@ public class NullArgumentToVariableArgMethodInspection return; } final PsiParameterList parameterList = method.getParameterList(); - if (parameterList.getParametersCount() != args.length) { + if (parameterList.getParametersCount() != arguments.length) { return; } final PsiParameter[] parameters = parameterList.getParameters(); - final PsiParameter lastParameter = - parameters[parameters.length - 1]; + final PsiParameter lastParameter = parameters[parameters.length - 1]; if (!lastParameter.isVarArgs()) { return; } - registerError(lastArg); + final PsiType type1 = lastParameter.getType(); + if (!(type1 instanceof PsiEllipsisType)) { + return; + } + final PsiEllipsisType ellipsisType = (PsiEllipsisType)type1; + final PsiType arrayType = ellipsisType.toArrayType(); + if (checkArray) { + if (arrayType.equals(type) || !arrayType.isAssignableFrom(type)) { + return; + } + } + registerError(lastArgument, lastArgument, ellipsisType.getComponentType(), arrayType); } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java index 9f4a952b51fc..4dc241c12896 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/PrimitiveArrayArgumentToVariableArgMethodInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2014 Dave Griffith, Bas Leijdekkers + * Copyright 2006-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,34 @@ package com.siyeh.ig.bugs; import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeCastFix; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.DelegatingFix; +import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; public class PrimitiveArrayArgumentToVariableArgMethodInspection extends BaseInspection { + @NotNull + @Override + public String getID() { + return "PrimitiveArrayArgumentToVarargsMethod"; + } + + @Nullable + @Override + public String getAlternativeID() { + return "PrimitiveArrayArgumentToVariableArgMethod"; // keep old suppression working + } + @Override @NotNull public String getDisplayName() { @@ -50,6 +66,14 @@ public class PrimitiveArrayArgumentToVariableArgMethodInspection extends BaseIns return PsiUtil.isLanguageLevel5OrHigher(file); } + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiExpression argument = (PsiExpression)infos[0]; + final PsiType type = (PsiType)infos[1]; + return new DelegatingFix(new AddTypeCastFix(type, argument)); + } + @Override public BaseInspectionVisitor buildVisitor() { return new PrimitiveArrayArgumentToVariableArgVisitor(); @@ -84,11 +108,11 @@ public class PrimitiveArrayArgumentToVariableArgMethodInspection extends BaseIns if (!lastParameter.isVarArgs()) { return; } - final PsiType parameterType = lastParameter.getType(); + final PsiEllipsisType parameterType = (PsiEllipsisType)lastParameter.getType(); if (isDeepPrimitiveArrayType(parameterType, result.getSubstitutor())) { return; } - registerError(lastArgument); + registerError(lastArgument, lastArgument, parameterType.getComponentType()); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java index c7502a5af5e3..f124e4f6cb12 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/PrivateMemberAccessBetweenOuterAndInnerClassInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +20,12 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.FileTypeUtils; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.ClassUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -98,8 +98,7 @@ public class PrivateMemberAccessBetweenOuterAndInnerClassInspection } @Override - public void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { + public void doFix(Project project, ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); if (constructor) { makeConstructorPackageLocal(project, element); @@ -127,8 +126,7 @@ public class PrivateMemberAccessBetweenOuterAndInnerClassInspection modifiers.setModifierProperty(PsiModifier.PRIVATE, false); } - private static void makeConstructorPackageLocal(Project project, - PsiElement element) { + private static void makeConstructorPackageLocal(Project project, PsiElement element) { final PsiNewExpression newExpression = PsiTreeUtil.getParentOfType(element, PsiNewExpression.class); @@ -175,8 +173,7 @@ public class PrivateMemberAccessBetweenOuterAndInnerClassInspection return new PrivateMemberAccessFromInnerClassVisitor(); } - private static class PrivateMemberAccessFromInnerClassVisitor - extends BaseInspectionVisitor { + private static class PrivateMemberAccessFromInnerClassVisitor extends BaseInspectionVisitor { @Override public void visitNewExpression(PsiNewExpression expression) { @@ -219,11 +216,9 @@ public class PrivateMemberAccessBetweenOuterAndInnerClassInspection } @Override - public void visitReferenceExpression( - @NotNull PsiReferenceExpression expression) { + public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) { super.visitReferenceExpression(expression); - final PsiElement referenceNameElement = - expression.getReferenceNameElement(); + final PsiElement referenceNameElement = expression.getReferenceNameElement(); if (referenceNameElement == null) { return; } @@ -235,6 +230,10 @@ public class PrivateMemberAccessBetweenOuterAndInnerClassInspection if (!member.hasModifierProperty(PsiModifier.PRIVATE)) { return; } + final Object value = ExpressionUtils.computeConstantExpression(expression); + if (value != null) { + return; // no synthetic accessor created, compile time constant will be inlined by javac + } final PsiElement containingClass = getContainingContextClass(expression); if (containingClass == null) { return; diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodCanBeVariableArityMethod.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodCanBeVariableArityMethod.html index 26e0474ebaed..774c41767144 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodCanBeVariableArityMethod.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodCanBeVariableArityMethod.html @@ -1,19 +1,19 @@ Reports methods with which can be converted to be a variable -arity/varargs method, available in Java 5 and newer. +arity method, available in Java 5 and newer. This inspection only reports if the project or module is configured to use a language level of 5.0 or higher. -Use the first checkbox below to not offer to convert byte[] or short[] parameters to vararg. +Use the first checkbox below to not offer to convert byte[] or short[] parameters to varargs. Use the second checkbox below to ignore methods overriding or implementing a method from a superclass. Use the third checkbox below to only report when the method is public -Use the fourth checkbox below ot ignore method with more than one array parameter. +Use the fourth checkbox below to ignore methods with two or more array parameters. \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html index 5c3508f7d142..dce77b590429 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html @@ -1,9 +1,9 @@ -Reports any calls to a variable-argument method which has a null -in the variable-argument position (e.g System.out.printf("%s", null) ). -Such a null argument may be confusing, as it is not -wrapped as a single-element array, as may be expected. +Reports any calls to a variable arity method where the call has a single argument +in the variable arity parameter position, which is either a null or an array of a subtype of the variable arity parameter. +Such an argument may be confusing as it is unclear if a varargs or non-varargs call is desired. +For example System.out.printf("%s", null). diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html index 4016a7494423..7cc58b83f598 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html @@ -1,7 +1,7 @@ -Reports any calls to a variable-argument method which has a primitive array in -in the variable-argument position (e.g System.out.printf("%s", new int[]{1, 2, 3}) ). +Reports any calls to a variable arity method where the call has a primitive array in +in the variable arity parameter position (e.g System.out.printf("%s", new int[]{1, 2, 3}) ). Such a primitive-array argument may be confusing, as it will wrapped as a single-element array, rather than each individual element being boxed, as might be expected. diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PrivateMemberAccessBetweenOuterAndInnerClass.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PrivateMemberAccessBetweenOuterAndInnerClass.html index 815bfa6930fe..d306240b079e 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PrivateMemberAccessBetweenOuterAndInnerClass.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PrivateMemberAccessBetweenOuterAndInnerClass.html @@ -3,6 +3,9 @@ This inspection is intended for J2ME and other highly resource constrained environments. Applying the results of this inspection without consideration might have negative effects on code clarity and design. +Reports references to non-constant private members of an outer class from inside an inner class or +to members of an inner class from a containing class. + An inner class and its containing class are compiled to separate class files. The virtual machine normally prohibits access from a class to private fields and methods of another class. To enable access from an inner class to private members of a diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html index abf21de69210..b68eeb731b7f 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html @@ -1,6 +1,7 @@ -Reports methods overriding a variable arity (varargs) method with an array parameter. While this is legal Java, it can be confusing. +Reports methods overriding a variable arity method and replacing the variable arity parameter with an array parameter. +While this is legal Java, it can be confusing. diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java index 44d88f74b9dc..43e63a73704c 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java @@ -1,9 +1,10 @@ package com.siyeh.igtest.bugs.null_argument_to_variable_arg_method; public class NullArgumentToVariableArgMethod { - public void foo() + public void foo(String[] ss) { - String.format("%s", null); + String.format("%s", null); String.format("%d", 1); + String.format("%s", ss); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java index 3c4ab826ef53..e56390324c55 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java @@ -6,7 +6,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod { public static void main(String[] arg) throws Throwable { - methodVarArgObject(new byte[3]); + methodVarArgObject(new byte[3]); methodVarArgByteArray(new byte[3]); MethodHandle meh = null; meh.invokeExact(new int[] { }); @@ -31,7 +31,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod } void m() { - String.format("%s", new int[]{1, 2, 3}); + String.format("%s", new int[]{1, 2, 3}); } static void bar1(java.lang.Object... objects) { @@ -48,7 +48,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod public static void invoke() { int[] ints = {1, 2, 3}; - bar1(ints); // warn here + bar1(ints); // warn here bar2(ints); // no warning needed here } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java index a0ebd068824b..3d86bf5f9144 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java @@ -2,12 +2,14 @@ package com.siyeh.igtest.j2me.private_member_access_between_outer_and_inner_clas public class PrivateMemberAccessBetweenOuterAndInnerClass { private String caption = "Button"; + private final int N = 100; private void initialize() { System.out.println(caption); Object btn = new Object() { public void foo() { System.out.println(caption); + System.out.println(N); } }; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java index 3b15deab1620..8eb2aae4014b 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java @@ -7,7 +7,7 @@ public class MethodCanBeVariableArity { public void method(String... s) {} - public void convertMe(String[] ss) {} + public void convertMe(String[] ss) {} public void convertMeNot(byte[] bs) {} } @@ -31,7 +31,7 @@ class Annotated { void m(String[] ss) {} } interface X { - void m(String[] ss); + void m(String[] ss); } class Yes { void m(int[] is, int[] js) {} diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java index bada121a93d8..2f441e4dcf38 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java @@ -117,7 +117,8 @@ public class ReplaceMethodRefWithLambdaIntention extends Intention { final String initialName; if (psiParameters != null) { final int idx = parameterIndex - (isReceiver ? 1 : 0); - initialName = psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName(); + initialName = psiParameters.length > 0 ? psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName() + : parameter.getName(); } else { initialName = parameter.getName(); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java index 09cd1a514945..3b83a9a14793 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java @@ -36,19 +36,29 @@ public class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferen protected PsiReference[] getReferencesByElement(@NotNull final PsiClass aClass, final XmlAttributeValue xmlAttributeValue, ProcessingContext context) { - final PsiField field = aClass.findFieldByName(xmlAttributeValue.getValue(), true); - return new PsiReference[]{new JavaFxControllerFieldRef(xmlAttributeValue, field, aClass)}; + final String name = xmlAttributeValue.getValue(); + PsiMember fieldOrGetterMethod = aClass.findFieldByName(name, true); + if (fieldOrGetterMethod == null) { + final PsiMethod[] methods = aClass.findMethodsByName(name, true); + for (PsiMethod method : methods) { + if (method.getParameterList().getParameters().length == 0) { + fieldOrGetterMethod = method; + break; + } + } + } + return new PsiReference[]{new JavaFxControllerFieldRef(xmlAttributeValue, fieldOrGetterMethod, aClass)}; } public static class JavaFxControllerFieldRef extends PsiReferenceBase { private final XmlAttributeValue myXmlAttributeValue; - private final PsiField myField; + private final PsiMember myFieldOrMethod; private final PsiClass myAClass; - public JavaFxControllerFieldRef(XmlAttributeValue xmlAttributeValue, PsiField field, PsiClass aClass) { + public JavaFxControllerFieldRef(XmlAttributeValue xmlAttributeValue, PsiMember fieldOrMethod, PsiClass aClass) { super(xmlAttributeValue, true); myXmlAttributeValue = xmlAttributeValue; - myField = field; + myFieldOrMethod = fieldOrMethod; myAClass = aClass; } @@ -63,11 +73,11 @@ public class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferen @Nullable @Override public PsiElement resolve() { - return myField != null ? myField : myXmlAttributeValue; + return myFieldOrMethod != null ? myFieldOrMethod : myXmlAttributeValue; } public boolean isUnresolved() { - if (myField == null && myAClass != null) { + if (myFieldOrMethod == null && myAClass != null) { final XmlFile xmlFile = (XmlFile)myXmlAttributeValue.getContainingFile(); if (xmlFile.getRootTag() != null && !JavaFxPsiUtil.isOutOfHierarchy(myXmlAttributeValue)) { return true; diff --git a/plugins/settings-repository/settings-repository-tests.iml b/plugins/settings-repository/settings-repository-tests.iml index d443e18e45cf..a403eda57634 100644 --- a/plugins/settings-repository/settings-repository-tests.iml +++ b/plugins/settings-repository/settings-repository-tests.iml @@ -34,6 +34,5 @@ - \ No newline at end of file diff --git a/resources-en/src/messages/QuickFixBundle.properties b/resources-en/src/messages/QuickFixBundle.properties index b3775aa3095a..506d63d4b51f 100644 --- a/resources-en/src/messages/QuickFixBundle.properties +++ b/resources-en/src/messages/QuickFixBundle.properties @@ -1,5 +1,6 @@ add.import=Add Import class.to.import.chooser.title=Class to Import +method.to.import.chooser.title=Method to Import access.static.via.class.reference.family=Access static via class reference access.static.via.class.reference.text=Access static ''{1}.{0}'' via class ''{2}'' reference add.default.constructor.family=Add Default Constructor
#ref()
#ref
This inspection only reports if the project or module is configured to use a language level of 5.0 or higher.
-Use the first checkbox below to not offer to convert byte[] or short[] parameters to vararg. +Use the first checkbox below to not offer to convert byte[] or short[] parameters to varargs.
Use the second checkbox below to ignore methods overriding or implementing a method from a superclass.
Use the third checkbox below to only report when the method is public
-Use the fourth checkbox below ot ignore method with more than one array parameter. +Use the fourth checkbox below to ignore methods with two or more array parameters.
\ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html index 5c3508f7d142..dce77b590429 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NullArgumentToVariableArgMethod.html @@ -1,9 +1,9 @@
System.out.printf("%s", null)
diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html index 4016a7494423..7cc58b83f598 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PrimitiveArrayArgumentToVariableArgMethod.html @@ -1,7 +1,7 @@
+Reports references to non-constant private members of an outer class from inside an inner class or +to members of an inner class from a containing class. +
An inner class and its containing class are compiled to separate class files. The virtual machine normally prohibits access from a class to private fields and methods of another class. To enable access from an inner class to private members of a diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html index abf21de69210..b68eeb731b7f 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ProblematicVarargsMethodOverride.html @@ -1,6 +1,7 @@
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java index 44d88f74b9dc..43e63a73704c 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/null_argument_to_variable_arg_method/NullArgumentToVariableArgMethod.java @@ -1,9 +1,10 @@ package com.siyeh.igtest.bugs.null_argument_to_variable_arg_method; public class NullArgumentToVariableArgMethod { - public void foo() + public void foo(String[] ss) { - String.format("%s", null); + String.format("%s", null); String.format("%d", 1); + String.format("%s", ss); } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java index 3c4ab826ef53..e56390324c55 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/var_arg/PrimitiveArrayArgumentToVariableArgMethod.java @@ -6,7 +6,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod { public static void main(String[] arg) throws Throwable { - methodVarArgObject(new byte[3]); + methodVarArgObject(new byte[3]); methodVarArgByteArray(new byte[3]); MethodHandle meh = null; meh.invokeExact(new int[] { }); @@ -31,7 +31,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod } void m() { - String.format("%s", new int[]{1, 2, 3}); + String.format("%s", new int[]{1, 2, 3}); } static void bar1(java.lang.Object... objects) { @@ -48,7 +48,7 @@ public class PrimitiveArrayArgumentToVariableArgMethod public static void invoke() { int[] ints = {1, 2, 3}; - bar1(ints); // warn here + bar1(ints); // warn here bar2(ints); // no warning needed here } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java index a0ebd068824b..3d86bf5f9144 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/j2me/private_member_access_between_outer_and_inner_class/PrivateMemberAccessBetweenOuterAndInnerClass.java @@ -2,12 +2,14 @@ package com.siyeh.igtest.j2me.private_member_access_between_outer_and_inner_clas public class PrivateMemberAccessBetweenOuterAndInnerClass { private String caption = "Button"; + private final int N = 100; private void initialize() { System.out.println(caption); Object btn = new Object() { public void foo() { System.out.println(caption); + System.out.println(N); } }; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java index 3b15deab1620..8eb2aae4014b 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/migration/method_can_be_variable_arity_method/MethodCanBeVariableArity.java @@ -7,7 +7,7 @@ public class MethodCanBeVariableArity { public void method(String... s) {} - public void convertMe(String[] ss) {} + public void convertMe(String[] ss) {} public void convertMeNot(byte[] bs) {} } @@ -31,7 +31,7 @@ class Annotated { void m(String[] ss) {} } interface X { - void m(String[] ss); + void m(String[] ss); } class Yes { void m(int[] is, int[] js) {} diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java index bada121a93d8..2f441e4dcf38 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/ReplaceMethodRefWithLambdaIntention.java @@ -117,7 +117,8 @@ public class ReplaceMethodRefWithLambdaIntention extends Intention { final String initialName; if (psiParameters != null) { final int idx = parameterIndex - (isReceiver ? 1 : 0); - initialName = psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName(); + initialName = psiParameters.length > 0 ? psiParameters[idx < psiParameters.length ? idx : psiParameters.length - 1].getName() + : parameter.getName(); } else { initialName = parameter.getName(); diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java index 09cd1a514945..3b83a9a14793 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxFieldIdReferenceProvider.java @@ -36,19 +36,29 @@ public class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferen protected PsiReference[] getReferencesByElement(@NotNull final PsiClass aClass, final XmlAttributeValue xmlAttributeValue, ProcessingContext context) { - final PsiField field = aClass.findFieldByName(xmlAttributeValue.getValue(), true); - return new PsiReference[]{new JavaFxControllerFieldRef(xmlAttributeValue, field, aClass)}; + final String name = xmlAttributeValue.getValue(); + PsiMember fieldOrGetterMethod = aClass.findFieldByName(name, true); + if (fieldOrGetterMethod == null) { + final PsiMethod[] methods = aClass.findMethodsByName(name, true); + for (PsiMethod method : methods) { + if (method.getParameterList().getParameters().length == 0) { + fieldOrGetterMethod = method; + break; + } + } + } + return new PsiReference[]{new JavaFxControllerFieldRef(xmlAttributeValue, fieldOrGetterMethod, aClass)}; } public static class JavaFxControllerFieldRef extends PsiReferenceBase { private final XmlAttributeValue myXmlAttributeValue; - private final PsiField myField; + private final PsiMember myFieldOrMethod; private final PsiClass myAClass; - public JavaFxControllerFieldRef(XmlAttributeValue xmlAttributeValue, PsiField field, PsiClass aClass) { + public JavaFxControllerFieldRef(XmlAttributeValue xmlAttributeValue, PsiMember fieldOrMethod, PsiClass aClass) { super(xmlAttributeValue, true); myXmlAttributeValue = xmlAttributeValue; - myField = field; + myFieldOrMethod = fieldOrMethod; myAClass = aClass; } @@ -63,11 +73,11 @@ public class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferen @Nullable @Override public PsiElement resolve() { - return myField != null ? myField : myXmlAttributeValue; + return myFieldOrMethod != null ? myFieldOrMethod : myXmlAttributeValue; } public boolean isUnresolved() { - if (myField == null && myAClass != null) { + if (myFieldOrMethod == null && myAClass != null) { final XmlFile xmlFile = (XmlFile)myXmlAttributeValue.getContainingFile(); if (xmlFile.getRootTag() != null && !JavaFxPsiUtil.isOutOfHierarchy(myXmlAttributeValue)) { return true; diff --git a/plugins/settings-repository/settings-repository-tests.iml b/plugins/settings-repository/settings-repository-tests.iml index d443e18e45cf..a403eda57634 100644 --- a/plugins/settings-repository/settings-repository-tests.iml +++ b/plugins/settings-repository/settings-repository-tests.iml @@ -34,6 +34,5 @@ - \ No newline at end of file diff --git a/resources-en/src/messages/QuickFixBundle.properties b/resources-en/src/messages/QuickFixBundle.properties index b3775aa3095a..506d63d4b51f 100644 --- a/resources-en/src/messages/QuickFixBundle.properties +++ b/resources-en/src/messages/QuickFixBundle.properties @@ -1,5 +1,6 @@ add.import=Add Import class.to.import.chooser.title=Class to Import +method.to.import.chooser.title=Method to Import access.static.via.class.reference.family=Access static via class reference access.static.via.class.reference.text=Access static ''{1}.{0}'' via class ''{2}'' reference add.default.constructor.family=Add Default Constructor