/*
* RapidMiner
*
* Copyright (C) 2001-2014 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapid_i.deployment.update.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import com.rapid_i.deployment.update.client.listmodels.AbstractPackageListModel;
import com.rapid_i.deployment.update.client.listmodels.BookmarksPackageListModel;
import com.rapid_i.deployment.update.client.listmodels.LicencedPackageListModel;
import com.rapidminer.RapidMiner;
import com.rapidminer.deployment.client.wsimport.PackageDescriptor;
import com.rapidminer.gui.tools.ExtendedHTMLJEditorPane;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.VersionNumber;
import com.rapidminer.gui.tools.components.LinkButton;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
/**
*
* @author Dominik Halfkann
*
*/
public class UpdatePanelTab extends JPanel {
private JPanel extensionButtonPane;
private static final long serialVersionUID = 1L;
private static final int LIST_WIDTH = 330;
protected UpdatePackagesModel updateModel;
protected AbstractPackageListModel listModel;
protected UpdateServerAccount usAccount;
private ExtendedHTMLJEditorPane displayPane;
private final SelectForInstallationButton installButton;
private LinkButton loginForInstallHint;
private LinkButton extensionHomepageLink;
private PackageDescriptor lastSelected = null;
private JList packageList;
private class SelectForInstallationButton extends JToggleButton implements Observer {
private boolean purchaseFirst = false;
private static final long serialVersionUID = 1L;
public SelectForInstallationButton(Action a) {
super(a);
}
public void setPurchaseFirst(boolean purchaseFirst) {
this.purchaseFirst = purchaseFirst;
}
public boolean getPurchaseFirst() {
return purchaseFirst;
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof UpdatePackagesModel) {
UpdatePackagesModel currentModel = (UpdatePackagesModel) o;
if (arg != null && arg instanceof PackageDescriptor) {
PackageDescriptor desc = (PackageDescriptor) arg;
Object selectedObject = getPackageList().getSelectedValue();
if (selectedObject instanceof PackageDescriptor) {
PackageDescriptor selectedDescriptor = (PackageDescriptor) selectedObject;
if (desc.getPackageId().equals(selectedDescriptor.getPackageId())) {
this.setSelected(currentModel.isSelectedForInstallation(desc));
if (this.isSelected()) {
this.setIcon(SwingTools.createIcon("16/checkbox.png"));
} else {
this.setIcon(SwingTools.createIcon("16/checkbox_unchecked.png"));
}
}
}
listModel.updateView(desc);
}
}
}
}
public UpdatePanelTab(final UpdatePackagesModel updateModel, AbstractPackageListModel model, final UpdateServerAccount usAccount) {
super(new GridBagLayout());
this.updateModel = updateModel;
this.listModel = model;
this.usAccount = usAccount;
this.usAccount.addObserver(new Observer() {
@Override
public void update(Observable o, Object arg) {
updateDisplayPane();
}
});
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.gridheight = GridBagConstraints.REMAINDER;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.weighty = 1;
c.insets = new Insets(0, 0, 0, 0);
installButton = new SelectForInstallationButton(new ResourceAction(true, "update.select") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
markForInstallation((PackageDescriptor) getPackageList().getSelectedValue(), true, true);
}
});
installButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!installButton.getPurchaseFirst()) {
if (installButton.isSelected()) {
installButton.setIcon(SwingTools.createIcon("16/checkbox.png"));
} else {
installButton.setIcon(SwingTools.createIcon("16/checkbox_unchecked.png"));
}
}
}
});
installButton.setEnabled(false);
updateModel.addObserver(installButton);
displayPane = new ExtendedHTMLJEditorPane("text/html", "");
displayPane.installDefaultStylesheet();
((HTMLEditorKit) displayPane.getEditorKit()).getStyleSheet().addRule("a {text-decoration:underline; color:blue;}");
setDefaultDescription();
displayPane.setEditable(false);
displayPane.addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType())) {
try {
Desktop.getDesktop().browse(e.getURL().toURI());
} catch (Exception e1) {
SwingTools.showVerySimpleErrorMessage("cannot_open_browser");
}
}
}
});
loginForInstallHint = new LinkButton(new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
usAccount.login(updateModel);
}
});
extensionHomepageLink = new LinkButton(new AbstractAction() {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
PackageDescriptor selectedDescriptor = (PackageDescriptor) getPackageList().getSelectedValue();
if (selectedDescriptor != null) {
String url = updateModel.getExtensionURL(selectedDescriptor);
// open link
Desktop desktop = Desktop.getDesktop();
if (desktop.isSupported(Desktop.Action.BROWSE)) {
URI uri;
try {
uri = new java.net.URI(url);
desktop.browse(uri);
} catch (URISyntaxException e1) {
LogService.getRoot().log(Level.WARNING, "Malformed extension URI.");
return;
} catch (IOException e2) {
LogService.getRoot().log(Level.WARNING, "Error opening extension URI in the default browser.");
return;
}
}
}
}
});
packageList = createUpdateList();
JScrollPane updateListScrollPane = new ExtendedJScrollPane(packageList);
updateListScrollPane.setMinimumSize(new Dimension(LIST_WIDTH, 100));
updateListScrollPane.setPreferredSize(new Dimension(LIST_WIDTH, 100));
updateListScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
Component topPanel = makeTopPanel();
Component bottomPanel = makeBottomPanel();
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.add(updateListScrollPane, BorderLayout.CENTER);
if (topPanel != null) {
leftPanel.add(topPanel, BorderLayout.NORTH);
add(leftPanel, c);
}
if (bottomPanel != null) {
leftPanel.add(bottomPanel, BorderLayout.SOUTH);
add(leftPanel, c);
}
c.gridx = 1;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
c.insets = new Insets(0, 0, 0, 0);
JScrollPane jScrollPane = new ExtendedJScrollPane(displayPane);
jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
JPanel descriptionPanel = new JPanel(new BorderLayout());
descriptionPanel.add(jScrollPane, BorderLayout.CENTER);
extensionButtonPane = new JPanel(new BorderLayout());
extensionButtonPane.setMinimumSize(new Dimension(100, 35));
extensionButtonPane.setPreferredSize(new Dimension(100, 35));
JPanel extensionButtonPaneLeft = new JPanel(new FlowLayout(FlowLayout.LEFT));
extensionButtonPaneLeft.add(installButton);
extensionButtonPaneLeft.add(loginForInstallHint);
extensionButtonPane.add(extensionButtonPaneLeft, BorderLayout.WEST);
JPanel extensionButtonPaneRight = new JPanel(new FlowLayout(FlowLayout.RIGHT));
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.extension_homepage.label"));
extensionButtonPaneRight.add(extensionHomepageLink);
extensionButtonPane.add(extensionButtonPaneRight, BorderLayout.CENTER);
for (Component component : extensionButtonPane.getComponents()) {
component.setVisible(false);
}
extensionButtonPane.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.LIGHT_GRAY));
descriptionPanel.setBorder(BorderFactory.createMatteBorder(0, 1, 0, 0, Color.LIGHT_GRAY));
descriptionPanel.add(extensionButtonPane, BorderLayout.SOUTH);
add(descriptionPanel, c);
}
private void showProductPage(PackageDescriptor desc) {
try {
String url = UpdateManager.getBaseUrl() + "/faces/product_details.xhtml?productId=" + desc.getPackageId();
Desktop.getDesktop().browse(new URI(url));
} catch (Exception e1) {
SwingTools.showVerySimpleErrorMessage("cannot_open_browser");
}
}
protected Component makeTopPanel() {
return null;
}
protected Component makeBottomPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.setMinimumSize(new Dimension(100, 35));
panel.setPreferredSize(new Dimension(100, 35));
panel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.LIGHT_GRAY));
return panel;
}
private JList createUpdateList() {
JList updateList = new JList(listModel);
updateList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
updateDisplayPane();
}
}
});
updateList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
markForInstallation((PackageDescriptor) getPackageList().getSelectedValue(), true, true);
}
}
});
updateList.setCellRenderer(new UpdateListCellRenderer(updateModel));
return updateList;
}
/**
*
* @param selectedDescriptor
* @param loginForRestricted shows login dialog if restricted package found but not logged in
* @param showProductPage shows product page if not yet purchased package selected
*/
protected void markForInstallation(final PackageDescriptor selectedDescriptor, final boolean loginForRestricted, final boolean showProductPage) {
// check if extension is installed and up to date
if (updateModel.isUpToDate(selectedDescriptor)) {
return;
}
// check if selected descriptor is restricted
if (selectedDescriptor.isRestricted()) {
if (usAccount.isLoggedIn()) {
if (updateModel.isPurchased(selectedDescriptor)) {
UpdatePanelTab.this.updateModel.toggleSelectionForInstallation(selectedDescriptor);
getModel().updateView(selectedDescriptor);
} else {
if (showProductPage) {
showProductPage(selectedDescriptor);
}
}
} else {
if (loginForRestricted) {
usAccount.login(updateModel, false, new Runnable() {
@Override
public void run() {
if (usAccount.isLoggedIn()) {
if (updateModel.isPurchased(selectedDescriptor)) {
UpdatePanelTab.this.updateModel.toggleSelectionForInstallation(selectedDescriptor);
getModel().updateView(selectedDescriptor);
} else {
if (showProductPage) {
showProductPage(selectedDescriptor);
}
}
}
}
}, null);
}
}
} else {
UpdatePanelTab.this.updateModel.toggleSelectionForInstallation(selectedDescriptor);
getModel().updateView(selectedDescriptor);
}
}
protected JList getPackageList() {
return packageList;
}
public void selectNotify() {
if (listModel instanceof BookmarksPackageListModel || listModel instanceof LicencedPackageListModel) {
usAccount.login(updateModel);
}
listModel.update();
}
public AbstractPackageListModel getModel() {
return listModel;
}
private void setDefaultDescription() {
new Thread("Load Default Description") {
@Override
public void run() {
try {
displayPane.setPage("http://rapid-i.com/marketplace_news");
} catch (Exception e) {
displayPane.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.dialog.update_welcome_message.text", UpdateManager.getBaseUrl()));
}
}
}.start();
}
private void updateDisplayPane() {
Object selectedValue = packageList.getSelectedValue();
PackageDescriptor desc = null;
if (selectedValue instanceof PackageDescriptor) {
desc = (PackageDescriptor) selectedValue;
lastSelected = desc;
} else {
packageList.clearSelection();
if (lastSelected != null) {
desc = lastSelected;
}
}
if (desc != null) {
for (Component component : extensionButtonPane.getComponents()) {
component.setVisible(true);
}
installButton.setVisible(false);
extensionButtonPane.setVisible(true);
StyleSheet css = new StyleSheet();//.makeDefaultStylesheet();
css.addRule("a {text-decoration:underline; color:blue;}");
css.addRule("h1 {font-size: 14px;}");
css.addRule("h2 {font-size: 11px;font-weight:bold;}");
css.addRule("div, p, hr { margin-bottom:8px }");
css.addRule("div.changes-section{padding-left:10px;font-size:9px;color:#444444;}");
css.addRule(".changes-header-version {margin-top:10px;margin-bottom:5px;color:#111111;}");
css.addRule("ul {padding-left:10px;}");
css.addRule("ul li {margin-left:0px;padding-left:0px;}");
HTMLDocument doc = new HTMLDocument(css);
displayPane.setDocument(doc);
displayPane.setText(updateModel.toString(desc, listModel.getChanges(desc.getPackageId())));
displayPane.setCaretPosition(0);
installButton.setSelected(updateModel.isSelectedForInstallation(desc));
boolean isInstalled = false;
boolean isUpToDate = false;
boolean isRapidMiner = "STAND_ALONE".equals(desc.getPackageTypeName());
if (isRapidMiner) {
isUpToDate = RapidMiner.getVersion().isAtLeast(new VersionNumber(desc.getVersion()));
isInstalled = true;
} else {
//updatesExist = !RapidMiner.getVersion().isAtLeast(new VersionNumber(getService().getLatestVersion("rapidminer", TARGET_PLATFORM)));
ManagedExtension ext = ManagedExtension.get(desc.getPackageId());
if (ext != null) {
isInstalled = true;
String installed = ext.getLatestInstalledVersion();
if (installed != null) {
boolean upToDate = installed.compareTo(desc.getVersion()) >= 0;
if (upToDate) {
isUpToDate = true;
} else {
isUpToDate = false;
}
}
}
}
if (desc.isRestricted() && !isInstalled) {
if (!usAccount.isLoggedIn()) {
// restricted, uninstalled, not logged in
installButton.setVisible(false);
loginForInstallHint.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.need_to_log_in.label"));
} else if (updateModel.isPurchased(desc)) {
// restricted, purchased but not installed yet
installButton.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.install.select.label"));
installButton.getAction().putValue(Action.MNEMONIC_KEY, (int) I18N.getMessage(I18N.getGUIBundle(), "gui.action.install.select.mne").toUpperCase().charAt(0));
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.extension_homepage.label"));
installButton.setPurchaseFirst(false);
installButton.setVisible(true);
installButton.setEnabled(true);
loginForInstallHint.setText("");
if (updateModel.isSelectedForInstallation(desc)) {
installButton.setIcon(SwingTools.createIcon("16/checkbox.png"));
} else {
installButton.setIcon(SwingTools.createIcon("16/checkbox_unchecked.png"));
}
} else {
// restricted, not purchased
installButton.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.purchase.label"));
installButton.setIcon(SwingTools.createIcon("16/shopping_cart_empty.png"));
installButton.getAction().putValue(Action.MNEMONIC_KEY, (int) I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.purchase.mne").toUpperCase().charAt(0));
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.extension_homepage.label"));
installButton.setVisible(true);
loginForInstallHint.setText("");
installButton.setPurchaseFirst(true);
}
}
else {
if (isInstalled) {
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.extension_homepage.label"));
if (!isUpToDate) {
// not restricted / restricted and installed but not updated
installButton.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.select.label"));
installButton.getAction().putValue(Action.MNEMONIC_KEY, (int) I18N.getMessage(I18N.getGUIBundle(), "gui.action.update.select.mne").toUpperCase().charAt(0));
installButton.setPurchaseFirst(false);
installButton.setEnabled(true);
installButton.setVisible(true);
loginForInstallHint.setText("");
if (updateModel.isSelectedForInstallation(desc)) {
installButton.setIcon(SwingTools.createIcon("16/checkbox.png"));
} else {
installButton.setIcon(SwingTools.createIcon("16/checkbox_unchecked.png"));
}
} else {
// Installed and updated. So showing nothing.
installButton.setVisible(false);
loginForInstallHint.setText("");
}
} else {
// not restricted / restricted not installed
installButton.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.action.install.select.label"));
installButton.getAction().putValue(Action.MNEMONIC_KEY, (int) I18N.getMessage(I18N.getGUIBundle(), "gui.action.install.select.mne").toUpperCase().charAt(0));
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.extension_homepage.label"));
installButton.setPurchaseFirst(false);
installButton.setVisible(true);
installButton.setEnabled(true);
loginForInstallHint.setText("");
if (updateModel.isSelectedForInstallation(desc)) {
installButton.setIcon(SwingTools.createIcon("16/checkbox.png"));
} else {
installButton.setIcon(SwingTools.createIcon("16/checkbox_unchecked.png"));
}
}
}
if (isRapidMiner) {
extensionHomepageLink.setText(I18N.getMessage(I18N.getGUIBundle(), "gui.label.update.product_homepage.label"));
}
}
}
@Override
public void removeNotify() {
super.removeNotify();
usAccount.deleteObservers();
updateModel.deleteObservers();
}
}