/*
* This file is part of ADDIS (Aggregate Data Drug Information System).
* ADDIS is distributed from http://drugis.org/.
* Copyright © 2009 Gert van Valkenhoef, Tommi Tervonen.
* Copyright © 2010 Gert van Valkenhoef, Tommi Tervonen, Tijs Zwinkels,
* Maarten Jacobs, Hanno Koeslag, Florin Schimbinschi, Ahmad Kamal, Daniel
* Reid.
* Copyright © 2011 Gert van Valkenhoef, Ahmad Kamal, Daniel Reid, Florin
* Schimbinschi.
* Copyright © 2012 Gert van Valkenhoef, Daniel Reid, Joël Kuiper, Wouter
* Reckman.
* Copyright © 2013 Gert van Valkenhoef, Joël Kuiper.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.drugis.addis.gui.builder;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
import org.drugis.addis.entities.Study;
import org.drugis.addis.entities.analysis.NetworkMetaAnalysis;
import org.drugis.addis.entities.treatment.Category;
import org.drugis.addis.entities.treatment.TreatmentDefinition;
import org.drugis.addis.gui.AddisWindow;
import org.drugis.addis.gui.AuxComponentFactory;
import org.drugis.addis.gui.CategoryKnowledgeFactory;
import org.drugis.addis.gui.StudyGraph;
import org.drugis.addis.gui.components.AddisTabbedPane;
import org.drugis.addis.gui.components.ScrollableJPanel;
import org.drugis.addis.presentation.NetworkMetaAnalysisPresentation;
import org.drugis.common.gui.ImageExporter;
import org.drugis.common.gui.LayoutUtil;
import org.drugis.common.gui.ViewBuilder;
import org.drugis.common.gui.table.EnhancedTable;
import org.drugis.common.gui.table.TableCopyHandler;
import org.drugis.common.gui.table.TablePanel;
import org.drugis.common.threading.Task;
import org.drugis.common.threading.ThreadHandler;
import org.drugis.mtc.gui.Help;
import org.drugis.mtc.gui.MainWindow;
import org.drugis.mtc.gui.results.NetworkRelativeEffectTableCellRenderer;
import org.drugis.mtc.gui.results.ResultsComponentFactory;
import org.drugis.mtc.gui.results.SimulationComponentFactory;
import org.drugis.mtc.gui.results.SummaryCellRenderer;
import org.drugis.mtc.model.Treatment;
import org.drugis.mtc.parameterization.BasicParameter;
import org.drugis.mtc.presentation.ConsistencyWrapper;
import org.drugis.mtc.presentation.InconsistencyWrapper;
import org.drugis.mtc.presentation.MTCModelWrapper;
import org.drugis.mtc.presentation.NodeSplitWrapper;
import org.drugis.mtc.presentation.results.NetworkInconsistencyFactorsTableModel;
import org.drugis.mtc.presentation.results.NetworkRelativeEffectTableModel;
import org.drugis.mtc.presentation.results.NetworkVarianceTableModel;
import org.drugis.mtc.presentation.results.NodeSplitResultsTableModel;
import org.drugis.mtc.summary.NodeSplitPValueSummary;
import org.drugis.mtc.summary.QuantileSummary;
import org.drugis.mtc.summary.Summary;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import com.jgoodies.forms.builder.ButtonBarBuilder2;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
public class NetworkMetaAnalysisView extends AbstractMetaAnalysisView<NetworkMetaAnalysisPresentation>
implements ViewBuilder {
private static final String MEMORY_USAGE_TAB_TITLE = "Memory Usage";
private static final String NODE_SPLIT_TAB_TITLE = "Node Split";
private static final String INCONSISTENCY_TAB_TITLE = "Inconsistency";
private static final String CONSISTENCY_TAB_TITLE = "Consistency";
private static final String OVERVIEW_TAB_TITLE = "Overview";
private final AddisWindow d_mainWindow;
public NetworkMetaAnalysisView(final NetworkMetaAnalysisPresentation model, final AddisWindow mainWindow) {
super(model, mainWindow);
d_mainWindow = mainWindow;
}
public JComponent buildOverviewTab() {
final FormLayout layout = new FormLayout(
"fill:0:grow",
"p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p" +
", 3dlu, p"); // Memory usage part
final PanelBuilder builder = new PanelBuilder(layout, new ScrollableJPanel());
builder.setDefaultDialogBorder();
final CellConstraints cc = new CellConstraints();
builder.addSeparator(CategoryKnowledgeFactory.getCategoryKnowledge(NetworkMetaAnalysis.class).getSingularCapitalized(), cc.xy(1, 1));
builder.add(buildPropertiesPart(), cc.xy(1, 3));
builder.addSeparator(CategoryKnowledgeFactory.getCategoryKnowledge(Study.class).getPlural(), cc.xy(1, 5));
builder.add(buildStudiesPart(), cc.xy(1, 7));
builder.addSeparator("Evidence network", cc.xy(1, 9));
builder.add(buildStudyGraphPart(), cc.xy(1, 11));
return builder.getPanel();
}
private JComponent buildMemoryUsageTab() {
final CellConstraints cc = new CellConstraints();
final FormLayout header = new FormLayout("fill:0:grow", "p");
final PanelBuilder builderheader = new PanelBuilder(header);
final FormLayout layout = new FormLayout(
"left:0:grow, 3dlu, left:pref, 3dlu, pref, 3dlu, pref, 3dlu, pref",
"p, 3dlu, p, 3dlu, p, 3dlu, p"
);
final int colSpan = layout.getColumnCount();
final PanelBuilder builder = new PanelBuilder(layout);
builder.setDefaultDialogBorder();
int row = 1;
builder.addSeparator("Memory usage", cc.xyw(1, row, colSpan));
row += 2;
builderheader.add(AuxComponentFactory.createTextPane(Help.getHelpText("memoryUsage")), cc.xy(1,1));
builder.add(builderheader.getPanel(), cc.xyw(1, row, colSpan));
row += 2;
row = ResultsComponentFactory.buildMemoryUsage(d_pm.getConsistencyModel(), "Consistency model", builder, layout, row, d_mainWindow);
row = ResultsComponentFactory.buildMemoryUsage(d_pm.getInconsistencyModel(), "Inconsistency model", builder, layout, row, d_mainWindow);
builder.addSeparator("", cc.xyw(1, row, 3));
row += 2;
for(final BasicParameter p : d_pm.getSplitParameters()) {
row = ResultsComponentFactory.buildMemoryUsage(d_pm.getNodeSplitModel(p), "<html>Node Split model:<br /> Parameter " + p.getName() + "</html>", builder, layout, row, d_mainWindow);
LayoutUtil.addRow(layout);
builder.addSeparator("", cc.xyw(1, row, 3));
row += 2;
}
return builder.getPanel();
}
private JComponent buildInconsistencyTab() {
final FormLayout layout = new FormLayout("pref, 3dlu, pref, 3dlu, fill:0:grow",
"p, 3dlu, p, 3dlu, p, 5dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p");
final PanelBuilder builder = new PanelBuilder(layout, new ScrollableJPanel());
builder.setDefaultDialogBorder();
final CellConstraints cc = new CellConstraints();
int row = 1;
final int colSpan = 5;
builder.addSeparator("Results - network inconsistency model", cc.xyw(1, row, colSpan));
row += 2;
final InconsistencyWrapper<TreatmentDefinition> inconsistencyModel = d_pm.getInconsistencyModel();
final JPanel simulationControls = SimulationComponentFactory.createSimulationControls(d_pm.getWrappedModel(inconsistencyModel), d_mainWindow, false, AuxComponentFactory.COLOR_NOTE, d_mainWindow.getReloadRightPanelAction(INCONSISTENCY_TAB_TITLE));
builder.add(simulationControls, cc.xyw(3, row, 3));
row += 2;
final JComponent inconsistencyNote = AuxComponentFactory.createTextPane(Help.getHelpText("inconsistency"));
builder.add(inconsistencyNote, cc.xyw(1, row, colSpan));
row += 2;
final TablePanel relativeEffectsTablePanel = createNetworkTablePanel(inconsistencyModel);
builder.addSeparator("Network Meta-Analysis (Inconsistency Model)", cc.xyw(1, row, colSpan));
row += 2;
builder.add(relativeEffectsTablePanel, cc.xyw(1, row, colSpan));
row += 2;
final NetworkInconsistencyFactorsTableModel inconsistencyFactorsTableModel = new NetworkInconsistencyFactorsTableModel(
d_pm.getInconsistencyModel(),
d_pm.getWrappedModel(d_pm.getInconsistencyModel()).isModelConstructed(),
true);
final EnhancedTable table = new EnhancedTable(inconsistencyFactorsTableModel, 300);
table.setDefaultRenderer(Summary.class, new SummaryCellRenderer(false));
final TablePanel inconsistencyFactorsTablePanel = new TablePanel(table);
d_pm.getWrappedModel(inconsistencyModel).isModelConstructed().addValueChangeListener(new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent event) {
if (event.getNewValue().equals(true)) {
final Runnable r = new Runnable() {
public void run() {
inconsistencyFactorsTablePanel.doLayout();
}
};
SwingUtilities.invokeLater(r);
}
}
});
builder.addSeparator("Inconsistency Factors", cc.xyw(1, row, colSpan));
row += 2;
builder.add(inconsistencyFactorsTablePanel, cc.xyw(1, row, colSpan));
row += 2;
final NetworkVarianceTableModel varianceTableModel = new NetworkVarianceTableModel(inconsistencyModel);
final EnhancedTable varianceTable = new EnhancedTable(varianceTableModel, 300);
varianceTable.setDefaultRenderer(QuantileSummary.class, new SummaryCellRenderer());
final TablePanel varianceTablePanel = new TablePanel(varianceTable);
builder.addSeparator("Variance Calculation", cc.xyw(1, row, colSpan));
row += 2;
builder.add(varianceTablePanel, cc.xyw(1, row, colSpan));
row += 2;
return builder.getPanel();
}
private JComponent buildConsistencyTab() {
final FormLayout layout = new FormLayout("pref, 3dlu, fill:0:grow",
"p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p");
final PanelBuilder builder = new PanelBuilder(layout, new ScrollableJPanel());
builder.setDefaultDialogBorder();
final CellConstraints cc = new CellConstraints();
int row = 1;
final int colSpan = 3;
builder.addSeparator("Results - network consistency model", cc.xyw(1, row, colSpan));
row += 2;
final ConsistencyWrapper<TreatmentDefinition> consistencyModel = d_pm.getConsistencyModel();
final JPanel simulationControls = SimulationComponentFactory.createSimulationControls(
d_pm.getWrappedModel(consistencyModel), d_mainWindow, false,
AuxComponentFactory.COLOR_NOTE, d_mainWindow.getReloadRightPanelAction(CONSISTENCY_TAB_TITLE));
builder.add(simulationControls, cc.xyw(1, row, 3));
row += 2;
final JComponent consistencyNote = AuxComponentFactory.createTextPane(Help.getHelpText("consistency"));
builder.add(consistencyNote, cc.xyw(1, row, colSpan));
final TablePanel relativeEffectsTablePanel = createNetworkTablePanel(consistencyModel);
row += 2;
builder.addSeparator("Network Meta-Analysis (Consistency Model)", cc.xyw(1, row, colSpan));
row += 2;
builder.add(relativeEffectsTablePanel, cc.xyw(1, row, colSpan));
row += 2;
builder.add(createRankProbChart(), cc.xyw(1, row, colSpan));
row += 2;
builder.add(createRankProbTable(), cc.xyw(1, row, colSpan));
row += 2;
builder.addSeparator("Variance Parameters", cc.xyw(1, row, colSpan));
row += 2;
final EnhancedTable varianceTable = new EnhancedTable(new NetworkVarianceTableModel(consistencyModel), 300);
varianceTable.setDefaultRenderer(QuantileSummary.class, new SummaryCellRenderer());
builder.add(new TablePanel(varianceTable), cc.xyw(1, row, colSpan));
return builder.getPanel();
}
private JComponent buildNodeSplitTab() {
final FormLayout layout = new FormLayout(
"pref, 3dlu, fill:0:grow",
"p, 3dlu, p, 3dlu, p, 3dlu, p");
final CellConstraints cc = new CellConstraints();
final PanelBuilder builder = new PanelBuilder(layout, new ScrollableJPanel());
builder.setDefaultDialogBorder();
final int colSpan = 3;
int row = 1;
builder.addSeparator("Results - node-splitting analysis of inconsistency", cc.xyw(1, row, colSpan));
row += 2;
builder.add(AuxComponentFactory.createTextPane(Help.getHelpText("nodeSplit")),
cc.xyw(1, row, colSpan));
row += 2;
builder.add(buildNodeSplitControls(), cc.xyw(1, row, colSpan));
row += 2;
builder.add(buildNodeSplitResultsTable(), cc.xyw(1, row, colSpan));
for (final BasicParameter p : d_pm.getSplitParameters()) {
LayoutUtil.addRow(layout);
row += 2;
final NodeSplitWrapper<TreatmentDefinition> model = d_pm.getNodeSplitModel(p);
final JPanel simulationControls = SimulationComponentFactory.createSimulationControls(d_pm.getWrappedModel(model), d_mainWindow, true, AuxComponentFactory.COLOR_NOTE, d_mainWindow.getReloadRightPanelAction(NODE_SPLIT_TAB_TITLE));
builder.add(simulationControls, cc.xyw(1, row, 3));
LayoutUtil.addRow(layout);
row += 2;
builder.add(ResultsComponentFactory.buildNodeSplitDensityChart(p, d_pm.getNodeSplitModel(p), d_pm.getConsistencyModel()), cc.xyw(1, row, colSpan));
LayoutUtil.addRow(layout);
row += 2;
}
return builder.getPanel();
}
private JComponent buildNodeSplitControls() {
final FormLayout layout = new FormLayout(
"fill:0:grow, 3dlu, pref",
"p");
final CellConstraints cc = new CellConstraints();
final PanelBuilder panelBuilder = new PanelBuilder(layout);
final JButton resetAll = new JButton(MainWindow.IMAGELOADER.getIcon(org.drugis.mtc.gui.FileNames.ICON_REDO));
resetAll.setToolTipText("Reset all simulations");
resetAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
d_pm.getBean().resetNodeSplitModels();
if(d_mainWindow instanceof AddisWindow) {
d_mainWindow.reloadRightPanel(NODE_SPLIT_TAB_TITLE);
}
}
});
final JButton runAll = new JButton(MainWindow.IMAGELOADER.getIcon(org.drugis.mtc.gui.FileNames.ICON_RUN));
runAll.setText("Run all node-split models");
runAll.setToolTipText("Run all simulations");
final List<Task> tasks = new ArrayList<Task>();
for (final BasicParameter p : d_pm.getSplitParameters()) {
final NodeSplitWrapper<TreatmentDefinition> wrapper = d_pm.getNodeSplitModel(p);
if (!wrapper.isSaved()) {
tasks.add(wrapper.getModel().getActivityTask());
}
}
runAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
ThreadHandler.getInstance().scheduleTasks(tasks);
}
});
panelBuilder.add(resetAll, cc.xy(3, 1));
panelBuilder.add(runAll, cc.xy(1, 1));
return panelBuilder.getPanel();
}
private JComponent buildNodeSplitResultsTable() {
final NodeSplitResultsTableModel tableModel = d_pm.createNodeSplitResultsTableModel();
final EnhancedTable table = EnhancedTable.createWithSorter(tableModel);
table.setDefaultRenderer(QuantileSummary.class, new SummaryCellRenderer());
table.setDefaultRenderer(NodeSplitPValueSummary.class, new SummaryCellRenderer());
table.autoSizeColumns();
return new TablePanel(table);
}
@Override
public JComponent buildPanel() {
final JTabbedPane tabbedPane = new AddisTabbedPane();
tabbedPane.addTab(OVERVIEW_TAB_TITLE, buildOverviewTab());
tabbedPane.addTab(CONSISTENCY_TAB_TITLE, buildConsistencyTab());
tabbedPane.addTab(INCONSISTENCY_TAB_TITLE, buildInconsistencyTab());
tabbedPane.addTab(NODE_SPLIT_TAB_TITLE, buildNodeSplitTab());
tabbedPane.addTab(MEMORY_USAGE_TAB_TITLE, buildMemoryUsageTab());
return tabbedPane;
}
private JComponent createRankProbChart() {
final CategoryDataset dataset = d_pm.getRankProbabilityDataset();
final JFreeChart chart = ChartFactory.createBarChart("Rank Probability", "Treatment", "Probability",
dataset, PlotOrientation.VERTICAL, true, true, false);
chart.addSubtitle(new org.jfree.chart.title.ShortTextTitle(d_pm.getRankProbabilityRankChartNote()));
final FormLayout layout = new FormLayout(
"fill:0:grow",
"p, 3dlu, p");
final PanelBuilder builder = new PanelBuilder(layout);
final CellConstraints cc = new CellConstraints();
final ChartPanel chartPanel = new ChartPanel(chart);
chartPanel.setSize(chartPanel.getPreferredSize().width, chartPanel.getPreferredSize().height+1);
chartPanel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
builder.add(chartPanel, cc.xy(1, 1));
final ButtonBarBuilder2 bbuilder = new ButtonBarBuilder2();
bbuilder.addButton(createSaveImageButton(chart));
builder.add(bbuilder.getPanel(), cc.xy(1, 3));
return builder.getPanel();
}
private JComponent createRankProbTable() {
final EnhancedTable table = EnhancedTable.createBare(d_pm.getRankProbabilityTableModel());
table.setDefaultRenderer(Double.class, new SummaryCellRenderer());
table.autoSizeColumns();
return new TablePanel(table);
}
private JButton createSaveImageButton(final JFreeChart chart) {
final JButton button = new JButton("Save Image");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent arg0) {
ImageExporter.writeImage(d_mainWindow, chart, 600, 400);
}
});
return button;
}
private JButton createSaveDataButton() {
final JButton button = new JButton("Open in GeMTC");
button.setToolTipText("Open for analysis in drugis.org GeMTC");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(final ActionEvent arg0) {
openGeMTC();
}
});
return button;
}
private void openGeMTC() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final MainWindow geMTC = new MainWindow(d_pm.getNetwork());
geMTC.setVisible(true);
}
});
}
@SuppressWarnings("serial")
public JComponent buildStudyGraphPart() {
final FormLayout layout = new FormLayout(
"pref",
"p, 3dlu, p");
final PanelBuilder builder = new PanelBuilder(layout);
final CellConstraints cc = new CellConstraints();
final StudyGraph panel = new StudyGraph(d_pm.getStudyGraphModel());
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.layoutGraph();
builder.add(panel, cc.xy(1, 1));
final JButton saveBtn = new JButton("Save Image");
saveBtn.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(final ActionEvent e) {
panel.saveImage(d_mainWindow);
}
});
final ButtonBarBuilder2 bbuilder = new ButtonBarBuilder2();
bbuilder.addButton(saveBtn);
bbuilder.addButton(createSaveDataButton());
builder.add(bbuilder.getPanel(), cc.xy(1, 3));
return builder.getPanel();
}
/**
* Make table of results (Cipriani et al., Lancet(2009), fig. 3, pp752).
* @param mtc Model for which to display results.
* @return A TablePanel
*/
private TablePanel createNetworkTablePanel(final MTCModelWrapper<TreatmentDefinition> mtc) {
NetworkRelativeEffectTableModel tableModel = NetworkRelativeEffectTableModel.build(d_pm.getAlternatives(), mtc);
final JTable table = new JTable(tableModel);
final NetworkRelativeEffectTableCellRenderer renderer = new NetworkRelativeEffectTableCellRenderer(!d_pm.isContinuous());
table.setDefaultRenderer(Object.class, renderer);
table.setTableHeader(null);
table.addMouseListener(treatmentCategorizationListener(renderer));
setColumnWidths(table);
TableCopyHandler.registerCopyAction(table);
return new TablePanel(table);
}
private MouseAdapter treatmentCategorizationListener(final NetworkRelativeEffectTableCellRenderer renderer) {
return new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() > 1) {
JTable table = (JTable)e.getComponent();
int row = table.convertRowIndexToModel(table.rowAtPoint(e.getPoint()));
int col = table.convertColumnIndexToModel(table.columnAtPoint(e.getPoint()));
if(col == row) {
Treatment treatment = renderer.getTreatment(table.getModel(), col);
TreatmentDefinition treatmentDefinition = d_pm.getTreatmentDefinition(treatment);
Category category = treatmentDefinition.getContents().first();
if(category != null && !category.isTrivial()) {
d_mainWindow.leftTreeFocus(category.getCategorization());
}
}
}
}
};
}
private void setColumnWidths(final JTable table) {
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (final TableColumn c : Collections.list(table.getColumnModel().getColumns())) {
c.setMinWidth(170);
c.setPreferredWidth(170);
}
}
}