GUI: reworked error dialog:

* added client version and improved stack trace;
* added copy to clipboard button;
* added button to create new issue on github (with prefilled form fields like error text);
* added GUI size settings support;
* some old errors now use new error dialog instead message box;
This commit is contained in:
Oleg Agafonov 2024-07-05 17:42:36 +04:00
parent 2631b31b8a
commit 6c0f7ebb90
11 changed files with 284 additions and 125 deletions

View file

@ -1,6 +1,7 @@
package mage.client; package mage.client;
import mage.MageException; import mage.MageException;
import mage.cards.RateCard;
import mage.cards.action.ActionCallback; import mage.cards.action.ActionCallback;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
@ -38,7 +39,6 @@ import mage.client.util.stats.UpdateMemUsageTask;
import mage.components.ImagePanel; import mage.components.ImagePanel;
import mage.components.ImagePanelStyle; import mage.components.ImagePanelStyle;
import mage.constants.PlayerAction; import mage.constants.PlayerAction;
import mage.cards.RateCard;
import mage.interfaces.MageClient; import mage.interfaces.MageClient;
import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.CallbackClient;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
@ -72,14 +72,13 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.SocketException; import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.*; import java.util.*;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import java.util.stream.Collectors;
/** /**
* Client app * Client app
@ -1356,11 +1355,52 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
userRequestDialog.showDialog(userRequestMessage); userRequestDialog.showDialog(userRequestMessage);
} }
public void showErrorDialog(final String title, final String message) { public void showErrorDialog(String errorType, Exception e) {
String errorMessage = e.getMessage();
if (errorMessage == null || errorMessage.isEmpty() || errorMessage.equals("Null")) {
errorMessage = e.getClass().getSimpleName() + " - look at server or client logs for more details";
}
int maxLines = 10;
String newLine = "\n";
// main error
String mainError = Arrays.stream(e.getStackTrace())
.map(StackTraceElement::toString)
.limit(maxLines)
.collect(Collectors.joining(newLine));
if (e.getStackTrace().length > maxLines) {
mainError += newLine + "and other " + (e.getStackTrace().length - maxLines) + " lines";
}
// root error
String rootError = "";
Throwable root = ThreadUtils.findRootException(e);
if (root != e) {
rootError = Arrays.stream(root.getStackTrace())
.map(StackTraceElement::toString)
.limit(maxLines)
.collect(Collectors.joining(newLine));
if (root.getStackTrace().length > maxLines) {
rootError += newLine + "and other " + (root.getStackTrace().length - maxLines) + " lines";
}
}
String allErrors = mainError;
if (!rootError.isEmpty()) {
allErrors += newLine + "Root caused by:" + newLine + rootError;
}
showErrorDialog(errorType,
e.getClass().getSimpleName(),
errorMessage + newLine + newLine + "Stack trace:" + newLine + allErrors
);
}
public void showErrorDialog(String errorType, String errorTitle, String errorText) {
if (SwingUtilities.isEventDispatchThread()) { if (SwingUtilities.isEventDispatchThread()) {
errorDialog.showDialog(title, message); errorDialog.showDialog(errorType, errorTitle, errorText);
} else { } else {
SwingUtilities.invokeLater(() -> errorDialog.showDialog(title, message)); SwingUtilities.invokeLater(() -> errorDialog.showDialog(errorType, errorTitle, errorText));
} }
} }
@ -1587,7 +1627,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
.filter(Component::isVisible) .filter(Component::isVisible)
.filter(p -> p instanceof MagePane) .filter(p -> p instanceof MagePane)
.map(p -> (MagePane) p) .map(p -> (MagePane) p)
.filter(p-> !onlyActive || p.isActiveTable()) .filter(p -> !onlyActive || p.isActiveTable())
.count(); .count();
} }
@ -1813,21 +1853,14 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
} }
} }
this.connectDialog.changeGUISize();
this.errorDialog.changeGUISize();
updateTooltipContainerSizes(); updateTooltipContainerSizes();
} }
public static void showWhatsNewDialog() { public static void showWhatsNewDialog() {
try { AppUtil.openUrlInBrowser("https://jaydi85.github.io/xmage-web-news/news.html");
URI newsURI = new URI("https://jaydi85.github.io/xmage-web-news/news.html");
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
desktop.browse(newsURI);
}
} catch (URISyntaxException e) {
LOGGER.error("URI Syntax error when creating news link", e);
} catch (IOException e) {
LOGGER.error("IOException while loading news page", e);
}
} }
public boolean isGameFrameActive(UUID gameId) { public boolean isGameFrameActive(UUID gameId) {

View file

@ -6,6 +6,7 @@ import mage.client.dialog.PreferencesDialog;
import mage.client.util.gui.ColorsChooser; import mage.client.util.gui.ColorsChooser;
import mage.client.util.gui.FastSearchUtil; import mage.client.util.gui.FastSearchUtil;
import mage.client.util.sets.ConstructedFormats; import mage.client.util.sets.ConstructedFormats;
import org.apache.log4j.Logger;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.CompoundBorder; import javax.swing.border.CompoundBorder;
@ -25,6 +26,8 @@ import static mage.cards.decks.DeckFormats.XMAGE;
*/ */
public class DeckGeneratorDialog { public class DeckGeneratorDialog {
private static final Logger logger = Logger.getLogger(DeckGeneratorDialog.class);
private static JDialog dlg; private static JDialog dlg;
private static String selectedColors; private static String selectedColors;
private static JComboBox cbSets, cbDeckSize, cbCMC; private static JComboBox cbSets, cbDeckSize, cbCMC;
@ -332,7 +335,8 @@ public class DeckGeneratorDialog {
cleanUp(); cleanUp();
return tmp.getAbsolutePath(); return tmp.getAbsolutePath();
} catch (Exception e) { } catch (Exception e) {
MageFrame.getInstance().showError("Couldn't generate deck. Try again."); logger.error("Can't generate deck due " + e, e);
MageFrame.getInstance().showErrorDialog("CLIENT - error on random deck save", e);
} }
return null; return null;
} }

View file

@ -5,10 +5,9 @@ import mage.cards.decks.DeckFormats;
import mage.cards.decks.exporter.DeckExporter; import mage.cards.decks.exporter.DeckExporter;
import mage.client.MageFrame; import mage.client.MageFrame;
import mage.client.dialog.MageDialog; import mage.client.dialog.MageDialog;
import mage.client.util.AppUtil;
import javax.swing.*; import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
@ -62,15 +61,6 @@ public class DeckExportClipboardDialog extends MageDialog {
this.setVisible(true); this.setVisible(true);
} }
private void setClipboardStringData(String text) {
try {
StringSelection data = new StringSelection(text);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(data, data);
} catch (HeadlessException e) {
//e.printStackTrace();
}
}
private void onOK() { private void onOK() {
onCopyToClipboard(); onCopyToClipboard();
this.removeDialog(); this.removeDialog();
@ -95,7 +85,7 @@ public class DeckExportClipboardDialog extends MageDialog {
} }
private void onCopyToClipboard() { private void onCopyToClipboard() {
setClipboardStringData(editData.getText()); AppUtil.setClipboardData(editData.getText());
} }
/** /**

View file

@ -5,6 +5,7 @@ import mage.choices.ChoiceImpl;
import mage.client.MageFrame; import mage.client.MageFrame;
import mage.client.SessionHandler; import mage.client.SessionHandler;
import mage.client.preference.MagePreferences; import mage.client.preference.MagePreferences;
import mage.client.util.AppUtil;
import mage.client.util.ClientDefaultSettings; import mage.client.util.ClientDefaultSettings;
import mage.client.util.gui.countryBox.CountryItemEditor; import mage.client.util.gui.countryBox.CountryItemEditor;
import mage.remote.Connection; import mage.remote.Connection;
@ -738,13 +739,7 @@ public class ConnectDialog extends MageDialog {
}//GEN-LAST:event_btnFlagSearchActionPerformed }//GEN-LAST:event_btnFlagSearchActionPerformed
private void btnCheckStatusActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheckStatusActionPerformed private void btnCheckStatusActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheckStatusActionPerformed
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { AppUtil.openUrlInBrowser("http://xmage.today/servers/");
try {
Desktop.getDesktop().browse(new URI("http://xmage.today/servers/"));
} catch (Exception e) {
//
}
}
}//GEN-LAST:event_btnCheckStatusActionPerformed }//GEN-LAST:event_btnCheckStatusActionPerformed
private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed

View file

@ -1,4 +1,4 @@
<?xml version="1.1" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo"> <Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo">
<Properties> <Properties>
@ -26,19 +26,17 @@
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Component id="pnlInfo" alignment="0" max="32767" attributes="1"/>
<Component id="jScrollPane2" pref="647" max="32767" attributes="1"/> <Component id="pnlError" alignment="0" max="32767" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jScrollPane1" pref="647" max="32767" attributes="1"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0"> <Group type="102" alignment="1" attributes="0">
<Component id="btnOK" min="-2" pref="60" max="-2" attributes="0"/> <Component id="btnCopyToClipboard" min="-2" pref="202" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="btnOpenGithub" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="btnOK" min="-2" pref="96" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -46,11 +44,15 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/> <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="jScrollPane2" pref="43" max="32767" attributes="0"/> <Component id="pnlInfo" min="-2" pref="73" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="237" max="32767" attributes="0"/> <Component id="pnlError" pref="225" max="32767" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="btnOK" min="-2" max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0">
<Component id="btnOK" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnCopyToClipboard" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnOpenGithub" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/> <EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -59,26 +61,32 @@
<SubComponents> <SubComponents>
<Component class="javax.swing.JButton" name="btnOK"> <Component class="javax.swing.JButton" name="btnOK">
<Properties> <Properties>
<Property name="text" type="java.lang.String" value="OK"/> <Property name="text" type="java.lang.String" value="Close"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[59, 33]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[59, 33]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[59, 33]"/>
</Property>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOKActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOKActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1"> <Container class="javax.swing.JScrollPane" name="pnlError">
<AuxValues> <AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTextArea" name="lblMessage"> <Component class="javax.swing.JTextArea" name="textError">
<Properties> <Properties>
<Property name="columns" type="int" value="20"/>
<Property name="editable" type="boolean" value="false"/> <Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> <Property name="columns" type="int" value="20"/>
<Font name="Arial" size="10" style="0"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/> <Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="5"/> <Property name="rows" type="int" value="5"/>
<Property name="wrapStyleWord" type="boolean" value="true"/> <Property name="wrapStyleWord" type="boolean" value="true"/>
@ -86,25 +94,43 @@
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JScrollPane" name="jScrollPane2"> <Container class="javax.swing.JScrollPane" name="pnlInfo">
<AuxValues> <AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues> </AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents> <SubComponents>
<Component class="javax.swing.JTextArea" name="jTextArea1"> <Component class="javax.swing.JTextArea" name="textInfo">
<Properties> <Properties>
<Property name="columns" type="int" value="20"/>
<Property name="editable" type="boolean" value="false"/> <Property name="editable" type="boolean" value="false"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> <Property name="lineWrap" type="boolean" value="true"/>
<Font name="Arial" size="10" style="0"/> <Property name="text" type="java.lang.String" value="[bug report instructions]"/>
</Property>
<Property name="rows" type="int" value="2"/>
<Property name="text" type="java.lang.String" value="An error has occurred on the MAGE server. Your last action will be rollbacked.&#xa;Please post the following report here: http://www.slightlymagic.net/forum/posting.php?mode=reply&amp;f=70&amp;t=3116"/>
</Properties> </Properties>
</Component> </Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="btnCopyToClipboard">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/copy_24.png"/>
</Property>
<Property name="text" type="java.lang.String" value="Copy error to clipboard"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCopyToClipboardActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnOpenGithub">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/search_24.png"/>
</Property>
<Property name="text" type="java.lang.String" value="Open github and create bug report"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOpenGithubActionPerformed"/>
</Events>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View file

@ -1,21 +1,40 @@
package mage.client.dialog; package mage.client.dialog;
import mage.client.MageFrame;
import mage.client.util.AppUtil;
import mage.client.util.GUISizeHelper;
import mage.util.CardUtil;
/** /**
* Game GUI: error dialog * GUI: error dialog with copyable error message
* *
* @author BetaSteward_at_googlemail.com * @author JayDi85
*/ */
public class ErrorDialog extends MageDialog { public class ErrorDialog extends MageDialog {
/** Creates new form ErrorDialog */ private final String GITHUB_ISSUES_PAGE = "https://github.com/magefree/mage/issues";
public ErrorDialog() { public ErrorDialog() {
initComponents(); initComponents();
} }
public void showDialog(String title, String message) { public void showDialog(String errorType, String errorTitle, String errorText) {
this.setTitle(title); this.textInfo.setText("You can report bugs and create new feature requests at github: " + GITHUB_ISSUES_PAGE);
this.lblMessage.setText(message); this.textInfo.setCaretPosition(0);
this.lblMessage.setCaretPosition(0);
String fullTitle = errorType + " - " + errorTitle;
this.setTitle(fullTitle);
// add additional info
String fullError = "Error type: " + fullTitle + "\n"
+ "Client version: " + MageFrame.getInstance().getVersion().toString() + "\n"
+ "\n"
+ errorText;
this.textError.setText(fullError);
this.textError.setCaretPosition(0);
this.changeGUISize();
this.pack(); this.pack();
this.revalidate(); this.revalidate();
this.repaint(); this.repaint();
@ -23,6 +42,31 @@ public class ErrorDialog extends MageDialog {
this.setVisible(true); this.setVisible(true);
} }
@Override
public void changeGUISize() {
super.changeGUISize();
this.textError.setFont(GUISizeHelper.menuFont);
this.textInfo.setFont(GUISizeHelper.menuFont);
this.btnCopyToClipboard.setFont(GUISizeHelper.menuFont);
this.btnOpenGithub.setFont(GUISizeHelper.menuFont);
this.btnOK.setFont(GUISizeHelper.menuFont);
}
private void openGithub() {
// create new issue on github with predefined fields
String title = this.getTitle();
String body = this.textError.getText();
String labels = "bug";
String url = String.format("%s/new?labels=%s&title=%s&body=%s",
GITHUB_ISSUES_PAGE,
CardUtil.urlEncode(labels),
CardUtil.urlEncode(title),
CardUtil.urlEncode(body)
);
AppUtil.openUrlInBrowser(url);
}
/** This method is called from within the constructor to /** This method is called from within the constructor to
* initialize the form. * initialize the form.
* WARNING: Do NOT modify this code. The content of this method is * WARNING: Do NOT modify this code. The content of this method is
@ -33,31 +77,53 @@ public class ErrorDialog extends MageDialog {
private void initComponents() { private void initComponents() {
btnOK = new javax.swing.JButton(); btnOK = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane(); pnlError = new javax.swing.JScrollPane();
lblMessage = new javax.swing.JTextArea(); textError = new javax.swing.JTextArea();
jScrollPane2 = new javax.swing.JScrollPane(); pnlInfo = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea(); textInfo = new javax.swing.JTextArea();
btnCopyToClipboard = new javax.swing.JButton();
btnOpenGithub = new javax.swing.JButton();
setResizable(true); setResizable(true);
setTitle("Error"); setTitle("Error");
btnOK.setText("OK"); btnOK.setText("Close");
btnOK.addActionListener(this::btnOKActionPerformed); btnOK.setMaximumSize(new java.awt.Dimension(59, 33));
btnOK.setMinimumSize(new java.awt.Dimension(59, 33));
btnOK.setPreferredSize(new java.awt.Dimension(59, 33));
btnOK.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnOKActionPerformed(evt);
}
});
lblMessage.setColumns(20); textError.setEditable(false);
lblMessage.setEditable(false); textError.setColumns(20);
lblMessage.setFont(new java.awt.Font("Arial", 0, 10)); // NOI18N textError.setLineWrap(true);
lblMessage.setLineWrap(true); textError.setRows(5);
lblMessage.setRows(5); textError.setWrapStyleWord(true);
lblMessage.setWrapStyleWord(true); pnlError.setViewportView(textError);
jScrollPane1.setViewportView(lblMessage);
jTextArea1.setColumns(20); textInfo.setEditable(false);
jTextArea1.setEditable(false); textInfo.setLineWrap(true);
jTextArea1.setFont(new java.awt.Font("Arial", 0, 10)); // NOI18N textInfo.setText("[bug report instructions]");
jTextArea1.setRows(2); pnlInfo.setViewportView(textInfo);
jTextArea1.setText("An error has occurred on the MAGE server. Your last action will be rollbacked.\nPlease post the following report here: https://github.com/magefree/mage/issues");
jScrollPane2.setViewportView(jTextArea1); btnCopyToClipboard.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/copy_24.png"))); // NOI18N
btnCopyToClipboard.setText("Copy error to clipboard");
btnCopyToClipboard.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCopyToClipboardActionPerformed(evt);
}
});
btnOpenGithub.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/search_24.png"))); // NOI18N
btnOpenGithub.setText("Open github and create bug report");
btnOpenGithub.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnOpenGithubActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout); getContentPane().setLayout(layout);
@ -66,25 +132,28 @@ public class ErrorDialog extends MageDialog {
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addComponent(pnlInfo)
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE) .addComponent(pnlError)
.addContainerGap())
.addGroup(layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE)
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(btnOK, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(btnCopyToClipboard, javax.swing.GroupLayout.PREFERRED_SIZE, 202, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnOpenGithub)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btnOK, javax.swing.GroupLayout.PREFERRED_SIZE, 96, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6) .addGap(6, 6, 6)
.addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 43, Short.MAX_VALUE) .addComponent(pnlInfo, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE) .addComponent(pnlError, javax.swing.GroupLayout.DEFAULT_SIZE, 225, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(btnOK) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnOK, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnCopyToClipboard)
.addComponent(btnOpenGithub))
.addGap(12, 12, 12)) .addGap(12, 12, 12))
); );
@ -95,13 +164,23 @@ public class ErrorDialog extends MageDialog {
this.hideDialog(); this.hideDialog();
}//GEN-LAST:event_btnOKActionPerformed }//GEN-LAST:event_btnOKActionPerformed
private void btnCopyToClipboardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCopyToClipboardActionPerformed
AppUtil.setClipboardData(textError.getText());
}//GEN-LAST:event_btnCopyToClipboardActionPerformed
private void btnOpenGithubActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOpenGithubActionPerformed
openGithub();
}//GEN-LAST:event_btnOpenGithubActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnCopyToClipboard;
private javax.swing.JButton btnOK; private javax.swing.JButton btnOK;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JButton btnOpenGithub;
private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane pnlError;
private javax.swing.JTextArea jTextArea1; private javax.swing.JScrollPane pnlInfo;
private javax.swing.JTextArea lblMessage; private javax.swing.JTextArea textError;
private javax.swing.JTextArea textInfo;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
} }

View file

@ -786,9 +786,9 @@ public class NewTableDialog extends MageDialog {
this.repaint(); this.repaint();
} }
private void handleError(Exception ex) { private void handleError(Exception e) {
logger.fatal("Error loading deck", ex); logger.fatal("Can't join table due " + e, e);
MageFrame.getInstance().showErrorDialog("Error loading deck", ex.getMessage()); MageFrame.getInstance().showErrorDialog("CLIENT - error on join table", e);
} }
public void showDialog(UUID roomId) { public void showDialog(UUID roomId) {

View file

@ -29,6 +29,8 @@ import java.awt.event.KeyEvent;
import java.util.*; import java.util.*;
/** /**
* Client side implementation (process commands from a server)
*
* @author BetaSteward_at_googlemail.com, JayDi85 * @author BetaSteward_at_googlemail.com, JayDi85
*/ */
public class CallbackClientImpl implements CallbackClient { public class CallbackClientImpl implements CallbackClient {
@ -266,7 +268,7 @@ public class CallbackClientImpl implements CallbackClient {
} }
case GAME_ERROR: { case GAME_ERROR: {
frame.showErrorDialog("Game Error", (String) callback.getData()); frame.showErrorDialog("SERVER", "game error", (String) callback.getData());
break; break;
} }
@ -693,12 +695,8 @@ public class CallbackClientImpl implements CallbackClient {
}); });
} }
private void handleException(Exception ex) { private void handleException(Exception e) {
logger.fatal("Client error\n", ex); logger.fatal("General error\n", e);
String errorMessage = ex.getMessage(); frame.showErrorDialog("General error", e);
if (errorMessage == null || errorMessage.isEmpty() || errorMessage.equals("Null")) {
errorMessage = ex.getClass().getSimpleName() + " - look at server or client logs for more details";
}
frame.showError("Server's error: " + errorMessage);
} }
} }

View file

@ -1,14 +1,24 @@
package mage.client.util; package mage.client.util;
import mage.client.MageFrame; import mage.client.MageFrame;
import org.apache.log4j.Logger;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.UUID; import java.util.UUID;
/** /**
* Helper class for client side app
*
* @author JayDi85 * @author JayDi85
*/ */
public class AppUtil { public class AppUtil {
private static final Logger logger = Logger.getLogger(AppUtil.class);
/** /**
* Application is active in operation system (got user focus) * Application is active in operation system (got user focus)
*/ */
@ -25,4 +35,29 @@ public class AppUtil {
public static boolean isGameActive(UUID gameId) { public static boolean isGameActive(UUID gameId) {
return MageFrame.getInstance().isGameFrameActive(gameId); return MageFrame.getInstance().isGameFrameActive(gameId);
} }
/**
* Save text to clipboard
*/
public static void setClipboardData(String text) {
try {
StringSelection data = new StringSelection(text);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(data, data);
} catch (HeadlessException ignore) {
}
}
public static void openUrlInBrowser(String url) {
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
try {
URI uri = new URI(url);
desktop.browse(uri);
} catch (IOException | URISyntaxException e) {
logger.error("Can't open url in browser: " + url, e);
}
} else {
logger.error("Can't open url in browser: non supported desktop mode");
}
}
} }

View file

@ -53,8 +53,7 @@ public class URLHandler {
try { try {
URI uri = new URI(url); URI uri = new URI(url);
desktop.browse(uri); desktop.browse(uri);
} catch (IOException | URISyntaxException ex) { } catch (IOException | URISyntaxException ignore) {
// do nothing
} }
} }
} }

View file

@ -1648,12 +1648,12 @@ public class SessionImpl implements Session {
private void handleMageException(MageException ex) { private void handleMageException(MageException ex) {
logger.fatal("Server error", ex); logger.fatal("Server error", ex);
client.showError(ex.getMessage()); client.showError("Server error: " + ex.getMessage());
} }
private void handleGameException(GameException ex) { private void handleGameException(GameException ex) {
logger.warn(ex.getMessage()); logger.warn(ex.getMessage());
client.showError(ex.getMessage()); client.showError("Game error: " + ex.getMessage());
} }
@Override @Override