/* * 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.wizard; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import javax.swing.BoxLayout; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import org.drugis.addis.entities.Entity; import org.drugis.addis.entities.OutcomeMeasure; import org.drugis.addis.entities.analysis.BenefitRiskAnalysis.AnalysisType; import org.drugis.addis.entities.analysis.MetaAnalysis; import org.drugis.addis.gui.AddisWindow; import org.drugis.addis.gui.AuxComponentFactory; import org.drugis.addis.gui.components.AddisScrollPane; import org.drugis.addis.presentation.AbstractBenefitRiskPresentation; import org.drugis.addis.presentation.ValueHolder; import org.drugis.addis.presentation.wizard.BenefitRiskWizardPM; import org.drugis.addis.presentation.wizard.BenefitRiskWizardPM.BRAType; import org.drugis.addis.presentation.wizard.CriteriaAndAlternativesPresentation; import org.drugis.addis.presentation.wizard.MetaCriteriaAndAlternativesPresentation; import org.drugis.addis.presentation.wizard.StudyCriteriaAndAlternativesPresentation; import org.drugis.common.gui.LayoutUtil; import org.drugis.common.gui.TextComponentFactory; import org.pietschy.wizard.InvalidStateException; import org.pietschy.wizard.PanelWizardStep; import org.pietschy.wizard.Wizard; import org.pietschy.wizard.WizardModel; import org.pietschy.wizard.models.Condition; import org.pietschy.wizard.models.DynamicModel; import com.jgoodies.binding.adapter.BasicComponentFactory; import com.jgoodies.binding.adapter.Bindings; import com.jgoodies.binding.value.ValueModel; import com.jgoodies.forms.builder.PanelBuilder; import com.jgoodies.forms.layout.CellConstraints; import com.jgoodies.forms.layout.FormLayout; public class BenefitRiskWizard extends Wizard { private static final long serialVersionUID = -8142319787888932L; private static final Dimension PREFERRED_COLUMN_SIZE = new Dimension(330, 370); public BenefitRiskWizard(AddisWindow mainWindow, BenefitRiskWizardPM pm) { super(buildModel(pm, mainWindow)); getTitleComponent().setPreferredSize(new Dimension(700 , 100)); setPreferredSize(new Dimension(700, 550)); setMinimumSize(new Dimension(700, 550)); setDefaultExitMode(Wizard.EXIT_ON_FINISH); } private static WizardModel buildModel(final BenefitRiskWizardPM pm, AddisWindow mainWindow) { DynamicModel wizardModel = new DynamicModel(); wizardModel.add(new FirstWizardStep(pm)); wizardModel.add(new DescriptivesStep(pm), new Condition() { public boolean evaluate(WizardModel model) { return pm.getIncludeDescriptivesModel().getValue(); } }); wizardModel.add(new SelectStudyWizardStep(pm.getStudyBRPresentation(), mainWindow), new Condition() { public boolean evaluate(WizardModel model) { return pm.getEvidenceTypeHolder().getValue() == BRAType.SingleStudy; } }); wizardModel.add(new SelectOutcomeMeasuresAndArmsWizardStep(pm, pm.getStudyBRPresentation(), mainWindow), new Condition() { public boolean evaluate(WizardModel model) { return pm.getEvidenceTypeHolder().getValue() == BRAType.SingleStudy; } }); wizardModel.add(new SelectCriteriaAndAlternativesWizardStep((BenefitRiskWizardPM)pm, mainWindow), new Condition() { public boolean evaluate(WizardModel model) { return pm.getEvidenceTypeHolder().getValue() == BRAType.Synthesis; } }); return wizardModel; } private static <Alternative extends Comparable<Alternative> & Entity> Component buildAlternativesPanel(FormLayout layout, CriteriaAndAlternativesPresentation<Alternative> critAltPM) { PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); JLabel alternativesLabel = new JLabel("Alternatives"); alternativesLabel.setFont(alternativesLabel.getFont().deriveFont(Font.BOLD)); builder.add(alternativesLabel, cc.xy(1, 1)); int row = 1; for (final Alternative a : critAltPM.getAlternativesListModel()){ LayoutUtil.addRow(layout); final ValueHolder<Boolean> selectedModel = critAltPM.getAlternativeSelectedModel(a); final ValueHolder<Boolean> enabledModel = critAltPM.getAlternativeEnabledModel(a); JCheckBox armCheckbox = AuxComponentFactory.createDynamicEnabledBoundCheckbox(a.getLabel(), enabledModel, selectedModel); builder.add(armCheckbox, cc.xy(1, row += 2)); } row = LayoutUtil.addRow(layout, row, "10dlu"); builder.add(new JLabel("Baseline:"), cc.xy(1, row += 2)); ValueModel model = critAltPM.getBaselineModel(); builder.add(AuxComponentFactory.createBoundComboBox(critAltPM.getSelectedAlternatives(), model, true), cc.xy(1, row += 2)); return AuxComponentFactory.createInScrollPane(builder, PREFERRED_COLUMN_SIZE); } private static <Alternative extends Comparable<Alternative>> int addCriterionCheckbox(OutcomeMeasure out, CriteriaAndAlternativesPresentation<Alternative> critAltPM, FormLayout layout, PanelBuilder builder, CellConstraints cc, int row) { // Add outcome measure checkbox row = LayoutUtil.addRow(layout, row); builder.add(AuxComponentFactory.createDynamicEnabledBoundCheckbox(out.getName(), critAltPM.getCriterionEnabledModel(out), critAltPM.getCriterionSelectedModel(out)), cc.xyw(1, row, 3)); return row; } private static class DescriptivesStep extends PanelWizardStep { private static final long serialVersionUID = 5441828903910494369L; public DescriptivesStep(BenefitRiskWizardPM pm) { super("BRAT descriptives", "Define the decision context according to the BRAT framework"); setComplete(true); FormLayout layout = new FormLayout( "right:pref, 3dlu, fill:0:grow", "p" ); PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); builder.setDefaultDialogBorder(); int row = 1; for (AbstractBenefitRiskPresentation.DecisionContextField field : pm.getDecisionContextFields()) { builder.addLabel(field.getName() + ": ", cc.xy(1, row)); builder.add(TextComponentFactory.createTextArea(field.getModel(), true), cc.xy(3, row)); row = LayoutUtil.addRow(layout, row); builder.add(AuxComponentFactory.createTextPane(field.getHelpText()), cc.xy(3, row)); row = LayoutUtil.addRow(layout, row, "7dlu"); } this.setLayout(new BorderLayout()); JScrollPane scrollPane = new AddisScrollPane(builder.getPanel()); add(scrollPane, BorderLayout.CENTER); } } private static class FirstWizardStep extends PanelWizardStep { private static final long serialVersionUID = 2986876155242979527L; public FirstWizardStep(BenefitRiskWizardPM pm) { super("Select indication", "Select the indication, evidence type and analysis type that you want to use for this benefit-risk analysis."); FormLayout layout = new FormLayout( "right:pref, 3dlu, left:pref:grow", "p, 7dlu, p, 7dlu, p, 7dlu, p, 7dlu, p" ); PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); int row = 1; builder.add(IndicationAndNameInputPanel.create(this, pm), cc.xyw(1, row, 3)); row += 2; builder.add(BasicComponentFactory.createCheckBox(pm.getIncludeDescriptivesModel(), "Include BRAT decision context definition"), cc.xyw(1, row, 3)); row += 2; builder.add(new JLabel("Study type : "), cc.xy(1, row)); JPanel studyTypeRadioButtonPanel = new JPanel(); studyTypeRadioButtonPanel.setLayout(new BoxLayout(studyTypeRadioButtonPanel,BoxLayout.Y_AXIS)); JRadioButton MetaAnalysisButton = BasicComponentFactory.createRadioButton(pm.getEvidenceTypeHolder(), BRAType.Synthesis, "Evidence synthesis"); JRadioButton StudyButton = BasicComponentFactory.createRadioButton(pm.getEvidenceTypeHolder(), BRAType.SingleStudy, "Single study"); studyTypeRadioButtonPanel.add(MetaAnalysisButton); studyTypeRadioButtonPanel.add(StudyButton); builder.add(studyTypeRadioButtonPanel, cc.xy(3, row)); row += 2; builder.add(new JLabel("Analysis type : "), cc.xy(1, row)); JPanel analysisTypeRadioButtonPanel = new JPanel(); analysisTypeRadioButtonPanel.setLayout(new BoxLayout(analysisTypeRadioButtonPanel,BoxLayout.Y_AXIS)); JRadioButton SMAAButton = BasicComponentFactory.createRadioButton(pm.getAnalysisTypeHolder(), AnalysisType.SMAA, "SMAA"); JRadioButton LyndOBrienButton = BasicComponentFactory.createRadioButton(pm.getAnalysisTypeHolder(), AnalysisType.LyndOBrien, "Lynd & O'Brien"); analysisTypeRadioButtonPanel.add(SMAAButton); analysisTypeRadioButtonPanel.add(LyndOBrienButton); builder.add(analysisTypeRadioButtonPanel, cc.xy(3, row)); add(builder.getPanel()); } } private static class SelectStudyWizardStep extends PanelWizardStep { private static final long serialVersionUID = -6351673911830174693L; public SelectStudyWizardStep(final StudyCriteriaAndAlternativesPresentation pm, AddisWindow mainWindow){ super("Select Study","In this step you select which study you use as a basis for your analysis."); add(new JLabel("Study : ")); JComboBox studyBox = AuxComponentFactory.createBoundComboBox(pm.getStudiesWithIndication(), pm.getStudyModel(), true); add(studyBox); pm.getStudyModel().addValueChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { setComplete(evt.getNewValue() != null); } }); } } private static class SelectOutcomeMeasuresAndArmsWizardStep extends PanelWizardStep { private static final long serialVersionUID = -6712176504045317313L; private AddisWindow d_main; private StudyCriteriaAndAlternativesPresentation d_studyPM; private final BenefitRiskWizardPM d_pm; public SelectOutcomeMeasuresAndArmsWizardStep(BenefitRiskWizardPM pm, StudyCriteriaAndAlternativesPresentation studyCriteriaAndAlternativesPresentation, AddisWindow main) { super("Select OutcomeMeasures and Arms","In this step you select the criteria (specific outcomemeasures) " + "and the alternatives (drugs) to include in the benefit-risk analysis. To perform the analysis, at least " + "two criteria and at least two alternatives must be included."); d_pm = pm; d_main = main; d_studyPM = studyCriteriaAndAlternativesPresentation; } @Override public void prepare() { removeAll(); add(buildPanel()); Bindings.bind(this, "complete", d_pm.getCompleteModel()); } @Override public void applyState() throws InvalidStateException { d_main.leftTreeFocus(d_pm.saveAnalysis()); } private JPanel buildPanel() { FormLayout layout = new FormLayout( "left:pref, 3dlu, left:pref", "p" ); PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); builder.add(buildOutcomeMeasuresPane(), cc.xy(1, 1)); builder.add(buildAlternativesPane(), cc.xy(3, 1)); return builder.getPanel(); } private Component buildOutcomeMeasuresPane() { FormLayout layout = new FormLayout( "left:pref:grow, 3dlu, left:pref", "p, 3dlu, p, 3dlu, p" ); PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); JLabel outcomeMeasuresLabel = new JLabel("Criteria"); outcomeMeasuresLabel.setFont(outcomeMeasuresLabel.getFont().deriveFont(Font.BOLD)); builder.add(outcomeMeasuresLabel, cc.xyw(1, 1, 3)); int row = 1; for (OutcomeMeasure out : d_studyPM.getCriteriaListModel()) { // Add outcome measure checkbox row = addCriterionCheckbox(out, d_studyPM, layout, builder, cc, row); } return AuxComponentFactory.createInScrollPane(builder, PREFERRED_COLUMN_SIZE); } private Component buildAlternativesPane() { FormLayout layout = new FormLayout( "left:pref, 3dlu, left:pref", "p, 3dlu, p, 3dlu, p" ); return buildAlternativesPanel(layout, d_studyPM); } } public static class SelectCriteriaAndAlternativesWizardStep extends PanelWizardStep { private static final long serialVersionUID = -7893619121154004229L; private AddisWindow d_mainWindow; private BenefitRiskWizardPM d_pm; private MetaCriteriaAndAlternativesPresentation d_metaPM; public SelectCriteriaAndAlternativesWizardStep(BenefitRiskWizardPM pm, AddisWindow main) { super("Select Criteria and Alternatives","In this step, you select the criteria (analyses on specific outcomemeasures) " + "and the alternatives (drugs) to include in the benefit-risk analysis. To perform the analysis, at least two criteria " + "and at least two alternatives must be included."); d_mainWindow = main; d_pm = pm; d_metaPM = d_pm.getMetaBRPresentation(); } @Override public void prepare() { this.removeAll(); add(buildPanel()); Bindings.bind(this, "complete", d_pm.getCompleteModel()); } @Override public void applyState() throws InvalidStateException { d_mainWindow.leftTreeFocus(d_pm.saveAnalysis()); } private JComponent buildPanel() { GridLayout layout = new GridLayout(1, 2, 15, 0); JPanel panel = new JPanel(layout); panel.add(buildCriteriaPane()); panel.add(buildAlternativesPane()); return panel; } private Component buildCriteriaPane() { FormLayout layout = new FormLayout( "left:pref, 3dlu, fill:0:grow", "p, 3dlu, p, 3dlu, p" ); PanelBuilder builder = new PanelBuilder(layout); CellConstraints cc = new CellConstraints(); JLabel criteriaLabel = new JLabel("Criteria"); criteriaLabel.setFont( criteriaLabel.getFont().deriveFont(Font.BOLD)); builder.add(criteriaLabel, cc.xy(1, 1)); if (checkSuitableMetaAnalysesAvailable(d_pm)) { int row = 1; for (OutcomeMeasure out : d_metaPM.getCriteriaListModel()){ if (!d_metaPM.getMetaAnalyses(out).isEmpty()) { row = addCriterionCheckbox(out, d_metaPM, layout, builder, cc, row); // Add radio-button panel row = LayoutUtil.addRow(layout, row); builder.add(buildRadioButtonAnalysisPanel(out), cc.xy(3, row, CellConstraints.LEFT, CellConstraints.DEFAULT)); } } } else { String warnHTMLText = "<i>Note</i>: To create a benefit-risk analysis, first create " + "at least two meta-analyses with at least two overlapping alternatives."; JComponent htmlField = AuxComponentFactory.createTextPane(warnHTMLText); htmlField.setPreferredSize(new Dimension(PREFERRED_COLUMN_SIZE.width - 5, 100)); int row = 1; row = LayoutUtil.addRow(layout, row); builder.add(htmlField, cc.xyw(1, row, 3)); } return AuxComponentFactory.createInScrollPane(builder, PREFERRED_COLUMN_SIZE); } private JPanel buildRadioButtonAnalysisPanel(OutcomeMeasure out) { // create the panel JPanel radioButtonPanel = new JPanel(); radioButtonPanel.setLayout(new BoxLayout(radioButtonPanel,BoxLayout.Y_AXIS)); // Retrieve the valueModel to see whether we should enable the radio-buttons. ValueHolder<Boolean> enabledModel = d_metaPM.getCriterionSelectedModel(out); // Add the radio buttons for (MetaAnalysis ma : d_metaPM.getMetaAnalyses(out)){ ValueHolder<MetaAnalysis> selectedModel = d_metaPM.getMetaAnalysesSelectedModel(out); JRadioButton radioButton = AuxComponentFactory.createDynamicEnabledRadioButton(ma.getName(), ma, selectedModel, enabledModel); radioButtonPanel.add(radioButton); } return radioButtonPanel; } private Component buildAlternativesPane() { FormLayout layout = new FormLayout( "left:pref", "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p" ); return buildAlternativesPanel(layout, d_metaPM); } private static boolean checkSuitableMetaAnalysesAvailable(BenefitRiskWizardPM pm) { MetaCriteriaAndAlternativesPresentation metaPM = pm.getMetaBRPresentation(); List<OutcomeMeasure> outcomeMeasures = metaPM.getCriteriaListModel(); if (outcomeMeasures.size() < 2) { return false; } final OutcomeMeasure firstOM = outcomeMeasures.get(0); boolean foundDifferentOM = false; for (OutcomeMeasure om : outcomeMeasures) { if (!firstOM.deepEquals(om)) { foundDifferentOM = true; break; } } if (!foundDifferentOM) { return false; } return true; } } }