diff --git a/platform/platform-api/src/com/intellij/openapi/diagnostic/IdeaLoggingEvent.java b/platform/platform-api/src/com/intellij/openapi/diagnostic/IdeaLoggingEvent.java index 34df1e3dcecd..f8f07c96e938 100644 --- a/platform/platform-api/src/com/intellij/openapi/diagnostic/IdeaLoggingEvent.java +++ b/platform/platform-api/src/com/intellij/openapi/diagnostic/IdeaLoggingEvent.java @@ -1,22 +1,7 @@ -/* - * Copyright 2000-2009 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. - */ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.diagnostic; import com.intellij.openapi.util.text.StringUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; /** @@ -25,10 +10,16 @@ import org.jetbrains.annotations.Nullable; public class IdeaLoggingEvent { private final String myMessage; private final Throwable myThrowable; + private final Object myData; public IdeaLoggingEvent(String message, Throwable throwable) { + this(message, throwable, null); + } + + public IdeaLoggingEvent(String message, Throwable throwable, Object data) { myMessage = message; myThrowable = throwable; + myData = data; } public String getMessage() { @@ -40,18 +31,16 @@ public class IdeaLoggingEvent { } public String getThrowableText() { - if (myThrowable == null) return ""; - - return StringUtil.getThrowableText(myThrowable); + return myThrowable != null ? StringUtil.getThrowableText(myThrowable) : ""; } @Nullable public Object getData() { - return null; + return myData; } - @NonNls + @Override public String toString() { return "IdeaLoggingEvent[message=" + myMessage + ", throwable=" + getThrowableText() + "]"; } -} +} \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/CheckBoxList.java b/platform/platform-api/src/com/intellij/ui/CheckBoxList.java index 5f5ecb38948b..ce4e643a0896 100644 --- a/platform/platform-api/src/com/intellij/ui/CheckBoxList.java +++ b/platform/platform-api/src/com/intellij/ui/CheckBoxList.java @@ -1,16 +1,4 @@ -// Copyright 2000-2017 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. +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.ui; import com.intellij.ui.components.JBList; @@ -76,8 +64,10 @@ public class CheckBoxList extends JBList { for (int index : getSelectedIndices()) { if (index >= 0) { JCheckBox checkbox = getCheckBoxAt(index); - value = value != null ? value : !checkbox.isSelected(); - setSelected(checkbox, index, value); + if (checkbox.isEnabled()) { + value = value != null ? value : !checkbox.isSelected(); + setSelected(checkbox, index, value); + } } } } @@ -98,7 +88,9 @@ public class CheckBoxList extends JBList { if (p != null) { Dimension dim = getCheckBoxDimension(checkBox); if (p.x >= 0 && p.x < dim.width && p.y >= 0 && p.y < dim.height) { - setSelected(checkBox, index, !checkBox.isSelected()); + if (checkBox.isEnabled()) { + setSelected(checkBox, index, !checkBox.isSelected()); + } return true; } } @@ -353,7 +345,7 @@ public class CheckBoxList extends JBList { Font font = getFont(); checkbox.setBackground(backgroundColor); checkbox.setForeground(textColor); - checkbox.setEnabled(isEnabled()); + checkbox.setEnabled(isEnabled() && isEnabled(index)); checkbox.setFont(font); checkbox.setFocusPainted(false); checkbox.setBorderPainted(false); @@ -367,7 +359,7 @@ public class CheckBoxList extends JBList { panel.add(checkbox, BorderLayout.LINE_START); JLabel infoLabel = new JLabel(auxText, SwingConstants.RIGHT); - infoLabel.setBorder(new EmptyBorder(0, 0, 0, checkbox.getInsets().left)); + infoLabel.setBorder(JBUI.Borders.emptyRight(checkbox.getInsets().left)); infoLabel.setFont(UIUtil.getFont(UIUtil.FontSize.SMALL, font)); panel.add(infoLabel, BorderLayout.CENTER); @@ -401,6 +393,10 @@ public class CheckBoxList extends JBList { return null; } + protected boolean isEnabled(int index) { + return true; + } + protected Color getBackground(final boolean isSelected) { return isSelected ? getSelectionBackground() : getBackground(); } @@ -408,4 +404,4 @@ public class CheckBoxList extends JBList { protected Color getForeground(final boolean isSelected) { return isSelected ? getSelectionForeground() : getForeground(); } -} +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java index 55ffff1e1faf..a1a3f744db8b 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java @@ -5,22 +5,18 @@ import com.intellij.CommonBundle; import com.intellij.ExtensionPoints; import com.intellij.credentialStore.CredentialAttributesKt; import com.intellij.credentialStore.Credentials; -import com.intellij.diagnostic.errordialog.*; import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; -import com.intellij.ide.impl.DataManagerImpl; +import com.intellij.ide.IdeBundle; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.plugins.PluginManagerMain; import com.intellij.ide.plugins.cl.PluginClassLoader; -import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; -import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.application.ex.ApplicationInfoEx; -import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.diagnostic.*; import com.intellij.openapi.extensions.ExtensionException; import com.intellij.openapi.extensions.Extensions; @@ -30,276 +26,130 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.SystemInfo; -import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; -import com.intellij.ui.HyperlinkLabel; -import com.intellij.ui.TabbedPaneWrapper; +import com.intellij.ui.*; +import com.intellij.ui.components.JBLabel; +import com.intellij.ui.components.JBScrollPane; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.xml.util.XmlStringUtil; -import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.DocumentEvent; import javax.swing.event.HyperlinkEvent; import java.awt.*; import java.awt.event.ActionEvent; +import java.awt.event.ItemEvent; import java.io.IOException; -import java.math.BigInteger; import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.List; +import java.util.zip.CRC32; + +import static java.awt.GridBagConstraints.*; public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListener, DataProvider { private static final Logger LOG = Logger.getInstance(IdeErrorsDialog.class); public static final DataKey CURRENT_TRACE_KEY = DataKey.create("current_stack_trace_key"); - public static final int COMPONENTS_WIDTH = 670; - private static final String ACTIVE_TAB_OPTION = IdeErrorsDialog.class.getName() + "activeTab"; private static List ourDevelopersList = Collections.emptyList(); - private JPanel myContentPane; - private JPanel myBackButtonPanel; - private HyperlinkLabel.Croppable myInfoLabel; - private JPanel myNextButtonPanel; - private JPanel myTabsPanel; - private JLabel myCountLabel; - private HyperlinkLabel.Croppable myForeignPluginWarningLabel; - private HyperlinkLabel.Croppable myDisableLink; - private JPanel myCredentialsPanel; - private HyperlinkLabel myCredentialsLabel; - private JPanel myForeignPluginWarningPanel; - private JPanel myAttachmentWarningPanel; - private HyperlinkLabel myAttachmentWarningLabel; - private JPanel myAttachments; - - private int myIndex; - private final List> myMergedMessages = new ArrayList<>(); - private List myRawMessages; private final MessagePool myMessagePool; - private final Set myMessagesWithIncludedAttachments = new THashSet<>(1); + private final Project myProject; private final boolean myInternalMode; - private final @Nullable MessageDigest myDigest; - private boolean myMute; + private final List myRawMessages = new ArrayList<>(); + private final List> myMergedMessages = new ArrayList<>(); + private int myIndex; - private @Nullable TabbedPaneWrapper myTabs; - private @Nullable CommentsTabForm myCommentsTabForm; - private DetailsTabForm myDetailsTabForm; - private AttachmentsTabForm myAttachmentsTabForm; + private JLabel myCountLabel; + private HyperlinkLabel.Croppable myInfoLabel; + private HyperlinkLabel.Croppable myDisableLink; + private HyperlinkLabel.Croppable myForeignPluginWarningLabel; + private JTextArea myCommentArea; + private AttachmentsList myAttachmentsList; + private JTextArea myAttachmentArea; + private JPanel myAssigneePanel; + private ComboBox myAssigneeCombo; + private HyperlinkLabel myCredentialsLabel; - private final ClearErrorsAction myClearAction = new ClearErrorsAction(); - private final BlameAction myBlameAction = new BlameAction(); - private @Nullable AnalyzeAction myAnalyzeAction; - - public IdeErrorsDialog(MessagePool messagePool, @Nullable LogMessage defaultMessage) { - super(JOptionPane.getRootFrame(), false); + public IdeErrorsDialog(@NotNull MessagePool messagePool, @Nullable Project project, @Nullable LogMessage defaultMessage) { + super(project, true); myMessagePool = messagePool; - ApplicationEx app = ApplicationManagerEx.getApplicationEx(); - myInternalMode = app != null && app.isInternal(); - MessageDigest md5 = null; - try { md5 = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException ignored) { } - myDigest = md5; + myProject = project; + myInternalMode = ApplicationManager.getApplication().isInternal(); setTitle(DiagnosticBundle.message("error.list.title")); - init(); - rebuildHeaders(); - if (defaultMessage == null || !moveSelectionToMessage(defaultMessage)) { - moveSelectionToEarliestMessage(); - } - setCancelButtonText(CommonBundle.message("close.action.name")); setModal(false); + init(); + setCancelButtonText(CommonBundle.message("close.action.name")); if (myInternalMode) { - if (ourDevelopersList.isEmpty()) { - loadDevelopersAsynchronously(); - } - else { - myDetailsTabForm.setDevelopers(ourDevelopersList); - } + loadDevelopersList(); + } + + updateMessages(); + selectMessage(defaultMessage); + updateControls(); + + messagePool.addListener(this); + } + + private void loadDevelopersList() { + if (!ourDevelopersList.isEmpty()) { + myAssigneeCombo.setModel(new CollectionComboBoxModel<>(ourDevelopersList)); + } + else { + new Task.Backgroundable(null, "Loading Developers List", true) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + List developers = ITNProxy.fetchDevelopers(indicator); + //noinspection AssignmentToStaticFieldFromInstanceMethod + ourDevelopersList = developers; + UIUtil.invokeLaterIfNeeded(() -> { + if (isShowing()) { + myAssigneeCombo.setModel(new CollectionComboBoxModel<>(developers)); + } + }); + } + catch (IOException e) { + LOG.warn(e); + } + } + }.queue(); } } - private void loadDevelopersAsynchronously() { - new Task.Backgroundable(null, "Loading Developers List", true) { - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - List developers = ITNProxy.fetchDevelopers(indicator); - myDetailsTabForm.setDevelopers(developers); - //noinspection AssignmentToStaticFieldFromInstanceMethod - ourDevelopersList = developers; - } - catch (IOException e) { - LOG.warn(e); - } - } - }.queue(); - } - - private boolean moveSelectionToMessage(LogMessage defaultMessage) { - int index = -1; + private void selectMessage(@Nullable LogMessage defaultMessage) { for (int i = 0; i < myMergedMessages.size(); i++) { AbstractMessage message = myMergedMessages.get(i).get(0); - if (message == defaultMessage) { - index = i; - break; + if (defaultMessage != null && message == defaultMessage || defaultMessage == null && !message.isRead()) { + myIndex = i; + return; } } - - if (index >= 0) { - myIndex = index; - updateControls(); - return true; - } - else { - return false; - } } + @Nullable @Override - public void newEntryAdded() { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(() -> { - rebuildHeaders(); - updateControls(); - }); - } - - @Override - public void poolCleared() { - //noinspection SSBasedInspection - SwingUtilities.invokeLater(() -> doOKAction()); - } - - @Override - public void entryWasRead() { } - - @NotNull - @Override - protected Action[] createActions() { - myBlameAction.putValue(DialogWrapper.DEFAULT_ACTION, true); - return SystemInfo.isMac ? new Action[]{getCancelAction(), myClearAction, myBlameAction} - : new Action[]{myClearAction, myBlameAction, getCancelAction()}; - } - - private class ForwardAction extends AnAction implements DumbAware { - public ForwardAction() { - super("Next", null, AllIcons.Actions.Forward); - AnAction forward = ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_TAB); - if (forward != null) { - registerCustomShortcutSet(forward.getShortcutSet(), getRootPane(), getDisposable()); - } - } - - @Override - public void actionPerformed(AnActionEvent e) { - myIndex++; - updateControls(); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(myIndex < myMergedMessages.size() - 1); - } - } - - private class BackAction extends AnAction implements DumbAware { - public BackAction() { - super("Previous", null, AllIcons.Actions.Back); - AnAction back = ActionManager.getInstance().getAction(IdeActions.ACTION_PREVIOUS_TAB); - if (back != null) { - registerCustomShortcutSet(back.getShortcutSet(), getRootPane(), getDisposable()); - } - } - - @Override - public void actionPerformed(AnActionEvent e) { - myIndex--; - updateControls(); - } - - @Override - public void update(AnActionEvent e) { - e.getPresentation().setEnabled(myIndex > 0); - } - } - - @Override - protected JComponent createCenterPanel() { - DefaultActionGroup goBack = new DefaultActionGroup(); - BackAction back = new BackAction(); - goBack.add(back); - ActionToolbar backToolbar = ActionManager.getInstance().createActionToolbar("IdeErrorsBack", goBack, true); - backToolbar.getComponent().setBorder(JBUI.Borders.empty()); - backToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); - myBackButtonPanel.add(backToolbar.getComponent(), BorderLayout.CENTER); - - DefaultActionGroup goForward = new DefaultActionGroup(); - ForwardAction forward = new ForwardAction(); - goForward.add(forward); - ActionToolbar forwardToolbar = ActionManager.getInstance().createActionToolbar("IdeErrorsForward", goForward, true); - forwardToolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); - forwardToolbar.getComponent().setBorder(JBUI.Borders.empty()); - myNextButtonPanel.add(forwardToolbar.getComponent(), BorderLayout.CENTER); - - LabeledTextComponent.TextListener commentsListener = newText -> { - if (!myMute) { - AbstractMessage message = getSelectedMessage(); - if (message != null) { - message.setAdditionalInfo(newText); - } - } - }; - - if (!myInternalMode) { - myCommentsTabForm = new CommentsTabForm(); - myCommentsTabForm.addCommentsListener(commentsListener); - myDetailsTabForm = new DetailsTabForm(null); - myDetailsTabForm.setCommentsAreaVisible(false); - - myTabs = new TabbedPaneWrapper(getDisposable()); - myTabs.addTab(DiagnosticBundle.message("error.comments.tab.title"), myCommentsTabForm.getContentPane()); - myTabs.addTab(DiagnosticBundle.message("error.details.tab.title"), myDetailsTabForm.getContentPane()); - myTabsPanel.add(myTabs.getComponent(), BorderLayout.CENTER); - - int activeTabIndex = Integer.parseInt(PropertiesComponent.getInstance().getValue(ACTIVE_TAB_OPTION, "0")); - if (activeTabIndex < 0 || activeTabIndex >= myTabs.getTabCount()) activeTabIndex = 0; - myTabs.setSelectedIndex(activeTabIndex); - - myTabs.addChangeListener(e -> { - JComponent c = getPreferredFocusedComponent(); - if (c != null) { - IdeFocusManager.findInstanceByComponent(myContentPane).requestFocus(c, true); - } - }); - } - else { - AnAction analyzePlatformAction = ActionManager.getInstance().getAction("AnalyzeStacktraceOnError"); - if (analyzePlatformAction != null) { - myAnalyzeAction = new AnalyzeAction(analyzePlatformAction); - } - myDetailsTabForm = new DetailsTabForm(myAnalyzeAction); - myDetailsTabForm.setCommentsAreaVisible(true); - myDetailsTabForm.addCommentsListener(commentsListener); - myTabsPanel.add(myDetailsTabForm.getContentPane(), BorderLayout.CENTER); - } - - myAttachmentsTabForm = new AttachmentsTabForm(); - myAttachmentsTabForm.addInclusionListener(e -> updateAttachmentWarning(getSelectedMessage())); - myAttachments.add(myAttachmentsTabForm.getContentPane(), BorderLayout.CENTER); + protected JComponent createNorthPanel() { + myCountLabel = new JBLabel(); + myInfoLabel = new HyperlinkLabel.Croppable(); + myDisableLink = new HyperlinkLabel.Croppable(); myDisableLink.setHyperlinkText(UIUtil.removeMnemonic(DiagnosticBundle.message("error.list.disable.plugin"))); myDisableLink.addHyperlinkListener(e -> { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { @@ -307,340 +157,556 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene } }); + myForeignPluginWarningLabel = new HyperlinkLabel.Croppable(); + + JPanel controls = new JPanel(new BorderLayout()); + controls.add(actionToolbar("IdeErrorsBack", new BackAction()), BorderLayout.WEST); + controls.add(myCountLabel, BorderLayout.CENTER); + controls.add(actionToolbar("IdeErrorsForward", new ForwardAction()), BorderLayout.EAST); + + JPanel panel = new JPanel(new GridBagLayout()); + panel.add(controls, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, CENTER, NONE, JBUI.insets(2), 0, 0)); + panel.add(myInfoLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, WEST, NONE, JBUI.emptyInsets(), 0, 0)); + panel.add(myDisableLink, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, WEST, NONE, JBUI.emptyInsets(), 0, 0)); + panel.add(new JPanel(), new GridBagConstraints(3, 0, 1, 1, 1.0, 0.0, CENTER, BOTH, JBUI.emptyInsets(), 0, 0)); // expander + panel.add(myForeignPluginWarningLabel, new GridBagConstraints(1, 1, 3, 1, 0.0, 0.0, WEST, NONE, JBUI.emptyInsets(), 0, 0)); + return panel; + } + + private static JComponent actionToolbar(String id, AnAction action) { + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(id, new DefaultActionGroup(action), true); + toolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); + toolbar.getComponent().setBorder(JBUI.Borders.empty()); + return toolbar.getComponent(); + } + + @Override + protected JComponent createCenterPanel() { + JBLabel commentLabel = new JBLabel(DiagnosticBundle.message("diagnostic.error.report.additional.info.label")); + + myCommentArea = new JTextArea(5, 0); + myCommentArea.setMargin(JBUI.insets(2)); + myCommentArea.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + selectedMessage().setAdditionalInfo(myCommentArea.getText().trim()); + } + }); + + JBLabel attachmentsLabel = new JBLabel(DiagnosticBundle.message("diagnostic.error.report.attachments.label")); + + myAttachmentsList = new AttachmentsList(myInternalMode); + myAttachmentsList.addListSelectionListener(e -> { + int index = myAttachmentsList.getSelectedIndex(); + if (index < 0) { + myAttachmentArea.setText(""); + } + else if (index == 0) { + myAttachmentArea.setText(getDetailsText(selectedMessage())); + } + else { + myAttachmentArea.setText(selectedMessage().getAllAttachments().get(index - 1).getDisplayText()); + } + myAttachmentArea.moveCaretPosition(0); + }); + myAttachmentsList.setCheckBoxListListener((index, value) -> { + if (index > 0) { + selectedMessage().getAllAttachments().get(index - 1).setIncluded(value); + } + }); + + myAttachmentArea = new JTextArea(); + myAttachmentArea.setMargin(JBUI.insets(2)); + myAttachmentArea.setEditable(false); + + if (myInternalMode) { + myAssigneeCombo = new ComboBox<>(); + myAssigneeCombo.setRenderer(new ListCellRendererWrapper() { + @Override + public void customize(JList list, Developer value, int index, boolean selected, boolean hasFocus) { + setText(value == null ? "" : value.getDisplayText()); + } + }); + myAssigneeCombo.setPrototypeDisplayValue(new Developer(0, StringUtil.repeatSymbol('-', 30))); + myAssigneeCombo.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + Developer developer = (Developer)e.getItem(); + selectedMessage().setAssigneeId(developer == null ? null : developer.getId()); + } + }); + new ComboboxSpeedSearch(myAssigneeCombo) { + @Override + protected String getElementText(Object element) { + return element == null ? "" : ((Developer)element).getDisplayText(); + } + }; + + myAssigneePanel = new JPanel(); + myAssigneePanel.add(new JBLabel("Assignee:")); + myAssigneePanel.add(myAssigneeCombo); + } + + myCredentialsLabel = new HyperlinkLabel(); myCredentialsLabel.addHyperlinkListener(e -> { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { JetBrainsAccountDialogKt.showJetBrainsAccountDialog(getRootPane()).show(); - updateCredentialsPane(getSelectedMessage()); + updateCredentialsPanel(getSubmitter(selectedMessage().getThrowable())); } }); - myAttachmentWarningLabel.setIcon(UIUtil.getBalloonWarningIcon()); - myAttachmentWarningLabel.addHyperlinkListener(e -> { - if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - myAttachmentsTabForm.selectFirstIncludedAttachment(); - } - }); + JPanel commentPanel = new JPanel(new BorderLayout()); + commentPanel.setBorder(JBUI.Borders.emptyTop(5)); + commentPanel.add(commentLabel, BorderLayout.NORTH); + commentPanel.add(scrollPane(myCommentArea, 500, 150), BorderLayout.CENTER); - myDetailsTabForm.addAssigneeListener(e -> { - if (!myMute) { - AbstractMessage message = getSelectedMessage(); - if (message != null) { - message.setAssigneeId(myDetailsTabForm.getAssigneeId()); - } - } - }); + JPanel attachmentsPanel = new JPanel(new BorderLayout(JBUI.scale(5), 0)); + attachmentsPanel.setBorder(JBUI.Borders.emptyTop(5)); + attachmentsPanel.add(attachmentsLabel, BorderLayout.NORTH); + attachmentsPanel.add(scrollPane(myAttachmentsList, 100, 400), BorderLayout.WEST); + attachmentsPanel.add(scrollPane(myAttachmentArea, 400, 400), BorderLayout.CENTER); - return myContentPane; + JPanel bottomRow = new JPanel(new BorderLayout()); + if (myInternalMode) { + bottomRow.add(myAssigneePanel, BorderLayout.WEST); + } + bottomRow.add(myCredentialsLabel, BorderLayout.EAST); + + JPanel rootPanel = new JPanel(new BorderLayout()); + rootPanel.add(commentPanel, BorderLayout.NORTH); + rootPanel.add(attachmentsPanel, BorderLayout.CENTER); + rootPanel.add(bottomRow, BorderLayout.SOUTH); + return rootPanel; } - private void moveSelectionToEarliestMessage() { - myIndex = 0; - for (int i = 0; i < myMergedMessages.size(); i++) { - AbstractMessage message = myMergedMessages.get(i).get(0); - if (message != null && !message.isRead()) { - myIndex = i; - break; + private static JScrollPane scrollPane(JComponent component, int width, int height) { + JScrollPane scrollPane = new JBScrollPane(component); + scrollPane.setMinimumSize(JBUI.size(width, height)); + return scrollPane; + } + + @NotNull + @Override + protected Action[] createActions() { + List actions = new ArrayList<>(); + if (myInternalMode && myProject != null && !myProject.isDefault()) { + AnAction action = ActionManager.getInstance().getAction("AnalyzeStacktraceOnError"); + if (action != null) { + actions.add(new AnalyzeAction(action)); } } - - updateControls(); - } - - private void disablePlugin() { - AbstractMessage message = getSelectedMessage(); - if (message != null) { - PluginId pluginId = findPluginId(message.getThrowable()); - if (pluginId != null) { - DisablePluginWarningDialog.disablePlugin(pluginId, getRootPane()); - } - } - } - - private void updateControls() { - updateCountLabel(); - AbstractMessage message = getSelectedMessage(); - updateInfoLabel(message); - updateCredentialsPane(message); - updateAssigneePane(message); - updateAttachmentWarning(message); - myDisableLink.setVisible(canDisablePlugin(message)); - updateForeignPluginLabel(message); - updateTabs(); - - myClearAction.update(); - myBlameAction.update(); - if (myAnalyzeAction != null) { - myAnalyzeAction.update(); - } - } - - private void updateAttachmentWarning(AbstractMessage message) { - if (message != null) { - if (!myMessagesWithIncludedAttachments.contains(message) && - (myInternalMode || Registry.is("ide.diagnostics.suggest.sending.all.attachments"))) { - for (Attachment attachment : message.getAllAttachments()) attachment.setIncluded(true); - myMessagesWithIncludedAttachments.add(message); - } - - List includedAttachments = message.getIncludedAttachments(); - if (!includedAttachments.isEmpty()) { - myAttachmentWarningPanel.setVisible(true); - if (includedAttachments.size() == 1) { - myAttachmentWarningLabel.setHtmlText( - DiagnosticBundle.message("diagnostic.error.report.include.attachment.warning", includedAttachments.get(0).getName())); - } - else { - myAttachmentWarningLabel.setHtmlText( - DiagnosticBundle.message("diagnostic.error.report.include.attachments.warning", includedAttachments.size())); - } - } - else { - myAttachmentWarningPanel.setVisible(false); - } - } - } - - private static boolean canDisablePlugin(AbstractMessage message) { - PluginId pluginId = message != null ? findPluginId(message.getThrowable()) : null; - return pluginId != null && !ApplicationInfoEx.getInstanceEx().isEssentialPlugin(pluginId.getIdString()); - } - - private void updateCountLabel() { - if (myMergedMessages.isEmpty()) { - myCountLabel.setText(DiagnosticBundle.message("error.list.empty")); - } - else { - myCountLabel.setText(DiagnosticBundle.message("error.list.message.index.count", Integer.toString(myIndex + 1), myMergedMessages.size())); - } - } - - private void updateCredentialsPane(AbstractMessage message) { - if (message != null) { - ErrorReportSubmitter submitter = getSubmitter(message.getThrowable()); - if (submitter instanceof ITNReporter) { - myCredentialsPanel.setVisible(true); - Credentials credentials = ErrorReportConfigurable.getCredentials(); - if (CredentialAttributesKt.isFulfilled(credentials)) { - assert credentials != null; - myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", credentials.getUserName())); - } - else { - myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously")); - } - return; - } - } - myCredentialsPanel.setVisible(false); - } - - private void updateAssigneePane(AbstractMessage message) { - ErrorReportSubmitter submitter = message != null ? getSubmitter(message.getThrowable()) : null; - myDetailsTabForm.setAssigneeVisible(submitter instanceof ITNReporter && myInternalMode); - } - - private void updateInfoLabel(AbstractMessage message) { - if (message == null) { - myInfoLabel.setText(""); - return; - } - Throwable throwable = message.getThrowable(); - if (throwable instanceof MessagePool.TooManyErrorsException) { - myInfoLabel.setText(""); - return; - } - - StringBuilder text = new StringBuilder(); - PluginId pluginId = findPluginId(throwable); - if (pluginId == null) { - if (throwable instanceof AbstractMethodError) { - text.append(DiagnosticBundle.message("error.list.message.blame.unknown.plugin")); - } - else { - text.append(DiagnosticBundle.message("error.list.message.blame.core", ApplicationNamesInfo.getInstance().getProductName())); - } - } - else { - IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); - text.append(DiagnosticBundle.message("error.list.message.blame.plugin", plugin != null ? plugin.getName() : pluginId)); - } - text.append(" ").append(DiagnosticBundle.message("error.list.message.info", - DateFormatUtil.formatPrettyDateTime(message.getDate()), - myMergedMessages.get(myIndex).size())); - - String url = null; - if (message.isSubmitted()) { - SubmittedReportInfo info = message.getSubmissionInfo(); - url = info.getURL(); - appendSubmissionInformation(info, text); - text.append(". "); - } - else if (message.isSubmitting()) { - text.append(" Submitting..."); - } - else if (!message.isRead()) { - text.append(" ").append(DiagnosticBundle.message("error.list.message.unread")); - } - myInfoLabel.setHtmlText(XmlStringUtil.wrapInHtml(text)); - myInfoLabel.setHyperlinkTarget(url); - } - - public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out) { - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.submission.failed")); - } - else if (info.getURL() != null && info.getLinkText() != null) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", info.getURL(), info.getLinkText())); - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate")); - } - } - else { - out.append(DiagnosticBundle.message("error.list.message.submitted")); - } - } - - private void updateForeignPluginLabel(AbstractMessage message) { - if (message != null) { - Throwable throwable = message.getThrowable(); - ErrorReportSubmitter submitter = getSubmitter(throwable); - if (submitter == null) { - PluginId pluginId = findPluginId(throwable); - IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); - if (plugin == null || PluginManagerMain.isDevelopedByJetBrains(plugin)) { - myForeignPluginWarningPanel.setVisible(false); - return; - } - - myForeignPluginWarningPanel.setVisible(true); - String vendor = plugin.getVendor(); - String contactInfo = plugin.getVendorUrl(); - if (StringUtil.isEmpty(contactInfo)) { - contactInfo = plugin.getVendorEmail(); - } - if (StringUtil.isEmpty(vendor)) { - if (StringUtil.isEmpty(contactInfo)) { - myForeignPluginWarningLabel.setText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text")); - } - else { - String prefix = DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") + " "; - myForeignPluginWarningLabel.setHyperlinkText(prefix, contactInfo, "."); - myForeignPluginWarningLabel.setHyperlinkTarget(contactInfo); - } - } - else { - if (StringUtil.isEmpty(contactInfo)) { - myForeignPluginWarningLabel.setText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") + " " + vendor + "."); - } - else { - String prefix = DiagnosticBundle.message("error.dialog.foreign.plugin.warning.text.vendor") + " " + vendor + " ("; - myForeignPluginWarningLabel.setHyperlinkText(prefix, contactInfo, ")."); - String target = (StringUtil.equals(contactInfo, plugin.getVendorEmail()) ? "mailto:" : "") + contactInfo; - myForeignPluginWarningLabel.setHyperlinkTarget(target); - } - } - myForeignPluginWarningPanel.setVisible(true); - return; - } - } - myForeignPluginWarningPanel.setVisible(false); - } - - private void updateTabs() { - myMute = true; - try { - AbstractMessage message = getSelectedMessage(); - if (myCommentsTabForm != null) { - if (message != null) { - String msg = message.getMessage(); - int i = msg.indexOf('\n'); - if (i != -1) { - // take first line - msg = msg.substring(0, i); - } - myCommentsTabForm.setErrorText(msg); - } - else { - myCommentsTabForm.setErrorText(null); - } - if (message != null) { - myCommentsTabForm.setCommentText(message.getAdditionalInfo()); - myCommentsTabForm.setCommentsTextEnabled(true); - } - else { - myCommentsTabForm.setCommentText(null); - myCommentsTabForm.setCommentsTextEnabled(false); - } - } - - myDetailsTabForm.setDetailsText(message != null ? getDetailsText(message) : null); - if (message != null) { - myDetailsTabForm.setCommentsText(message.getAdditionalInfo()); - myDetailsTabForm.setCommentsTextEnabled(true); - } - else { - myDetailsTabForm.setCommentsText(null); - myDetailsTabForm.setCommentsTextEnabled(false); - } - - myDetailsTabForm.setAssigneeId(message == null ? null : message.getAssigneeId()); - - List attachments = message != null ? message.getAllAttachments() : Collections.emptyList(); - myAttachmentsTabForm.getContentPane().setVisible(!attachments.isEmpty()); - myAttachmentsTabForm.setAttachments(attachments); - } - finally { - myMute = false; - } - } - - private static String getDetailsText(AbstractMessage message) { - Throwable t = message.getThrowable(); - return t instanceof MessagePool.TooManyErrorsException ? t.getMessage() : message.getMessage() + "\n" + message.getThrowableText(); - } - - private void rebuildHeaders() { - myMergedMessages.clear(); - myRawMessages = myMessagePool.getFatalErrors(true, true); - - Map> messageGroups = new LinkedHashMap<>(); - for (AbstractMessage message : myRawMessages) { - Number key = digest(StringUtil.getThrowableText(message.getThrowable())); - List group = messageGroups.computeIfAbsent(key, k -> new ArrayList<>()); - group.add(0, message); - } - myMergedMessages.addAll(messageGroups.values()); - } - - private Number digest(String throwableText) { - if (myDigest != null) { - myDigest.reset(); - myDigest.update(throwableText.getBytes(StandardCharsets.UTF_8)); - return new BigInteger(myDigest.digest()); - } - else { - return throwableText.hashCode(); - } + actions.add(new ClearErrorsAction()); + actions.add(getOKAction()); + actions.add(getCancelAction()); + return actions.toArray(new Action[0]); } @Override public JComponent getPreferredFocusedComponent() { - JComponent result; - if (myTabs == null || myTabs.getSelectedIndex() > 0 || myCommentsTabForm == null) { - result = myDetailsTabForm.getPreferredFocusedComponent(); + return myCommentArea; + } + + @Override + protected String getDimensionServiceKey() { + return "IDE.errors.dialog"; + } + + @Override + public void doOKAction() { + if (getOKAction().isEnabled()) { + boolean closeDialog = myMergedMessages.size() == 1; + boolean reportingStarted = reportMessage(selectedMessage(), closeDialog); + if (!closeDialog) { + updateControls(); + } + else if (reportingStarted) { + super.doOKAction(); + } + } + } + + @Override + protected void dispose() { + myMessagePool.removeListener(this); + super.dispose(); + } + + private AbstractMessage selectedMessage() { + return myMergedMessages.get(myIndex).get(0); + } + + private void updateMessages() { + myRawMessages.clear(); + myRawMessages.addAll(myMessagePool.getFatalErrors(true, true)); + + Map> messageGroups = new LinkedHashMap<>(); + for (AbstractMessage message : myRawMessages) { + CRC32 digest = new CRC32(); + digest.update(StringUtil.getThrowableText(message.getThrowable()).getBytes(StandardCharsets.UTF_8)); + messageGroups.computeIfAbsent(digest.getValue(), k -> new ArrayList<>()).add(message); + } + myMergedMessages.clear(); + myMergedMessages.addAll(messageGroups.values()); + } + + private void updateControls() { + myMergedMessages.get(myIndex).forEach(m -> m.setRead(true)); + AbstractMessage message = selectedMessage(); + ErrorReportSubmitter submitter = getSubmitter(message.getThrowable()); + + updateLabels(message, submitter); + + updateDetails(message); + + if (myInternalMode) { + updateAssigneePanel(message, submitter); + } + + updateCredentialsPanel(submitter); + + setOKActionEnabled(submitter != null && !(message.isSubmitted() || message.isSubmitting())); + setOKButtonText(submitter != null ? submitter.getReportActionText() : DiagnosticBundle.message("error.report.to.jetbrains.action")); + } + + private void updateLabels(AbstractMessage message, @Nullable ErrorReportSubmitter submitter) { + myCountLabel.setText(DiagnosticBundle.message("error.list.message.index.count", myIndex + 1, myMergedMessages.size())); + + Throwable t = message.getThrowable(); + if (t instanceof MessagePool.TooManyErrorsException) { + myInfoLabel.setText(message.getMessage()); + myDisableLink.setVisible(false); + myForeignPluginWarningLabel.setVisible(false); + return; + } + + PluginId pluginId = findPluginId(t); + IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); + + StringBuilder info = new StringBuilder(); + String url = null; + + if (pluginId != null) { + info.append(DiagnosticBundle.message("error.list.message.blame.plugin", plugin != null ? plugin.getName() : pluginId)); + } + else if (t instanceof AbstractMethodError) { + info.append(DiagnosticBundle.message("error.list.message.blame.unknown.plugin")); } else { - result = myCommentsTabForm.getPreferredFocusedComponent(); + info.append(DiagnosticBundle.message("error.list.message.blame.core", ApplicationNamesInfo.getInstance().getProductName())); } - return result != null ? result : super.getPreferredFocusedComponent(); + + String date = DateFormatUtil.formatPrettyDateTime(message.getDate()); + int count = myMergedMessages.get(myIndex).size(); + info.append(' ').append(DiagnosticBundle.message("error.list.message.info", date, count)); + + if (message.isSubmitted()) { + SubmittedReportInfo submissionInfo = message.getSubmissionInfo(); + appendSubmissionInformation(submissionInfo, info); + info.append('.'); + url = submissionInfo.getURL(); + } + else if (message.isSubmitting()) { + info.append(' ').append(DiagnosticBundle.message("error.list.message.submitting")); + } + + myInfoLabel.setHtmlText(XmlStringUtil.wrapInHtml(info)); + myInfoLabel.setHyperlinkTarget(url); + myInfoLabel.setToolTipText(url); + + myDisableLink.setVisible(pluginId != null && !ApplicationInfoEx.getInstanceEx().isEssentialPlugin(pluginId.getIdString())); + + if (submitter != null || plugin == null || PluginManagerMain.isDevelopedByJetBrains(plugin)) { + myForeignPluginWarningLabel.setVisible(false); + } + else { + myForeignPluginWarningLabel.setVisible(true); + String vendor = plugin.getVendor(); + String contactUrl = plugin.getVendorUrl(); + String contactEmail = plugin.getVendorEmail(); + if (!StringUtil.isEmpty(vendor) && !StringUtil.isEmpty(contactUrl)) { + myForeignPluginWarningLabel.setHtmlText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.vendor", vendor)); + myForeignPluginWarningLabel.setHyperlinkTarget(contactUrl); + } + else if (!StringUtil.isEmpty(contactUrl)) { + myForeignPluginWarningLabel.setHtmlText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.unknown")); + myForeignPluginWarningLabel.setHyperlinkTarget(contactUrl); + } + else if (!StringUtil.isEmpty(contactEmail)) { + contactEmail = StringUtil.trimStart(contactEmail, " mailto:"); + myForeignPluginWarningLabel.setHtmlText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning.vendor", contactEmail)); + myForeignPluginWarningLabel.setHyperlinkTarget("mailto:" + contactEmail); + } + else { + myForeignPluginWarningLabel.setText(DiagnosticBundle.message("error.dialog.foreign.plugin.warning")); + myForeignPluginWarningLabel.setHyperlinkTarget(null); + } + myForeignPluginWarningLabel.setToolTipText(contactUrl); + } + } + + private void updateDetails(AbstractMessage message) { + myCommentArea.setText(message.getAdditionalInfo()); + myCommentArea.setEnabled(!(message.isSubmitted() || message.isSubmitting())); + myCommentArea.setCaretPosition(0); + + myAttachmentsList.clear(); + myAttachmentsList.addItem("stacktrace.txt", true); + for (Attachment attachment : message.getAllAttachments()) { + myAttachmentsList.addItem(attachment.getName(), myInternalMode || attachment.isIncluded()); + } + myAttachmentsList.setSelectedIndex(0); + } + + private void updateAssigneePanel(AbstractMessage message, ErrorReportSubmitter submitter) { + if (submitter instanceof ITNReporter) { + myAssigneePanel.setVisible(true); + myAssigneeCombo.setEnabled(!(message.isSubmitted() || message.isSubmitting())); + Integer assignee = message.getAssigneeId(); + if (assignee == null) { + myAssigneeCombo.setSelectedIndex(-1); + } + else { + Condition lookup = d -> Objects.equals(assignee, d.getId()); + myAssigneeCombo.setSelectedIndex(ContainerUtil.indexOf(ourDevelopersList, lookup)); + } + } + else { + myAssigneePanel.setVisible(false); + } + } + + private void updateCredentialsPanel(ErrorReportSubmitter submitter) { + if (submitter instanceof ITNReporter) { + myCredentialsLabel.setVisible(true); + Credentials credentials = ErrorReportConfigurable.getCredentials(); + if (CredentialAttributesKt.isFulfilled(credentials)) { + myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", credentials.getUserName())); + } + else { + myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously")); + } + } + else { + myCredentialsLabel.setVisible(false); + } + } + + private boolean reportMessage(AbstractMessage message, boolean dialogClosed) { + ErrorReportSubmitter submitter = getSubmitter(message.getThrowable()); + if (submitter == null) return false; + + message.setSubmitting(true); + + IdeaLoggingEvent[] events; + if (message instanceof GroupedLogMessage) { + events = ((GroupedLogMessage)message).getMessages().stream().map(IdeErrorsDialog::getEvent).toArray(IdeaLoggingEvent[]::new); + } + else { + events = new IdeaLoggingEvent[]{getEvent(message)}; + } + + Container parentComponent = getRootPane(); + if (dialogClosed) { + IdeFrame frame = UIUtil.getParentOfType(IdeFrame.class, parentComponent); + parentComponent = frame != null ? frame.getComponent() : WindowManager.getInstance().findVisibleFrame(); + } + + return submitter.submit(events, message.getAdditionalInfo(), parentComponent, reportInfo -> { + message.setSubmitting(false); + message.setSubmitted(reportInfo); + ApplicationManager.getApplication().invokeLater(() -> updateOnSubmit()); + }); + } + + private static IdeaLoggingEvent getEvent(AbstractMessage message) { + if (message instanceof LogMessageEx) { + return ((LogMessageEx)message).toEvent(); + } + else { + return new IdeaLoggingEvent(message.getMessage(), message.getThrowable(), message); + } + } + + private void disablePlugin() { + PluginId pluginId = findPluginId(selectedMessage().getThrowable()); + if (pluginId != null) { + IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); + if (plugin != null) { + Ref hasDependants = new Ref<>(false); + PluginManagerCore.checkDependants(plugin, PluginManager::getPlugin, dependantId -> { + if (PluginManagerCore.CORE_PLUGIN_ID.equals(dependantId.getIdString())) { + return true; + } + else { + hasDependants.set(true); + return false; + } + }); + boolean canRestart = ApplicationManager.getApplication().isRestartCapable(); + + String message = + "" + + DiagnosticBundle.message("error.dialog.disable.prompt", plugin.getName()) + "
" + + DiagnosticBundle.message(hasDependants.get() ? "error.dialog.disable.prompt.deps" : "error.dialog.disable.prompt.lone") + "

" + + DiagnosticBundle.message(canRestart ? "error.dialog.disable.plugin.can.restart" : "error.dialog.disable.plugin.no.restart") + + ""; + String title = DiagnosticBundle.message("error.dialog.disable.plugin.title"); + String disable = DiagnosticBundle.message("error.dialog.disable.plugin.action.disable"); + String cancel = IdeBundle.message("button.cancel"); + + boolean doDisable, doRestart; + if (canRestart) { + String restart = DiagnosticBundle.message("error.dialog.disable.plugin.action.disableAndRestart"); + int result = Messages.showYesNoCancelDialog(myProject, message, title, disable, restart, cancel, Messages.getQuestionIcon()); + doDisable = result == Messages.YES || result == Messages.NO; + doRestart = result == Messages.NO; + } + else { + int result = Messages.showYesNoDialog(myProject, message, title, disable, cancel, Messages.getQuestionIcon()); + doDisable = result == Messages.YES; + doRestart = false; + } + + if (doDisable) { + PluginManagerCore.disablePlugin(pluginId.getIdString()); + if (doRestart) { + ApplicationManager.getApplication().restart(); + } + } + } + } + } + + protected void updateOnSubmit() { + if (isShowing()) { + updateControls(); + } + } + + /* UI components */ + + private static class AttachmentsList extends CheckBoxList { + private final boolean myInternalMode; + + private AttachmentsList(boolean internalMode) { + myInternalMode = internalMode; + } + + private void addItem(String item, boolean selected) { + super.addItem(item, item + " ", selected); + } + + @Override + protected boolean isEnabled(int index) { + return !myInternalMode && index > 0; + } + } + + private class BackAction extends AnAction implements DumbAware { + public BackAction() { + super("Previous", null, AllIcons.Actions.Back); + AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_PREVIOUS_TAB); + if (action != null) { + registerCustomShortcutSet(action.getShortcutSet(), getRootPane(), getDisposable()); + } + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(myIndex > 0); + } + + @Override + public void actionPerformed(AnActionEvent e) { + myIndex--; + updateControls(); + } + } + + private class ForwardAction extends AnAction implements DumbAware { + public ForwardAction() { + super("Next", null, AllIcons.Actions.Forward); + AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_NEXT_TAB); + if (action != null) { + registerCustomShortcutSet(action.getShortcutSet(), getRootPane(), getDisposable()); + } + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(myIndex < myMergedMessages.size() - 1); + } + + @Override + public void actionPerformed(AnActionEvent e) { + myIndex++; + updateControls(); + } + } + + private class ClearErrorsAction extends AbstractAction { + private ClearErrorsAction() { + super(DiagnosticBundle.message("error.dialog.clear.all.action")); + } + + @Override + public void actionPerformed(ActionEvent e) { + myMessagePool.clearErrors(); + doCancelAction(); + } + } + + private class AnalyzeAction extends AbstractAction { + private final AnAction myAnalyze; + + private AnalyzeAction(AnAction analyze) { + super(analyze.getTemplatePresentation().getText()); + putValue(Action.MNEMONIC_KEY, analyze.getTemplatePresentation().getMnemonic()); + myAnalyze = analyze; + } + + @Override + public void actionPerformed(ActionEvent e) { + DataContext ctx = DataManager.getInstance().getDataContext((Component)e.getSource()); + AnActionEvent event = AnActionEvent.createFromAnAction(myAnalyze, null, ActionPlaces.UNKNOWN, ctx); + myAnalyze.actionPerformed(event); + doCancelAction(); + } + } + + /* interfaces */ + + @Override + public void newEntryAdded() { + UIUtil.invokeLaterIfNeeded(() -> { + updateMessages(); + updateControls(); + }); + } + + @Override + public void poolCleared() { + UIUtil.invokeLaterIfNeeded(() -> doCancelAction()); + } + + @Override + public void entryWasRead() { } + + @Override + public Object getData(String dataId) { + return CURRENT_TRACE_KEY.is(dataId) ? getDetailsText(selectedMessage()) : null; + } + + /* helpers */ + + private static String getDetailsText(AbstractMessage message) { + Throwable t = message.getThrowable(); + return t instanceof MessagePool.TooManyErrorsException ? message.getMessage() : + t instanceof NullPointerException ? message.getThrowableText() : + message.getMessage() + "\n" + message.getThrowableText(); } @Nullable - private AbstractMessage getSelectedMessage() { - return myIndex >= 0 && myIndex < myMergedMessages.size() ? myMergedMessages.get(myIndex).get(0) : null; - } - - @Nullable - public static PluginId findPluginId(Throwable t) { + public static PluginId findPluginId(@NotNull Throwable t) { if (t instanceof PluginException) { return ((PluginException)t).getPluginId(); } @@ -651,9 +717,7 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene String className = element.getClassName(); if (visitedClassNames.add(className) && PluginManagerCore.isPluginClass(className)) { PluginId id = PluginManagerCore.getPluginByClassName(className); - if (LOG.isDebugEnabled()) { - LOG.debug(diagnosePluginDetection(className, id)); - } + logPluginDetection(className, id); return id; } } @@ -711,231 +775,71 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene return null; } - @NotNull - private static String diagnosePluginDetection(String className, PluginId id) { - String msg = "Detected plugin " + id + " by class " + className; - IdeaPluginDescriptor descriptor = PluginManager.getPlugin(id); - if (descriptor != null) { - ClassLoader loader = descriptor.getPluginClassLoader(); - msg += "; loader=" + loader + '/' + loader.getClass(); - if (loader instanceof PluginClassLoader) { - msg += "; loaded class: " + ((PluginClassLoader)loader).hasLoadedClass(className); - } - } - return msg; - } - - private class ClearErrorsAction extends AbstractAction { - private ClearErrorsAction() { - super(DiagnosticBundle.message("error.dialog.clear.action")); - } - - @Override - public void actionPerformed(ActionEvent e) { - myMessagePool.clearErrors(); - doOKAction(); - } - - public void update() { - putValue(NAME, DiagnosticBundle.message(myMergedMessages.size() > 1 ? "error.dialog.clear.all.action" : "error.dialog.clear.action")); - setEnabled(!myMergedMessages.isEmpty()); - } - } - - private class BlameAction extends AbstractAction { - private BlameAction() { - super(DiagnosticBundle.message("error.report.to.jetbrains.action")); - } - - public void update() { - AbstractMessage logMessage = getSelectedMessage(); - if (logMessage != null) { - ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable()); - if (submitter != null) { - putValue(NAME, submitter.getReportActionText()); - setEnabled(!(logMessage.isSubmitting() || logMessage.isSubmitted())); - return; + private static void logPluginDetection(String className, PluginId id) { + if (LOG.isDebugEnabled()) { + String message = "Detected plugin " + id + " by class " + className; + IdeaPluginDescriptor descriptor = PluginManager.getPlugin(id); + if (descriptor != null) { + ClassLoader loader = descriptor.getPluginClassLoader(); + message += "; loader=" + loader + '/' + loader.getClass(); + if (loader instanceof PluginClassLoader) { + message += "; loaded class: " + ((PluginClassLoader)loader).hasLoadedClass(className); } } - putValue(NAME, DiagnosticBundle.message("error.report.to.jetbrains.action")); - setEnabled(false); + LOG.debug(message); } - - @Override - public void actionPerformed(ActionEvent e) { - boolean closeDialog = myMergedMessages.size() == 1; - boolean reportingStarted = reportMessage(getSelectedMessage(), closeDialog); - if (closeDialog) { - if (reportingStarted) { - doOKAction(); - } - } - else { - rebuildHeaders(); - updateControls(); - } - } - - private boolean reportMessage(AbstractMessage logMessage, boolean dialogClosed) { - if (logMessage == null) return false; - ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable()); - if (submitter == null) return false; - - logMessage.setSubmitting(true); - if (!dialogClosed) { - updateControls(); - } - Container parentComponent; - if (dialogClosed) { - IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane()); - parentComponent = ideFrame != null ? ideFrame.getComponent() : WindowManager.getInstance().findVisibleFrame(); - } - else { - parentComponent = getContentPane(); - } - - return submitter.submit(getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, submittedReportInfo -> { - logMessage.setSubmitting(false); - logMessage.setSubmitted(submittedReportInfo); - ApplicationManager.getApplication().invokeLater(() -> { - if (!dialogClosed) { - updateOnSubmit(); - } - }); - }); - } - - private IdeaLoggingEvent[] getEvents(AbstractMessage logMessage) { - if (logMessage instanceof GroupedLogMessage) { - List messages = ((GroupedLogMessage)logMessage).getMessages(); - IdeaLoggingEvent[] res = new IdeaLoggingEvent[messages.size()]; - for (int i = 0; i < res.length; i++) { - res[i] = getEvent(messages.get(i)); - } - return res; - } - else { - return new IdeaLoggingEvent[]{getEvent(logMessage)}; - } - } - - private IdeaLoggingEvent getEvent(AbstractMessage logMessage) { - if (logMessage instanceof LogMessageEx) { - return ((LogMessageEx)logMessage).toEvent(); - } - else { - return new IdeaLoggingEvent(logMessage.getMessage(), logMessage.getThrowable()) { - @Override - public AbstractMessage getData() { - return logMessage; - } - }; - } - } - } - - protected void updateOnSubmit() { - updateControls(); - } - - @Override - public Object getData(String dataId) { - if (CURRENT_TRACE_KEY.is(dataId)) { - AbstractMessage message = getSelectedMessage(); - if (message != null) { - return getDetailsText(message); - } - } - return null; } @Nullable - static ErrorReportSubmitter getSubmitter(Throwable throwable) { - if (throwable instanceof MessagePool.TooManyErrorsException || throwable instanceof AbstractMethodError) { + static ErrorReportSubmitter getSubmitter(@NotNull Throwable t) { + if (t instanceof MessagePool.TooManyErrorsException || t instanceof AbstractMethodError) { return null; } - PluginId pluginId = findPluginId(throwable); + ErrorReportSubmitter[] reporters; try { reporters = Extensions.getExtensions(ExtensionPoints.ERROR_HANDLER_EP); } - catch (Throwable t) { + catch (Throwable ignored) { return null; } + + PluginId pluginId = findPluginId(t); IdeaPluginDescriptor plugin = PluginManager.getPlugin(pluginId); - if (plugin == null) { - return getCorePluginSubmitter(reporters); - } - for (ErrorReportSubmitter reporter : reporters) { - PluginDescriptor descriptor = reporter.getPluginDescriptor(); - if (descriptor != null && Comparing.equal(pluginId, descriptor.getPluginId())) { - return reporter; + + if (plugin != null) { + for (ErrorReportSubmitter reporter : reporters) { + PluginDescriptor descriptor = reporter.getPluginDescriptor(); + if (descriptor != null && Comparing.equal(pluginId, descriptor.getPluginId())) { + return reporter; + } } } - if (PluginManagerMain.isDevelopedByJetBrains(plugin)) { - return getCorePluginSubmitter(reporters); + + if (plugin == null || PluginManagerMain.isDevelopedByJetBrains(plugin)) { + for (ErrorReportSubmitter reporter : reporters) { + PluginDescriptor descriptor = reporter.getPluginDescriptor(); + if (descriptor == null || PluginId.getId(PluginManagerCore.CORE_PLUGIN_ID) == descriptor.getPluginId()) { + return reporter; + } + } } + return null; } - @Nullable - private static ErrorReportSubmitter getCorePluginSubmitter(ErrorReportSubmitter[] reporters) { - for (ErrorReportSubmitter reporter : reporters) { - PluginDescriptor descriptor = reporter.getPluginDescriptor(); - if (descriptor == null || PluginId.getId(PluginManagerCore.CORE_PLUGIN_ID) == descriptor.getPluginId()) { - return reporter; + public static void appendSubmissionInformation(@NotNull SubmittedReportInfo info, @NotNull StringBuilder out) { + if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED) { + out.append(' ').append(DiagnosticBundle.message("error.list.message.submission.failed")); + } + else if (info.getURL() != null && info.getLinkText() != null) { + out.append(' ').append(DiagnosticBundle.message("error.list.message.submitted.as.link", info.getURL(), info.getLinkText())); + if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { + out.append(' ').append(DiagnosticBundle.message("error.list.message.duplicate")); } } - return null; - } - - @Override - public void doOKAction() { - onClose(); - super.doOKAction(); - } - - @Override - public void doCancelAction() { - onClose(); - super.doCancelAction(); - } - - private void onClose() { - myRawMessages.forEach(each -> each.setRead(true)); - if (myTabs != null) { - PropertiesComponent.getInstance().setValue(ACTIVE_TAB_OPTION, String.valueOf(myTabs.getSelectedIndex())); - } - } - - @Override - @SuppressWarnings("SpellCheckingInspection") - protected String getDimensionServiceKey() { - return "IdeErrosDialog"; - } - - private class AnalyzeAction extends AbstractAction { - private final AnAction myAnalyze; - - public AnalyzeAction(AnAction analyze) { - super(analyze.getTemplatePresentation().getText()); - putValue(Action.MNEMONIC_KEY, analyze.getTemplatePresentation().getMnemonic()); - myAnalyze = analyze; - } - - public void update() { - setEnabled(getSelectedMessage() != null); - } - - @Override - public void actionPerformed(ActionEvent e) { - DataContext dataContext = ((DataManagerImpl)DataManager.getInstance()).getDataContextTest((Component)e.getSource()); - AnActionEvent event = AnActionEvent.createFromAnAction(myAnalyze, null, ActionPlaces.UNKNOWN, dataContext); - Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project != null) { - myAnalyze.actionPerformed(event); - doOKAction(); - } + else { + out.append(' ').append(DiagnosticBundle.message("error.list.message.submitted")); } } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java index f0ca96afa486..896c8a631a46 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeMessagePanel.java @@ -111,17 +111,17 @@ public class IdeMessagePanel extends JPanel implements MessagePoolListener, Icon } private void doOpenErrorsDialog(@Nullable LogMessage message) { - myDialog = new IdeErrorsDialog(myMessagePool, message) { - @Override - public void doOKAction() { - super.doOKAction(); - disposeDialog(this); - } + if (isOtherModalWindowActive()) { + return; + } + Project project = myFrame != null ? myFrame.getProject() : null; + myDialog = new IdeErrorsDialog(myMessagePool, project, message) { @Override - public void doCancelAction() { - super.doCancelAction(); - disposeDialog(this); + protected void dispose() { + super.dispose(); + myDialog = null; + updateFatalErrorsIcon(); } @Override @@ -131,17 +131,11 @@ public class IdeMessagePanel extends JPanel implements MessagePoolListener, Icon } }; - myMessagePool.addListener(myDialog); - if (!isOtherModalWindowActive()) { - if (myBalloon != null) { - myBalloon.hide(); - } - myDialog.show(); - } - else { - myDialog.close(0); - disposeDialog(myDialog); + if (myBalloon != null) { + myBalloon.hide(); } + + myDialog.show(); } private void updateState(IdeErrorsIcon.State state) { @@ -149,12 +143,6 @@ public class IdeMessagePanel extends JPanel implements MessagePoolListener, Icon UIUtil.invokeLaterIfNeeded(() -> setVisible(state != IdeErrorsIcon.State.NoErrors)); } - private void disposeDialog(IdeErrorsDialog dialog) { - myMessagePool.removeListener(dialog); - updateFatalErrorsIcon(); - myDialog = null; - } - @Override public void newEntryAdded() { updateFatalErrorsIcon(); diff --git a/platform/platform-impl/src/com/intellij/diagnostic/errordialog/DetailsTabForm.java b/platform/platform-impl/src/com/intellij/diagnostic/errordialog/DetailsTabForm.java index 15b450b8eaaa..e044f32636bc 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/errordialog/DetailsTabForm.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/errordialog/DetailsTabForm.java @@ -3,7 +3,6 @@ package com.intellij.diagnostic.errordialog; import com.intellij.diagnostic.Developer; import com.intellij.diagnostic.DiagnosticBundle; -import com.intellij.diagnostic.IdeErrorsDialog; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.ComboboxSpeedSearch; import com.intellij.ui.IdeBorderFactory; @@ -35,7 +34,7 @@ public class DetailsTabForm { public DetailsTabForm(@Nullable Action analyzeAction) { myCommentsArea.setTitle(DiagnosticBundle.message("error.dialog.comment.prompt")); myDetailsPane.setBackground(UIUtil.getTextFieldBackground()); - myDetailsHolder.setPreferredSize(JBUI.size(IdeErrorsDialog.COMPONENTS_WIDTH, 150)); + myDetailsHolder.setPreferredSize(JBUI.size(670, 150)); myDetailsHolder.setBorder(IdeBorderFactory.createBorder()); if (analyzeAction != null) { myAnalyzeStacktraceButton.setAction(analyzeAction); diff --git a/platform/platform-impl/src/com/intellij/diagnostic/errordialog/IdeErrorsDialog.form b/platform/platform-impl/src/com/intellij/diagnostic/errordialog/IdeErrorsDialog.form deleted file mode 100644 index c8ab1fb58e90..000000000000 --- a/platform/platform-impl/src/com/intellij/diagnostic/errordialog/IdeErrorsDialog.form +++ /dev/null @@ -1,143 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties index 820f7260c188..242835bec2f6 100644 --- a/platform/platform-resources-en/src/messages/DiagnosticBundle.properties +++ b/platform/platform-resources-en/src/messages/DiagnosticBundle.properties @@ -12,9 +12,11 @@ diagnostic.error.report.send=&Send diagnostic.error.report.description=If you would like to get notified when the exceptions you submit are fixed,\nplease fill in your JetBrains Account login and password. diagnostic.error.report.login.name=&Username: diagnostic.error.report.login.password=&Password: -diagnostic.error.report.submit.error.anonymously=Submit report anonymously or use JetBrains Account... -diagnostic.error.report.submit.report.as=Submit report as {0} -diagnostic.error.report.additional.info.label=&Additional information (steps to reproduce, what were you doing when the exception occurred): +diagnostic.error.report.submit.error.anonymously=Submit anonymously or use JetBrains Account +diagnostic.error.report.submit.report.as=Submit as {0} or change account +diagnostic.error.report.additional.info.label=&Additional information (steps to reproduce, what were you doing when the exception occurred, etc.): +diagnostic.error.report.attachments.label=To investigate and fix the problem, we need the following files to be attached to the bug report.
\ + Note: all the data you send will be kept private. error.report.to.jetbrains.action=&Report to JetBrains error.report.gratitude=Thank you for your feedback! error.report.authentication.failed=JetBrains Account authentication failed. Do you want to try again? @@ -25,6 +27,7 @@ error.report.new.eap.build.message=New build {0} is available. error.report.sending.failure=Sending failed. Do you want to try again? error.list.message.info={0}. Occurred {1,choice,1#once|2#{1} times} since the last clear. error.list.message.unread=Unread. +error.list.message.submitting=Submitting... error.list.message.submission.failed=Submission failed error.list.message.submitted.as.link=Submitted as {1} error.list.message.duplicate=[Duplicate] @@ -76,10 +79,16 @@ error.dialog.disable.plugin.prompt=Are you sure to disable plugin {0}{0}? +error.dialog.disable.prompt.lone=Functionality provided by the plugin will no longer be available. +error.dialog.disable.prompt.deps=Functionality provided by this and dependent plugins will no longer be available. +error.dialog.disable.plugin.can.restart=Changes in plugin configuration will take an effect after restart. +error.dialog.disable.plugin.no.restart=Please restart the IDE to apply changes in plugin configuration. error.dialog.disable.plugin.action.disable=&Disable error.dialog.disable.plugin.action.disableAndRestart=Disable and &Restart -error.dialog.foreign.plugin.warning.text=This plugin is not a production of JetBrains. Please report the problem to plugin vendor. -error.dialog.foreign.plugin.warning.text.vendor=This plugin is not a production of JetBrains. Please report the problem to plugin vendor: +error.dialog.foreign.plugin.warning=This plugin is not a production of JetBrains. Please report the problem to plugin vendor. +error.dialog.foreign.plugin.warning.unknown=This plugin is not a production of JetBrains. Please report the problem to plugin vendor. +error.dialog.foreign.plugin.warning.vendor=This plugin is not a production of JetBrains. Please report the problem to {0}. error.dialog.clear.action=&Clear error.dialog.clear.all.action=&Clear all error.list.empty=(no errors)