diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java index b6700c6a5ea4..39767266e618 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/SelectedBlockHistoryAction.java @@ -32,6 +32,7 @@ import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl; import com.intellij.openapi.vcs.impl.VcsBackgroundableActions; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; +import com.intellij.util.WaitForProgressToShow; import com.intellij.vcsUtil.VcsSelection; import com.intellij.vcsUtil.VcsSelectionUtil; @@ -77,11 +78,15 @@ public class SelectedBlockHistoryAction extends AbstractVcsAction { final int selectionStart = selection.getSelectionStartLineNumber(); final int selectionEnd = selection.getSelectionEndLineNumber(); + final VcsException[] preloadException = new VcsException[1]; final CachedRevisionsContents cachedRevisionsContents = new CachedRevisionsContents(project, file); new VcsHistoryProviderBackgroundableProxy(activeVcs, provider, activeVcs.getDiffProvider()). createSessionFor(activeVcs.getKeyInstanceMethod(), new FilePathImpl(file), new Consumer() { public void consume(VcsHistorySession session) { + if (preloadException[0] != null) { + reportError(preloadException[0]); + } if (session == null) return; final VcsHistoryDialog vcsHistoryDialog = new VcsHistoryDialog(project, @@ -103,7 +108,12 @@ public class SelectedBlockHistoryAction extends AbstractVcsAction { cachedRevisionsContents.setRevisions(revisionList); if (VcsConfiguration.getInstance(project).SHOW_ONLY_CHANGED_IN_SELECTION_DIFF) { // preload while in bckgrnd - cachedRevisionsContents.loadContentsFor(revisionList.toArray(new VcsFileRevision[revisionList.size()])); + try { + cachedRevisionsContents.loadContentsFor(revisionList.toArray(new VcsFileRevision[revisionList.size()])); + } + catch (VcsException e) { + preloadException[0] = e; + } } } }); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java index 95a1f025e061..27e5bac31e18 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/VcsHistoryUtil.java @@ -64,7 +64,7 @@ public class VcsHistoryUtil { } } - private static int compareNumbers(VcsFileRevision first, VcsFileRevision second) { + public static int compareNumbers(VcsFileRevision first, VcsFileRevision second) { return first.getRevisionNumber().compareTo(second.getRevisionNumber()); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/CachedRevisionsContents.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/CachedRevisionsContents.java index a293bb7f1555..87dd5fedc259 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/CachedRevisionsContents.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/CachedRevisionsContents.java @@ -42,7 +42,7 @@ import java.util.*; */ public class CachedRevisionsContents { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.history.impl.CachedRevisionsContents"); - private final Map myCachedContents = new HashMap(); + private final Map myCachedContents; private final Project myProject; // managed outside, for reference here private List myRevisions; @@ -51,13 +51,14 @@ public class CachedRevisionsContents { public CachedRevisionsContents(final Project project, final VirtualFile file) { myProject = project; myFile = file; + myCachedContents = Collections.synchronizedMap(new HashMap()); } public void setRevisions(List revisions) { myRevisions = revisions; } - public void loadContentsFor(final VcsFileRevision[] revisions) { + public void loadContentsFor(final VcsFileRevision[] revisions) throws VcsException { final VcsFileRevision[] revisionsToLoad = revisionsNeededToBeLoaded(revisions); final List toBeLoaded = new LinkedList(); @@ -67,6 +68,7 @@ public class CachedRevisionsContents { } if (toBeLoaded.isEmpty()) return; + final VcsException[] exception = new VcsException[1]; final Runnable process = new Runnable() { public void run() { ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); @@ -91,19 +93,17 @@ public class CachedRevisionsContents { vcsFileRevision.loadContent(); } catch (final VcsException e) { - WaitForProgressToShow.runOrInvokeLaterAboveProgress(new Runnable() { - public void run() { - Messages.showErrorDialog(VcsBundle.message("message.text.cannot.load.version.because.of.error", - vcsFileRevision.getRevisionNumber(), e.getLocalizedMessage()), - VcsBundle.message("message.title.load.version")); - } - }, null, myProject); + exception[0] = new VcsException(e); + LOG.info(e); + return; } catch (ProcessCanceledException ex) { return; } catch (IOException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + exception[0] = new VcsException(e); + LOG.info(e); + return; } String content = null; try { @@ -113,13 +113,16 @@ public class CachedRevisionsContents { } } catch (IOException e) { + exception[0] = new VcsException(e); LOG.info(e); + return; } catch (VcsException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + exception[0] = new VcsException(e); + LOG.info(e); + return; } myCachedContents.put(vcsFileRevision.getRevisionNumber(), content); - } } } @@ -136,9 +139,12 @@ public class CachedRevisionsContents { } else { process.run(); } + if (exception[0] != null) { + throw exception[0]; + } } - public String getContentOf(VcsFileRevision revision) { + public String getContentOf(VcsFileRevision revision) throws VcsException { if (! myCachedContents.containsKey(revision.getRevisionNumber())) { loadContentsFor(new VcsFileRevision[]{revision}); } @@ -157,7 +163,7 @@ public class CachedRevisionsContents { private Collection collectRevisionsFromFirstTo(VcsFileRevision revision) { ArrayList result = new ArrayList(); for (VcsFileRevision vcsFileRevision : myRevisions) { - if (VcsHistoryUtil.compare(revision, vcsFileRevision) > 0) continue; + if (VcsHistoryUtil.compareNumbers(revision, vcsFileRevision) > 0) continue; result.add(vcsFileRevision); } return result; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsHistoryDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsHistoryDialog.java index fcd27d07828e..0d573bcf4a06 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsHistoryDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/impl/VcsHistoryDialog.java @@ -20,6 +20,7 @@ import com.intellij.diff.FindBlock; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diff.DiffManager; import com.intellij.openapi.diff.DiffPanel; @@ -29,12 +30,11 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Splitter; -import com.intellij.openapi.vcs.AbstractVcs; -import com.intellij.openapi.vcs.VcsBundle; -import com.intellij.openapi.vcs.VcsConfiguration; -import com.intellij.openapi.vcs.VcsDataKeys; +import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.history.*; +import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.table.TableView; @@ -61,6 +61,7 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { private final int mySelectionStart; private final int mySelectionEnd; + // todo equals??? private final Map myRevisionToContentMap = new com.intellij.util.containers.HashMap(); private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.history.impl.VcsHistoryDialog"); @@ -173,11 +174,11 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { }); - myList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + final ListSelectionListener selectionListener = new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { final VcsFileRevision revision; - if (myList.getSelectedRowCount() == 1) { - revision = (VcsFileRevision) myList.getItems().get(myList.getSelectedRow()); + if (myList.getSelectedRowCount() == 1 && !myList.isEmpty()) { + revision = (VcsFileRevision)myList.getItems().get(myList.getSelectedRow()); myComments.setText(revision.getCommitMessage()); myComments.setCaretPosition(0); } @@ -190,21 +191,34 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { } updateDiff(); } - }); + }; + myList.getSelectionModel().addListSelectionListener(selectionListener); myChangesOnlyCheckBox.setSelected(configuration.SHOW_ONLY_CHANGED_IN_SELECTION_DIFF); - updateRevisionsList(); + try { + updateRevisionsList(); + } + catch (final VcsException e) { + // todo test it, always exception + canNotLoadRevisionMessage(e); + } myChangesOnlyCheckBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { configuration.SHOW_ONLY_CHANGED_IN_SELECTION_DIFF = myChangesOnlyCheckBox.isSelected(); - updateRevisionsList(); + try { + updateRevisionsList(); + } + catch (VcsException e1) { + canNotLoadRevisionMessage(e1); + } } }); init(); - ApplicationManager.getApplication().invokeLater(new Runnable() { + SwingUtilities.invokeLater(new Runnable() { public void run() { + if (! VcsHistoryDialog.this.isShowing()) return; myList.getSelectionModel().addSelectionInterval(0, 0); } }); @@ -212,6 +226,21 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { setTitle(VcsBundle.message("dialog.title.history.for.file", file.getName())); } + private void canNotLoadRevisionMessage(final VcsException e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (! VcsHistoryDialog.this.isShowing()) return; + VcsBalloonProblemNotifier.showBalloonForComponent(VcsHistoryDialog.this.getRootPane(), + canNoLoadMessage(e), MessageType.ERROR, true); + } + }); + } + + private String canNoLoadMessage(VcsException e) { + return "Can not load revision contents: " + e.getMessage(); + } + @Override public JComponent getPreferredFocusedComponent() { return myList; @@ -235,15 +264,15 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { return result; } - protected String getContentOf(VcsFileRevision revision) { + protected String getContentOf(VcsFileRevision revision) throws VcsException { return myCachedContents.getContentOf(revision); } - private void loadContentsFor(final VcsFileRevision[] revisions) { + private void loadContentsFor(final VcsFileRevision[] revisions) throws VcsException { myCachedContents.loadContentsFor(revisions); } - private void updateRevisionsList() { + private void updateRevisionsList() throws VcsException { if (myIsInLoading) return; if (myChangesOnlyCheckBox.isSelected()) { loadContentsFor(myRevisions.toArray(new VcsFileRevision[myRevisions.size()])); @@ -267,7 +296,7 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { } - private List filteredRevisions() throws FilesTooBigForDiffException { + private List filteredRevisions() throws FilesTooBigForDiffException, VcsException { ArrayList result = new ArrayList(); VcsFileRevision nextRevision = myRevisions.get(myRevisions.size() - 1); result.add(nextRevision); @@ -282,6 +311,7 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { } private synchronized void updateDiff() { + if (myList.isEmpty()) return; int[] selectedIndices = myList.getSelectedRows(); if (selectedIndices.length == 0) { updateDiff(CURRENT, CURRENT); @@ -314,6 +344,12 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { catch (FilesTooBigForDiffException e) { myDiffPanel.setTooBigFileErrorContents(); } + catch (VcsException e) { + final String text = canNoLoadMessage(e); + myDiffPanel.setContents(new SimpleContent(text, myContentFileType), + new SimpleContent(text, myContentFileType)); + canNotLoadRevisionMessage(e); + } myDiffPanel.setTitle1(VcsBundle.message("diff.content.title.revision.number", firstRev.getRevisionNumber())); myDiffPanel.setTitle2(VcsBundle.message("diff.content.title.revision.number", secondRev.getRevisionNumber())); @@ -414,14 +450,14 @@ public class VcsHistoryDialog extends DialogWrapper implements DataProvider { return null; } - protected String getContentToShow(VcsFileRevision revision) throws FilesTooBigForDiffException { + protected String getContentToShow(VcsFileRevision revision) throws FilesTooBigForDiffException, VcsException { final Block block = getBlock(revision); if (block == null) return ""; return block.getBlockContent(); } @Nullable - private Block getBlock(VcsFileRevision revision) throws FilesTooBigForDiffException { + private Block getBlock(VcsFileRevision revision) throws FilesTooBigForDiffException, VcsException { if (myRevisionToContentMap.containsKey(revision)) return myRevisionToContentMap.get(revision); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/VcsBalloonProblemNotifier.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/VcsBalloonProblemNotifier.java index 23780caadc75..8adad110695a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/VcsBalloonProblemNotifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ui/VcsBalloonProblemNotifier.java @@ -20,9 +20,17 @@ import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.BalloonBuilder; +import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; +import com.intellij.ui.awt.RelativePoint; import org.jetbrains.annotations.NotNull; +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.TimeUnit; + /** * Shows a notification balloon over one of version control related tool windows: Changes View or Version Control View. * By default the notification is shown over the Changes View. @@ -74,4 +82,24 @@ public class VcsBalloonProblemNotifier implements Runnable { public void run() { NOTIFICATION_GROUP.createNotification(myMessage, myMessageType).notify(myProject.isDefault() ? null : myProject); } + + public static void showBalloonForComponent(@NotNull JComponent component, @NotNull final String message, final MessageType type, + final boolean atTop) { + BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder(message, type, null); + Balloon balloon = balloonBuilder.createBalloon(); + Dimension size = component.getSize(); + Balloon.Position position; + int x; + int y; + if (size == null) { + x = y = 0; + position = Balloon.Position.above; + } + else { + x = Math.min(10, size.width / 2); + y = size.height; + position = Balloon.Position.below; + } + balloon.show(new RelativePoint(component, new Point(x, y)), position); + } }