diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java index 0d965a038ed1..5e026810b52a 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java @@ -15,6 +15,7 @@ */ package com.intellij.execution.filters; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.TextAttributes; @@ -23,14 +24,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.Trinity; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; +import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.BeforeAfter; +import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -38,12 +38,14 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; -public class ExceptionFilter implements Filter, DumbAware { +public class ExceptionFilter implements Filter, DumbAware, FilterMixin { + public static final Color CHANGED_BACKGROUND = new Color(188, 237, 201); private final Project myProject; @NonNls private static final String AT = "at"; private static final String AT_PREFIX = AT + " "; private static final String STANDALONE_AT = " " + AT + " "; private static final TextAttributes HYPERLINK_ATTRIBUTES = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); + private final GlobalSearchScope mySearchScope; public ExceptionFilter(@NotNull final Project project) { @@ -57,7 +59,7 @@ public class ExceptionFilter implements Filter, DumbAware { } @Nullable - static Trinity parseExceptionLine(final String line) { + static Trinity parseExceptionLine(final String line) { int atIndex; if (line.startsWith(AT_PREFIX)){ atIndex = 0; @@ -74,76 +76,178 @@ public class ExceptionFilter implements Filter, DumbAware { if (lparenthIndex < 0) return null; final int lastDotIndex = line.lastIndexOf('.', lparenthIndex); if (lastDotIndex < 0 || lastDotIndex < atIndex) return null; - String className = line.substring(atIndex + AT.length() + 1, lastDotIndex).trim(); - - String methodName = line.substring(lastDotIndex + 1, lparenthIndex).trim(); final int rparenthIndex = line.indexOf(')', lparenthIndex); if (rparenthIndex < 0) return null; - return Trinity.create(className, methodName, new TextRange(lparenthIndex, rparenthIndex)); + // class, method, link + return Trinity.create(adjustedRange(line, atIndex + AT.length() + 1, lastDotIndex), + adjustedRange(line, lastDotIndex + 1, lparenthIndex), new TextRange(lparenthIndex, rparenthIndex)); + } + + private static TextRange adjustedRange(final String line, final int start, final int end) { + String sub = line.substring(start, end); + return new TextRange(start, end - spacesEnd(sub)); + } + + private static int spacesStart(final String s) { + int cnt = 0; + for (int i = 0; i < s.length(); i++) { + final char c = s.charAt(i); + if (! Character.isSpaceChar(c)) return cnt; + ++ cnt; + } + return 0; + } + private static int spacesEnd(final String s) { + int cnt = 0; + for (int i = s.length() - 1; i >= 0; i--) { + final char c = s.charAt(i); + if (! Character.isSpaceChar(c)) return cnt; + ++ cnt; + } + return 0; + } + + // todo do not work internal code + @Override + public void applyHeavyFilter(final String line, final int entireLength, int lineNumber, Consumer consumer) { + final MyWorker worker = new MyWorker(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + worker.execute(line, entireLength); + } + }); + if (worker.getResult() != null) { + // find method range + final PsiMethod[] methodsByName = worker.getPsiClass().findMethodsByName(worker.getMethod(), false); + // todo also go up etc. now just take first + if (methodsByName.length > 0) { + + } + VcsContentAnnotation.Details details = VcsContentAnnotationImpl.getInstance(myProject) + .annotateLine(worker.getFile().getVirtualFile(), new BeforeAfter(-1, -1), lineNumber); + if (details != null) { + if (details.isFileChanged()) { + final int textStartOffset = entireLength - line.length(); + int idx = line.indexOf(':', worker.getInfo().getThird().getStartOffset()); + int endIdx = idx == -1 ? worker.getInfo().getThird().getEndOffset() : idx; + consumer.consume(new AdditionalHighlight(textStartOffset + worker.getInfo().getThird().getStartOffset() + 1, + textStartOffset + endIdx) { + @Override + public TextAttributes getTextAttributes(@Nullable TextAttributes source) { + if (source == null) { + TextAttributes atts = + EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.CLASS_NAME_ATTRIBUTES).clone(); + atts.setBackgroundColor(CHANGED_BACKGROUND); + return atts; + } + TextAttributes clone = source.clone(); + clone.setBackgroundColor(CHANGED_BACKGROUND); + return clone; + } + }); + } + // todo also other + } + } } public Result applyFilter(final String line, final int textEndOffset) { - final Trinity info = parseExceptionLine(line); - if (info == null) { - return null; - } - - String className = info.first; - final int dollarIndex = className.indexOf('$'); - if (dollarIndex >= 0){ - className = className.substring(0, dollarIndex); - } - - final int lparenthIndex = info.third.getStartOffset(); - final int rparenthIndex = info.third.getEndOffset(); - final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim(); - - final int colonIndex = fileAndLine.lastIndexOf(':'); - if (colonIndex < 0) return null; - - final String lineString = fileAndLine.substring(colonIndex + 1); - try{ - final int lineNumber = Integer.parseInt(lineString); - final PsiManager manager = PsiManager.getInstance(myProject); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject()); - PsiClass aClass = psiFacade.findClass(className, mySearchScope); - if (aClass == null) { - aClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); - if (aClass == null) {//try to find class according to all dollars in package name - aClass = psiFacade.findClass(info.first, GlobalSearchScope.allScope(myProject)); - } - if (aClass == null) return null; - } - final PsiFile file = (PsiFile) aClass.getContainingFile().getNavigationElement(); - if (file == null) return null; - - /* - IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name. - final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar); - final int slashIndex = filePath.lastIndexOf(File.separatorChar); - final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1); - if (!file.getName().equalsIgnoreCase(shortFileName)) return null; - */ - - final int textStartOffset = textEndOffset - line.length(); - - final int highlightStartOffset = textStartOffset + lparenthIndex + 1; - final int highlightEndOffset = textStartOffset + rparenthIndex; - VirtualFile virtualFile = file.getVirtualFile(); - final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1); - TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone(); - if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) { - Color color = UIUtil.getInactiveTextColor(); - attributes.setForegroundColor(color); - attributes.setEffectColor(color); - } - return new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes); - } - catch(NumberFormatException e){ - return null; - } + final MyWorker worker = new MyWorker(); + worker.execute(line, textEndOffset); + return worker.getResult(); } + private class MyWorker { + private Result myResult; + private PsiClass myClass; + private PsiFile myFile; + private String myMethod; + private Trinity myInfo; + + public void execute(final String line, final int textEndOffset) { + myInfo = parseExceptionLine(line); + if (myInfo == null) { + return; + } + + myMethod = myInfo.getSecond().substring(line); + String className = myInfo.first.substring(line).trim(); + final int dollarIndex = className.indexOf('$'); + if (dollarIndex >= 0){ + className = className.substring(0, dollarIndex); + } + + final int lparenthIndex = myInfo.third.getStartOffset(); + final int rparenthIndex = myInfo.third.getEndOffset(); + final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim(); + + final int colonIndex = fileAndLine.lastIndexOf(':'); + if (colonIndex < 0) return; + + final String lineString = fileAndLine.substring(colonIndex + 1); + try{ + final int lineNumber = Integer.parseInt(lineString); + final PsiManager manager = PsiManager.getInstance(myProject); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject()); + myClass = psiFacade.findClass(className, mySearchScope); + if (myClass == null) { + myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); + if (myClass == null) {//try to find class according to all dollars in package name + myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); + } + if (myClass == null) return; + } + myFile = (PsiFile) myClass.getContainingFile().getNavigationElement(); + if (myFile == null) return; + + /* + IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name. + final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar); + final int slashIndex = filePath.lastIndexOf(File.separatorChar); + final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1); + if (!file.getName().equalsIgnoreCase(shortFileName)) return null; + */ + + final int textStartOffset = textEndOffset - line.length(); + + final int highlightStartOffset = textStartOffset + lparenthIndex + 1; + final int highlightEndOffset = textStartOffset + rparenthIndex; + VirtualFile virtualFile = myFile.getVirtualFile(); + final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1); + TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone(); + if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) { + Color color = UIUtil.getInactiveTextColor(); + attributes.setForegroundColor(color); + attributes.setEffectColor(color); + } + myResult = new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes); + } + catch(NumberFormatException e){ + // + } + } + + public Result getResult() { + return myResult; + } + + public PsiClass getPsiClass() { + return myClass; + } + + public String getMethod() { + return myMethod; + } + + public PsiFile getFile() { + return myFile; + } + + public Trinity getInfo() { + return myInfo; + } + } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java index 90921e32e1fb..0a87d9541f09 100644 --- a/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java +++ b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java @@ -89,7 +89,8 @@ public class ThreadDumpPanel extends JPanel { }); toolbarActions.add(new CopyToClipboardAction(threadDump, project)); toolbarActions.add(new SortThreadsAction()); - add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions,false).getComponent(), BorderLayout.WEST); + //toolbarActions.add(new ShowRecentlyChanged()); + add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false).getComponent(), BorderLayout.WEST); final Splitter splitter = new Splitter(false, 0.3f); splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myThreadList)); diff --git a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java index 67bfb51c47ed..f663a93d2199 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java @@ -17,13 +17,15 @@ package com.intellij.execution.filters; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.util.Consumer; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class CompositeFilter implements Filter { +public class CompositeFilter implements Filter, FilterMixin { private final List myFilters = new ArrayList(); + private boolean myIsAnyHeavy; private final DumbService myDumbService; public CompositeFilter(Project project) { @@ -48,11 +50,31 @@ public class CompositeFilter implements Filter { return null; } + @Override + public void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer) { + final boolean dumb = myDumbService.isDumb(); + List filters = myFilters; + int count = filters.size(); + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < count; i++) { + Filter filter = filters.get(i); + if (! (filter instanceof FilterMixin)) continue; + if (!dumb || DumbService.isDumbAware(filter)) { + ((FilterMixin) filter).applyHeavyFilter(line, entireLength, lineNumber, consumer); + } + } + } + public boolean isEmpty() { return myFilters.isEmpty(); } + public boolean isAnyHeavy() { + return myIsAnyHeavy; + } + public void addFilter(final Filter filter) { myFilters.add(filter); + myIsAnyHeavy |= filter instanceof FilterMixin; } -} \ No newline at end of file +} diff --git a/platform/lang-api/src/com/intellij/execution/filters/Filter.java b/platform/lang-api/src/com/intellij/execution/filters/Filter.java index 264792ec8723..83ef58fd741f 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/Filter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/Filter.java @@ -44,6 +44,7 @@ public interface Filter { /** * Filters line by creating an instance of {@link Result}. * + * * @param line * The line to be filtered. Note that the line must contain a line * separator at the end. diff --git a/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java new file mode 100644 index 000000000000..175817c59a4f --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2011 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.execution.filters; + +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.Nullable; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 4:50 PM + */ +public interface FilterMixin { + @Nullable + void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer); + + abstract class AdditionalHighlight { + private final int myStart; + private final int myEnd; + + public AdditionalHighlight(int start, int end) { + myStart = start; + myEnd = end; + } + + public int getStart() { + return myStart; + } + + public int getEnd() { + return myEnd; + } + + public abstract TextAttributes getTextAttributes(@Nullable final TextAttributes source); + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index c98211693584..8a062ad00de0 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -41,7 +41,6 @@ import com.intellij.openapi.editor.actions.ToggleUseSoftWrapsToolbarAction; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterClient; @@ -60,6 +59,7 @@ import com.intellij.openapi.keymap.KeymapManager; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MyLayeredPane; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; @@ -75,6 +75,7 @@ import com.intellij.util.Consumer; import com.intellij.util.EditorPopupHandler; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayUtil; +import com.intellij.util.ui.AsyncProcessIcon; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -91,7 +92,7 @@ import java.util.*; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; -public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { +public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { @NonNls private static final String CONSOLE_VIEW_POPUP_MENU = "ConsoleView.PopupMenu"; private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.ConsoleViewImpl"); @@ -115,12 +116,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private Computable myStateForUpdate; private final Alarm mySpareTimeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); + @Nullable + private final Alarm myHeavyAlarm; private final CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); private final ArrayList customActions = new ArrayList(); private final ConsoleBuffer myBuffer = new ConsoleBuffer(); private boolean myUpdateFoldingsEnabled = true; private EditorHyperlinkSupport myHyperlinks; + private AsyncProcessIcon myAsyncProcessIcon; + private JLayeredPane myJLayeredPane; + private JPanel myMainPanel; @TestOnly public Editor getEditor() { @@ -272,7 +278,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo protected ConsoleViewImpl(final Project project, GlobalSearchScope searchScope, boolean viewer, FileType fileType, @NotNull final ConsoleState initialState) { - super(new BorderLayout()); isViewer = viewer; myState = initialState; myPsiDisposedCheck = new DisposedPsiManagerCheck(project); @@ -289,6 +294,11 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myPredefinedMessageFilter.addFilter(filter); } } + if (myPredefinedMessageFilter.isAnyHeavy()) { + myHeavyAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this); + } else { + myHeavyAlarm = null; + } Disposer.register(project, this); } @@ -368,17 +378,34 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } }, 100, - ModalityState.stateForComponent(this) + ModalityState.stateForComponent(myJLayeredPane) ); } } + public void addToolbar(JComponent component, String constraint) { + myMainPanel.add(component, constraint); + } + public JComponent getComponent() { + myJLayeredPane = new MyLayeredPane(); + myJLayeredPane.setLayout(new BorderLayout()); if (myEditor == null) { myEditor = createEditor(); myHyperlinks = new EditorHyperlinkSupport(myEditor, myProject); requestFlushImmediately(); - add(createCenterComponent(), BorderLayout.CENTER); + myMainPanel = new JPanel(new BorderLayout()); + myMainPanel.add(createCenterComponent(), BorderLayout.CENTER); + myJLayeredPane.add(myMainPanel, BorderLayout.CENTER, JLayeredPane.DEFAULT_LAYER); + + myAsyncProcessIcon = new AsyncProcessIcon(toString()).setUseMask(false); + myAsyncProcessIcon.setOpaque(false); + myAsyncProcessIcon.setPaintPassiveIcon(false); + myAsyncProcessIcon.suspend(); + /*JPanel wrapper = new JPanel(new BorderLayout()); + wrapper.add(myAsyncProcessIcon, BorderLayout.NORTH); + wrapper.setOpaque(false);*/ + myJLayeredPane.add(myAsyncProcessIcon, BorderLayout.NORTH, JLayeredPane.DRAG_LAYER); myEditor.getDocument().addDocumentListener(new DocumentAdapter() { public void documentChanged(DocumentEvent e) { @@ -412,7 +439,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } }); } - return this; + return myJLayeredPane; } protected JComponent createCenterComponent() { @@ -476,7 +503,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private ModalityState getStateForUpdate() { - return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); + return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(myJLayeredPane); } private void requestFlushImmediately() { @@ -789,6 +816,46 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo if (canHighlightHyperlinks) { myHyperlinks.highlightHyperlinks(myCustomFilter, myPredefinedMessageFilter, line1, endLine); } + if (myPredefinedMessageFilter.isAnyHeavy()) { + final Document document = getEditor().getDocument(); + final int startLine = Math.max(0, line1); + for (int line = startLine; line <= endLine; line++) { + int endOffset = document.getLineEndOffset(line); + if (endOffset < document.getTextLength()) { + endOffset++; // add '\n' + } + final String lineText = EditorHyperlinkSupport.getLineText(document, line, true); + assert myHeavyAlarm != null; + final int finalEndOffset = endOffset; + final int finalLine = line; + myAsyncProcessIcon.resume(); + myHeavyAlarm.addRequest(new Runnable() { + @Override + public void run() { + myPredefinedMessageFilter.applyHeavyFilter(lineText, finalEndOffset, finalLine, new Consumer() { + @Override + public void consume(final FilterMixin.AdditionalHighlight additionalHighlight) { + SwingUtilities.invokeLater( + new Runnable() { + @Override + public void run() { + myFlushAlarm.addRequest(new Runnable() { + @Override + public void run() { + myHyperlinks.adjustHighlighters(Collections.singletonList(additionalHighlight)); + } + }, 0); + } + }); + } + }); + if (myHeavyAlarm.getActiveRequestCount() == 0) { + myAsyncProcessIcon.suspend(); + } + } + }, 0); + } + } if (myUpdateFoldingsEnabled) { updateFoldings(line1, endLine, true); } @@ -1066,7 +1133,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo s = (String)content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { - consoleView.getToolkit().beep(); + consoleView.getComponent().getToolkit().beep(); } if (s == null) return; Editor editor = consoleView.myEditor; @@ -1279,6 +1346,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo consoleActions[1] = nextAction; consoleActions[2] = switchSoftWrapsAction; consoleActions[3] = autoScrollToTheEndAction; + //consoleActions[4] = new ShowRecentlyChanged(); for (int i = 0; i < customActions.size(); ++i) { consoleActions[i + 4] = customActions.get(i); } diff --git a/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java b/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java new file mode 100644 index 000000000000..678f8b44f9a0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2011 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.unscramble; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.configurable.VcsContentAnnotationConfigurable; + +/** +* Created by IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 8/4/11 +* Time: 2:29 PM +* To change this template use File | Settings | File Templates. +*/ +public class ShowRecentlyChanged extends DumbAwareAction { + public ShowRecentlyChanged() { + super("Show recently changed", "Show recently changed", IconLoader.getIcon("/general/copy.png")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (! enabled(e)) return; + Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + VcsContentAnnotationConfigurable configurable = new VcsContentAnnotationConfigurable(project); + ShowSettingsUtil.getInstance().editConfigurable(project, configurable); + // todo recalculate highlight + } + + private boolean enabled(AnActionEvent e) { + Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project == null) return false; + ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); + if (! vcsManager.hasActiveVcss()) return false; + return true; + } + + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(enabled(e)); + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java index 6e1fd9ca0f2c..22ae559d18ed 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java @@ -258,16 +258,4 @@ public class LoadingDecorator { frame.setBounds(300, 300, 300, 300); frame.show(); } - - - private static class MyLayeredPane extends JLayeredPane { - @Override - public void doLayout() { - super.doLayout(); - for (int i = 0; i < getComponentCount(); i++) { - final Component each = getComponent(i); - each.setBounds(0, 0, getWidth(), getHeight()); - } - } - } } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java b/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java new file mode 100644 index 000000000000..f0dce003be41 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2011 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.openapi.ui; + +import javax.swing.*; +import java.awt.*; + +/** +* Created by IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 8/4/11 +* Time: 3:59 PM +* To change this template use File | Settings | File Templates. +*/ +public class MyLayeredPane extends JLayeredPane { + @Override + public void doLayout() { + super.doLayout(); + for (int i = 0; i < getComponentCount(); i++) { + final Component each = getComponent(i); + if (each instanceof Icon) { + each.setBounds(0, 0, each.getWidth(), each.getHeight()); + } else { + each.setBounds(0, 0, getWidth(), getHeight()); + } + } + } +} diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java index 9b8725ea98b7..49d7a30a7cf7 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java @@ -45,6 +45,7 @@ import java.util.Map; */ public class SimpleColoredComponent extends JComponent implements Accessible { private static final Logger LOG = Logger.getInstance("#com.intellij.ui.SimpleColoredComponent"); + public static final Color STYLE_SEARCH_MATCH_BACKGROUND = new Color(250, 250, 250, 140); private final ArrayList myFragments; private final ArrayList myAttributes; @@ -439,7 +440,7 @@ public class SimpleColoredComponent extends JComponent implements Accessible { if (!attributes.isSearchMatch()) { if (shouldDrawMacShadow()) { - g.setColor(new Color(250, 250, 250, 140)); + g.setColor(STYLE_SEARCH_MATCH_BACKGROUND); g.drawString(fragment, xOffset, textBaseline + 1); } diff --git a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java index 85832860109c..68fb46a6368f 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java @@ -16,6 +16,7 @@ package com.intellij.execution.impl; import com.intellij.execution.filters.Filter; +import com.intellij.execution.filters.FilterMixin; import com.intellij.execution.filters.HyperlinkInfo; import com.intellij.ide.OccurenceNavigator; import com.intellij.openapi.editor.Document; @@ -33,7 +34,9 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.pom.Navigatable; +import com.intellij.util.BeforeAfter; import com.intellij.util.Consumer; +import com.intellij.util.SmartList; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,14 +53,18 @@ import java.util.List; public class EditorHyperlinkSupport { public static final Key OLD_HYPERLINK_TEXT_ATTRIBUTES = Key.create("OLD_HYPERLINK_TEXT_ATTRIBUTES"); private static final int HYPERLINK_LAYER = HighlighterLayer.SELECTION - 123; + private static final int HIGHLIGHT_LAYER = HighlighterLayer.SELECTION - 111; private static final int NO_INDEX = Integer.MIN_VALUE; private final Editor myEditor; private final Map myHighlighterToMessageInfoMap = new HashMap(); private int myLastIndex = NO_INDEX; + private final Consumer> myRefresher; + private final List myHighlighters; public EditorHyperlinkSupport(@NotNull final Editor editor, @NotNull final Project project) { myEditor = editor; + myHighlighters = new SmartList(); editor.addEditorMouseListener(new EditorMouseAdapter() { public void mouseReleased(final EditorMouseEvent e) { @@ -86,10 +93,78 @@ public class EditorHyperlinkSupport { } } ); + + myRefresher = new Consumer>() { + @Override + public void consume(BeforeAfter resultBeforeAfter) { + if (resultBeforeAfter.getBefore() == null) return; + final RangeHighlighter hyperlinkRange = findHyperlinkRange(resultBeforeAfter.getBefore().hyperlinkInfo); + if (hyperlinkRange != null) { + myHighlighterToMessageInfoMap.remove(hyperlinkRange); + } else { + final Iterator iterator = myHighlighters.iterator(); + while (iterator.hasNext()) { + final RangeHighlighter highlighter = iterator.next(); + if (highlighter.isValid() && containsOffset(resultBeforeAfter.getBefore().highlightStartOffset, highlighter)) { + iterator.remove(); + break; + } + } + } + + if (resultBeforeAfter.getAfter() != null) { + if (resultBeforeAfter.getAfter().hyperlinkInfo != null) { + addHyperlink(resultBeforeAfter.getAfter().highlightStartOffset, resultBeforeAfter.getAfter().highlightEndOffset, + resultBeforeAfter.getAfter().highlightAttributes, resultBeforeAfter.getAfter().hyperlinkInfo); + } else if (resultBeforeAfter.getAfter().highlightAttributes != null) { + addHighlighter(resultBeforeAfter.getAfter().highlightStartOffset, resultBeforeAfter.getAfter().highlightEndOffset, + resultBeforeAfter.getAfter().highlightAttributes); + } + } + } + }; + } + + public void adjustHighlighters(final List highlights) { + for (FilterMixin.AdditionalHighlight highlight : highlights) { + RangeHighlighter found = null; + for (RangeHighlighter rangeHighlighter : myHighlighterToMessageInfoMap.keySet()) { + if (rangeHighlighter.getStartOffset() <= highlight.getStart() && rangeHighlighter.getEndOffset() >= highlight.getEnd()) { + found = rangeHighlighter; + break; + } + } + if (found != null) { + TextAttributes textAttributes = highlight.getTextAttributes(found.getTextAttributes()); + final HyperlinkInfo hyperlinkInfo = myHighlighterToMessageInfoMap.remove(found); + if (found.getStartOffset() != highlight.getStart()) { + addHyperlink(found.getStartOffset(), highlight.getEnd(), found.getTextAttributes(), hyperlinkInfo); + } + if (found.getEndOffset() != highlight.getEnd()) { + addHyperlink(highlight.getEnd(), found.getEndOffset(), found.getTextAttributes(), hyperlinkInfo); + } + addHyperlink(highlight.getStart(), highlight.getEnd(), textAttributes, hyperlinkInfo); + myEditor.getMarkupModel().removeHighlighter(found); + return; + } + final Iterator iterator = myHighlighters.iterator(); + while (iterator.hasNext()) { + final RangeHighlighter highlighter = iterator.next(); + if (highlighter.getStartOffset() == highlight.getStart() && highlighter.getEndOffset() == highlight.getEnd()) { + iterator.remove(); + final TextAttributes textAttributes = highlight.getTextAttributes(highlighter.getTextAttributes()); + addHighlighter(highlight.getStart(), highlight.getEnd(), textAttributes); + return; + } + } + final TextAttributes textAttributes = highlight.getTextAttributes(null); + addHighlighter(highlight.getStart(), highlight.getEnd(), textAttributes); + } } public void clearHyperlinks() { myHighlighterToMessageInfoMap.clear(); + myHighlighters.clear(); myLastIndex = NO_INDEX; } @@ -171,12 +246,25 @@ public class EditorHyperlinkSupport { if (result == null) { result = predefinedMessageFilter.applyFilter(text, endOffset); } - if (result != null && result.hyperlinkInfo != null) { - addHyperlink(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes, result.hyperlinkInfo); + if (result != null) { + if (result.hyperlinkInfo != null) { + addHyperlink(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes, result.hyperlinkInfo); + } else if (result.highlightAttributes != null) { + addHighlighter(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes); + } } } } + private void addHighlighter(int highlightStartOffset, int highlightEndOffset, TextAttributes highlightAttributes) { + final RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(highlightStartOffset, + highlightEndOffset, + HIGHLIGHT_LAYER, + highlightAttributes, + HighlighterTargetArea.EXACT_RANGE); + myHighlighters.add(highlighter); + } + private static TextAttributes getHyperlinkAttributes() { return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); } diff --git a/platform/platform-resources/src/componentSets/VCS.xml b/platform/platform-resources/src/componentSets/VCS.xml index 2f0f306727ef..4b06ef8b184e 100644 --- a/platform/platform-resources/src/componentSets/VCS.xml +++ b/platform/platform-resources/src/componentSets/VCS.xml @@ -106,5 +106,10 @@ com.intellij.openapi.vcs.impl.VcsBaseContentProvider com.intellij.openapi.vcs.impl.VcsFileStatusProvider + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings + + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java b/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java new file mode 100644 index 000000000000..573558853e34 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2011 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.openapi.vcs; + +import com.intellij.ui.SimpleTextAttributes; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 12:56 PM + * To change this template use File | Settings | File Templates. + */ +public class RichTextItem { + private final String myText; + private final SimpleTextAttributes myTextAttributes; + + public RichTextItem(String text, SimpleTextAttributes textAttributes) { + myText = text; + myTextAttributes = textAttributes; + } + + public String getText() { + return myText; + } + + public SimpleTextAttributes getTextAttributes() { + return myTextAttributes; + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java new file mode 100644 index 000000000000..500c3f93cad3 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2011 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.openapi.vcs.contentAnnotation; + +import com.intellij.openapi.vcs.RichTextItem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.BeforeAfter; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 12:50 PM + */ +public interface VcsContentAnnotation { + @Nullable + Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber); + + class Details { + private final boolean myLineChanged; + // meaningful enclosing structure + private final boolean myMethodChanged; + private final boolean myFileChanged; + @Nullable + private final List myDetails; + + public Details(boolean lineChanged, boolean methodChanged, boolean fileChanged, List details) { + myLineChanged = lineChanged; + myMethodChanged = methodChanged; + myFileChanged = fileChanged; + myDetails = details; + } + + public boolean isLineChanged() { + return myLineChanged; + } + + public boolean isMethodChanged() { + return myMethodChanged; + } + + public boolean isFileChanged() { + return myFileChanged; + } + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java new file mode 100644 index 000000000000..f52078c65284 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2011 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.openapi.vcs.contentAnnotation; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.diff.DiffMixin; +import com.intellij.openapi.vcs.history.VcsRevisionDescription; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.BeforeAfter; +import org.jetbrains.annotations.Nullable; + +import java.util.Date; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 1:04 PM + */ +public class VcsContentAnnotationImpl implements VcsContentAnnotation { + private final Project myProject; + private final VcsContentAnnotationSettings mySettings; + + public static VcsContentAnnotation getInstance(final Project project) { + return ServiceManager.getService(project, VcsContentAnnotation.class); + } + + public VcsContentAnnotationImpl(Project project, VcsContentAnnotationSettings settings) { + myProject = project; + mySettings = settings; + } + + @Nullable + @Override + public Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber) { + final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); + final AbstractVcs vcs = vcsManager.getVcsFor(vf); + if (vcs == null) return null; + if (vcs.getDiffProvider() instanceof DiffMixin) { + boolean fileRecent = false; + final VcsRevisionDescription description = ((DiffMixin)vcs.getDiffProvider()).getCurrentRevisionDescription(vf); + final Date date = description.getRevisionDate(); + if (date.getTime() > (System.currentTimeMillis() - mySettings.getLimit())) { + fileRecent = true; + } + return new Details(false, false, fileRecent, null); + } + return null; + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java new file mode 100644 index 000000000000..87228feb1dbf --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2011 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.openapi.vcs.contentAnnotation; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 1:13 PM + */ +@State( + name = "VcsContentAnnotationSettings", + storages = {@Storage( file = "$WORKSPACE_FILE$")}) +public class VcsContentAnnotationSettings implements PersistentStateComponent { + // approx + public static final int ourMaxDays = 31; + public final static long ourAbsoluteLimit = ourMaxDays * 24 * 60 * 60 * 1000L; + private State myState = new State(); + + { + myState.myLimit = ourAbsoluteLimit; + } + + public static VcsContentAnnotationSettings getInstance(final Project project) { + return ServiceManager.getService(project, VcsContentAnnotationSettings.class); + } + + public static class State { + public boolean myShow = true; + public long myLimit; + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public long getLimit() { + return myState.myLimit; + } + + public long getLimitDays() { + return myState.myLimit / (24 * 60 * 60 * 1000L); + } + + public void setLimit(long limit) { + myState.myLimit = limit * 24 * 60 * 60 * 1000L; + } + + public boolean isShow() { + return myState.myShow; + } + + public void setShow(final boolean value) { + myState.myShow = value; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java new file mode 100644 index 000000000000..f2d9f4f4b5f4 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2011 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.openapi.vcs.configurable; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings; +import org.jetbrains.annotations.Nls; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/4/11 + * Time: 2:05 PM + */ +public class VcsContentAnnotationConfigurable implements Configurable { + private final Project myProject; + private JCheckBox myHighlightRecentlyChanged; + private JSpinner myHighlightInterval; + + public VcsContentAnnotationConfigurable(Project project) { + myProject = project; + } + + @Nls + @Override + public String getDisplayName() { + return "Show recently changed"; + } + + @Override + public Icon getIcon() { + return null; + } + + @Override + public String getHelpTopic() { + return null; + } + + @Override + public JComponent createComponent() { + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT)); + myHighlightRecentlyChanged = new JCheckBox("Show changed in last"); + myHighlightRecentlyChanged.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + myHighlightInterval = new JSpinner(new SpinnerNumberModel(1, 1, VcsContentAnnotationSettings.ourMaxDays, 1)); + wrapper.add(myHighlightRecentlyChanged); + wrapper.add(myHighlightInterval); + wrapper.add(new JLabel("days")); + + myHighlightRecentlyChanged.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myHighlightInterval.setEnabled(myHighlightRecentlyChanged.isSelected()); + } + }); + return wrapper; + } + + @Override + public boolean isModified() { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + if (myHighlightRecentlyChanged.isSelected() != settings.isShow()) return true; + if (! Comparing.equal(myHighlightInterval.getValue(), settings.getLimitDays())) return true; + return false; + } + + @Override + public void apply() throws ConfigurationException { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + settings.setShow(myHighlightRecentlyChanged.isSelected()); + settings.setLimit(((Number) myHighlightInterval.getValue()).intValue()); + } + + @Override + public void reset() { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + myHighlightRecentlyChanged.setSelected(settings.isShow()); + myHighlightInterval.setValue(settings.getLimitDays()); + myHighlightInterval.setEnabled(myHighlightRecentlyChanged.isSelected()); + } + + @Override + public void disposeUIResources() { + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java index f2b0f790866f..4e5e87c13f45 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.configurable; import com.intellij.CommonBundle; import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.AbstractVcs; @@ -134,6 +135,7 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements private JButton myEditButton; private JButton myRemoveButton; private final Map myAllVcss; + private VcsContentAnnotationConfigurable myRecentlyChangedConfigurable; private final boolean myIsDisabled; public VcsDirectoryConfigurationPanel(final Project project) { @@ -147,6 +149,7 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } myDirectoryMappingTable = new TableView(); + initPanel(); initializeModel(); final JComboBox comboBox = myVcsComboBox.getComboBox(); @@ -172,7 +175,6 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements updateButtons(); } }); - initPanel(); updateButtons(); if (myIsDisabled) { myDirectoryMappingTable.setEnabled(false); @@ -187,6 +189,8 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } myModel = new ListTableModel(new ColumnInfo[]{DIRECTORY, VCS_SETTING}, mappings, 0); myDirectoryMappingTable.setModel(myModel); + + myRecentlyChangedConfigurable.reset(); } private void updateButtons() { @@ -273,19 +277,26 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } protected JComponent createMainComponent() { - return ScrollPaneFactory.createScrollPane(myDirectoryMappingTable); + JPanel panel = new JPanel(new BorderLayout()); + final JScrollPane scroll = ScrollPaneFactory.createScrollPane(myDirectoryMappingTable); + panel.add(scroll, BorderLayout.CENTER); + myRecentlyChangedConfigurable = new VcsContentAnnotationConfigurable(myProject); + panel.add(myRecentlyChangedConfigurable.createComponent(), BorderLayout.SOUTH); + return panel; } public void reset() { initializeModel(); } - public void apply() { + public void apply() throws ConfigurationException { myVcsManager.setDirectoryMappings(myModel.getItems()); + myRecentlyChangedConfigurable.apply(); initializeModel(); } public boolean isModified() { + if (myRecentlyChangedConfigurable.isModified()) return true; return !myModel.getItems().equals(myVcsManager.getDirectoryMappings()); } diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index a69c0cbd97d0..32679c59b42f 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -118,7 +118,6 @@ serviceImplementation="git4idea.DialogManager"/> -