diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 1100c708d60..f747ae528df 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -70,6 +70,8 @@ import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.Executors; @@ -101,7 +103,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { private static MageFrame instance; private final ConnectDialog connectDialog; - private WhatsNewDialog whatsNewDialog; // can be null private final ErrorDialog errorDialog; private static CallbackClient callbackClient; private static final Preferences PREFS = Preferences.userNodeForPackage(MageFrame.class); @@ -270,14 +271,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { SessionHandler.startSession(this); callbackClient = new CallbackClientImpl(this); connectDialog = new ConnectDialog(); - try { - whatsNewDialog = new WhatsNewDialog(); - } catch (NoClassDefFoundError e) { - // JavaFX is not supported on old MacOS with OpenJDK - // https://bugs.openjdk.java.net/browse/JDK-8202132 - LOGGER.error("JavaFX is not supported by your system. What's new page will be disabled.", e); - whatsNewDialog = null; - } desktopPane.add(connectDialog, JLayeredPane.MODAL_LAYER); errorDialog = new ErrorDialog(); errorDialog.setLocation(100, 100); @@ -359,11 +352,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { SystemUtil.toggleMacOSFullScreenMode(this); } } - - // run what's new checks (loading in background) - SwingUtilities.invokeLater(() -> { - showWhatsNewDialog(false); - }); } private void setWindowTitle() { @@ -1655,9 +1643,17 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { updateTooltipContainerSizes(); } - public void showWhatsNewDialog(boolean forceToShowPage) { - if (whatsNewDialog != null) { - whatsNewDialog.checkUpdatesAndShow(forceToShowPage); + public static void showWhatsNewDialog() { + try { + 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); } } diff --git a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java index 4e1ac407315..a5e4a0032fe 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java @@ -147,7 +147,7 @@ public class AboutDialog extends MageDialog { }//GEN-LAST:event_btnOkActionPerformed private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed - MageFrame.getInstance().showWhatsNewDialog(true); + MageFrame.showWhatsNewDialog(); }//GEN-LAST:event_btnWhatsNewActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 04b881738fb..0b05b82a61c 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -817,7 +817,7 @@ public class ConnectDialog extends MageDialog { }//GEN-LAST:event_btnCheckStatusActionPerformed private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed - MageFrame.getInstance().showWhatsNewDialog(true); + MageFrame.getInstance().showWhatsNewDialog(); }//GEN-LAST:event_btnWhatsNewActionPerformed private void btnFindMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindMainActionPerformed diff --git a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form deleted file mode 100644 index af26b1a5408..00000000000 --- a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form +++ /dev/null @@ -1,73 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java deleted file mode 100644 index 4c00fc76a2a..00000000000 --- a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java +++ /dev/null @@ -1,436 +0,0 @@ -package mage.client.dialog; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.concurrent.Worker; -import javafx.embed.swing.JFXPanel; -import javafx.scene.Scene; -import javafx.scene.web.WebEngine; -import javafx.scene.web.WebView; -import mage.client.MageFrame; -import mage.utils.MageVersion; -import org.apache.log4j.Logger; -import org.w3c.dom.events.EventListener; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Type; -import java.net.*; -import java.util.List; -import java.util.Scanner; -import java.util.concurrent.TimeUnit; - -/** - * App GUI: show latest xmage news from the web page - * - * @author JayDi85 - */ -public class WhatsNewDialog extends MageDialog { - - private static final Logger LOGGER = Logger.getLogger(WhatsNewDialog.class); - private static final MageVersion CLIENT_VERSION = new MageVersion(WhatsNewDialog.class); - - // cookies tester: http://www.html-kit.com/tools/cookietester/ - private static final String WHATS_NEW_PAGE = "https://jaydi85.github.io/xmage-web-news/news.html"; - private static final String WHATS_NEW_VERSION_PAGE = "https://jaydi85.github.io/xmage-web-news/news_version.html"; // increment version=123 to auto-shown for all users - private static final int WHATS_NEW_LOAD_TIMEOUT_SECS = 20; // timeout for page loading - - private final JFXPanel fxPanel; - private WebView webView; - private WebEngine engine; - private boolean isPageReady = false; - - private final SwingWorker backgroundWorker = new SwingWorker() { - @Override - public Void doInBackground() { - try { - int maxWait = WHATS_NEW_LOAD_TIMEOUT_SECS; - int currentWait = 0; - while (!isPageReady) { - TimeUnit.SECONDS.sleep(1); - currentWait++; - if (currentWait > maxWait) { - LOGGER.error("Can't load news page: " + WHATS_NEW_PAGE); - break; - } - } - } catch (InterruptedException e) { - LOGGER.error("Checking news was interrupted", e); - Thread.currentThread().interrupt(); - } - return null; - } - - @Override - public void done() { - SwingUtilities.invokeLater(() -> { - if (isPageReady) { - showDialog(); - } - }); - } - }; - - private final SwingWorker backgroundUpdatesWorker = new SwingWorker() { - - private String newVersion = ""; - - @Override - public Void doInBackground() { - try { - // download version - URLConnection connection = new URL(WHATS_NEW_VERSION_PAGE).openConnection(); - connection.setRequestProperty("user-agent", "xmage"); - InputStream download = connection.getInputStream(); - Scanner s = new Scanner(download).useDelimiter("\\A"); - String result = s.hasNext() ? s.next() : ""; - - // check version (default: always have new version) - if (result.startsWith("version=")) { - newVersion = result.substring("version=".length()); - } - } catch (MalformedURLException e) { - LOGGER.error("Checking updates got wrong url " + WHATS_NEW_VERSION_PAGE, e); - } catch (IOException e) { - LOGGER.error("Checking updates got error", e); - } - return null; - } - - @Override - public void done() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - String oldVersion = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEWS_PAGE_LAST_VERSION, "1"); - - boolean isHaveUpdates = newVersion.isEmpty() || !newVersion.equals(oldVersion); - if (isHaveUpdates) { - PreferencesDialog.saveValue(PreferencesDialog.KEY_NEWS_PAGE_LAST_VERSION, newVersion); - loadURL(WHATS_NEW_PAGE); - backgroundWorker.execute(); - } - } - }); - } - }; - - public WhatsNewDialog() { - initComponents(); - this.setDefaultCloseOperation(HIDE_ON_CLOSE); - - fxPanel = new JFXPanel(); - panelData.add(fxPanel); - webView = null; - engine = null; - - createWebView(); - } - - public void checkUpdatesAndShow(boolean forceToShowPage) { - // lazy loading in background - // shows it on page ready or by force - - isPageReady = false; - if (forceToShowPage) { - // direct open - loadURL(WHATS_NEW_PAGE); - if (!backgroundUpdatesWorker.isDone()) backgroundUpdatesWorker.cancel(true); - if (!backgroundWorker.isDone()) backgroundWorker.cancel(true); - - showDialog(); - } else { - // checks version -> loads on new - backgroundUpdatesWorker.execute(); - } - } - - private void showDialog() { - this.setModal(true); - this.setResizable(true); - getRootPane().setDefaultButton(buttonCancel); - this.setSize(800, 600); - - // windows settings - MageFrame.getDesktop().remove(this); - if (this.isModal()) { - MageFrame.getDesktop().add(this, JLayeredPane.MODAL_LAYER); - } else { - MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); - } - this.makeWindowCentered(); - - // Close on "ESC" - registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); - - this.setVisible(true); - } - - - /** - * Store cookies in preferences - */ - private class PersistentCookieStore implements CookieStore, Runnable { - private final CookieStore store; - - public PersistentCookieStore() { - store = new CookieManager().getCookieStore(); - - // restore data - loadFromPrefs(); - - // save data on app close - Runtime.getRuntime().addShutdownHook(new Thread(this)); - } - - private void loadFromPrefs() { - String sourceValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEWS_PAGE_COOKIES, ""); - - Gson gson = new GsonBuilder().create(); - List httpCookies; - Type type = new TypeToken>() { - }.getType(); - try { - httpCookies = gson.fromJson(sourceValue, type); - if (httpCookies != null) { - for (HttpCookie cookie : httpCookies) { - store.add(URI.create(cookie.getDomain()), cookie); - } - } - } catch (Exception e) { - LOGGER.error("Wrong news page cookies", e); - } - } - - private void saveToPrefs() { - List httpCookies = store.getCookies(); - Gson gson = new GsonBuilder().create(); - String destValue = gson.toJson(httpCookies); - PreferencesDialog.saveValue(PreferencesDialog.KEY_NEWS_PAGE_COOKIES, destValue); - } - - @Override - public void run() { - saveToPrefs(); - } - - @Override - public void add(URI uri, HttpCookie cookie) { - store.add(uri, cookie); - } - - @Override - public List get(URI uri) { - return store.get(uri); - } - - @Override - public List getCookies() { - return store.getCookies(); - } - - @Override - public List getURIs() { - return store.getURIs(); - } - - @Override - public boolean remove(URI uri, HttpCookie cookie) { - return store.remove(uri, cookie); - } - - @Override - public boolean removeAll() { - return store.removeAll(); - } - } - - private void createWebView() { - - // init web engine and events - // https://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htm - - Platform.runLater(new Runnable() { - @Override - public void run() { - - webView = new WebView(); - engine = webView.getEngine(); - engine.setUserAgent(engine.getUserAgent() + " XMage/" + CLIENT_VERSION.toString(false)); - webView.contextMenuEnabledProperty().setValue(false); - - CookieManager cookieManager = new CookieManager(new PersistentCookieStore(), CookiePolicy.ACCEPT_ALL); - CookieHandler.setDefault(cookieManager); - - // on error - engine.getLoadWorker().exceptionProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue o, Throwable old, final Throwable value) { - if (engine.getLoadWorker().getState() == Worker.State.FAILED) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - LOGGER.error("Can't load news page: " + (value != null ? value.getMessage() : "null"), value); - } - }); - } - } - }); - - // on completed - engine.getLoadWorker().stateProperty().addListener(new ChangeListener() { - @Override - public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) { - if (newState == Worker.State.SUCCEEDED) { - - // 1. replace urls with custom click processing - // all classes from org.w3c.dom - org.w3c.dom.events.EventListener listener = new EventListener() { - @Override - public void handleEvent(org.w3c.dom.events.Event ev) { - String href = ((org.w3c.dom.Element) ev.getTarget()).getAttribute("href"); - ev.preventDefault(); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - try { - URI uri = new URI(href); - desktop.browse(uri); - } catch (IOException | URISyntaxException ex) { - // do nothing - } - } - } - }); - } - }; - org.w3c.dom.Document doc = engine.getDocument(); - org.w3c.dom.NodeList listA = doc.getElementsByTagName("a"); - for (int i = 0; i < listA.getLength(); i++) { - ((org.w3c.dom.events.EventTarget) listA.item(i)).addEventListener("click", listener, false); - } - - // 2. page can be shown - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - isPageReady = true; - } - }); - } - } - }); - - fxPanel.setScene(new Scene(webView)); - } - }); - } - - public void loadURL(final String url) { - Platform.runLater(new Runnable() { - @Override - public void run() { - String tmp = toURL(url); - if (url == null) { - tmp = toURL("http://" + url); - } - engine.load(tmp); - } - }); - } - - private static String toURL(String str) { - try { - return new URL(str).toExternalForm(); - } catch (MalformedURLException exception) { - return null; - } - } - - - private void onCancel() { - this.hideDialog(); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - buttonCancel = new javax.swing.JButton(); - buttonRefresh = new javax.swing.JButton(); - panelData = new javax.swing.JPanel(); - - buttonCancel.setText("Close"); - buttonCancel.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - buttonCancelActionPerformed(evt); - } - }); - - buttonRefresh.setText("Refresh"); - buttonRefresh.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - buttonRefreshActionPerformed(evt); - } - }); - - panelData.setLayout(new java.awt.BorderLayout()); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(panelData, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(buttonRefresh, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(panelData, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(buttonCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(buttonRefresh, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed - onCancel(); - }//GEN-LAST:event_buttonCancelActionPerformed - - private void buttonRefreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonRefreshActionPerformed - loadURL(WHATS_NEW_PAGE); - }//GEN-LAST:event_buttonRefreshActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton buttonCancel; - private javax.swing.JButton buttonRefresh; - private javax.swing.JPanel panelData; - // End of variables declaration//GEN-END:variables -} diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 6b53a9097ac..8282e14b8e2 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1760,7 +1760,7 @@ public class TablesPanel extends javax.swing.JPanel { }//GEN-LAST:event_btnStateFinishedActionPerformed private void buttonWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonWhatsNewActionPerformed - MageFrame.getInstance().showWhatsNewDialog(true); + MageFrame.showWhatsNewDialog(); }//GEN-LAST:event_buttonWhatsNewActionPerformed private void btnQuickStart2PlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartDuelActionPerformed