001package conexp.fx.gui.assistent; 002 003/* 004 * #%L 005 * Concept Explorer FX 006 * %% 007 * Copyright (C) 2010 - 2023 Francesco Kriegel 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import java.io.File; 026import java.io.IOException; 027 028import conexp.fx.core.exporter.TeXExporter; 029import conexp.fx.core.exporter.TeXExporter.ContextTeXPackage; 030import conexp.fx.core.exporter.TeXExporter.DiagramTeXPackage; 031import conexp.fx.core.exporter.TeXExporter.FitScale; 032import conexp.fx.core.exporter.TeXExporter.ScaleEnum; 033import conexp.fx.core.exporter.TeXExporter.TeXOptions; 034import conexp.fx.core.util.FileFormat; 035import conexp.fx.gui.ConExpFX; 036import conexp.fx.gui.dataset.FCADataset; 037import javafx.beans.binding.ObjectBinding; 038import javafx.beans.value.ChangeListener; 039import javafx.beans.value.ObservableValue; 040import javafx.geometry.Insets; 041import javafx.scene.Node; 042import javafx.scene.control.CheckBox; 043import javafx.scene.control.CheckBoxBuilder; 044import javafx.scene.control.RadioButton; 045import javafx.scene.control.RadioButtonBuilder; 046import javafx.scene.control.Toggle; 047import javafx.scene.control.ToggleGroup; 048import javafx.scene.control.ToggleGroupBuilder; 049import javafx.scene.layout.BorderPane; 050import javafx.scene.layout.HBoxBuilder; 051import javafx.scene.layout.VBox; 052import javafx.scene.layout.VBoxBuilder; 053import javafx.stage.FileChooser; 054import javafx.stage.Stage; 055import javafx.util.converter.IntegerStringConverter; 056import jfxtras.scene.control.ListSpinner; 057 058@SuppressWarnings("deprecation") 059public class ExportAssistent extends Assistent<String> { 060 061 public final class TeXExportPage extends AssistentPage<TeXOptions> { 062 063 private final FCADataset<?, ?> fcaInstance; 064 065 public TeXExportPage(final FCADataset<?, ?> fcaInstance) { 066 super("TeX Export", "Exports a Formal Context and its Concept Lattice to LaTeX", null, r -> null); 067 this.fcaInstance = fcaInstance; 068 this.resultProperty.set(new TeXOptions(null, false, false, false, null, null, null)); 069 this.contentProperty.set(createContentNode()); 070 } 071 072 @SuppressWarnings({ 073 "rawtypes", 074 "unchecked" 075 }) 076 @Override 077 protected void onNext() { 078 try { 079 this.resultProperty.get().file = ExportAssistent.this.showFileChooser(); 080 new TeXExporter( 081 fcaInstance.context, 082 fcaInstance.contextWidget.rowHeaderPane.rowMap, 083 fcaInstance.contextWidget.colHeaderPane.columnMap, 084 fcaInstance.layout, 085 this.resultProperty.get()).export(); 086 } catch (IOException e) { 087 e.printStackTrace(); 088 } 089 } 090 091 protected Node createContentNode() { 092 VBox box = new VBox(); 093 box.setPadding(new Insets(0, 10, 0, 10)); 094 box.setSpacing(4); 095 this.resultProperty.set( 096 new TeXOptions( 097 null, 098 false, 099 true, 100 false, 101 ContextTeXPackage.None, 102 DiagramTeXPackage.ConExpFX, 103 new FitScale(80, 120))); 104 final CheckBox arrowsCheckBox = CheckBoxBuilder.create().text("Arrow Relations").build(); 105 final CheckBox labelsCheckBox = CheckBoxBuilder.create().text("Concept Labels").selected(true).build(); 106 final CheckBox standAloneCheckBox = CheckBoxBuilder.create().disable(true).text("Stand-Alone Document").build(); 107 final RadioButton noneContextButton = RadioButtonBuilder 108 .create() 109 .text("Context Package: None") 110 .selected(true) 111 .userData(ContextTeXPackage.None) 112 .build(); 113 final RadioButton ganterContextButton = 114 RadioButtonBuilder.create().text("Context Package: Ganter").userData(ContextTeXPackage.Ganter).build(); 115 final RadioButton tabularContextButton = 116 RadioButtonBuilder.create().text("Context Package: Tabular").userData(ContextTeXPackage.Tabular).build(); 117 final RadioButton noneDiagramButton = 118 RadioButtonBuilder.create().text("Diagram Package: None").userData(DiagramTeXPackage.None).build(); 119 final RadioButton ganterDiagramButton = 120 RadioButtonBuilder.create().text("Diagram Package: Ganter").userData(DiagramTeXPackage.Ganter).build(); 121 final RadioButton conExpFXDiagramButton = RadioButtonBuilder 122 .create() 123 .text("Diagram Package: ConExpFX") 124 .selected(true) 125 .userData(DiagramTeXPackage.ConExpFX) 126 .build(); 127 final RadioButton fitButton = 128 RadioButtonBuilder.create().text("Diagram Scale: Fit").userData(ScaleEnum.Fit).build(); 129 final RadioButton fitWidthButton = 130 RadioButtonBuilder.create().text("Diagram Scale: Fit Width").userData(ScaleEnum.FitWidth).build(); 131 final RadioButton fitHeightButton = 132 RadioButtonBuilder.create().text("Diagram Scale: Fit Height").userData(ScaleEnum.FitHeight).build(); 133 final RadioButton fitRatioButton = RadioButtonBuilder 134 .create() 135 .text("Diagram Scale: Fit Ratio") 136 .selected(true) 137 .userData(ScaleEnum.FitRatio) 138 .build(); 139 final ListSpinner<Integer> widthSpinner = new ListSpinner<Integer>(1, 1000); 140 final ListSpinner<Integer> heightSpinner = new ListSpinner<Integer>(1, 1000); 141 widthSpinner.valueProperty().set(80); 142 heightSpinner.valueProperty().set(120); 143 widthSpinner.withPostfix("mm"); 144 heightSpinner.withPostfix("mm"); 145// widthSpinner.withAlignment(Pos.CENTER); 146// heightSpinner.withAlignment(Pos.CENTER); 147// widthSpinner.withArrowDirection(ArrowDirection.HORIZONTAL); 148// heightSpinner.withArrowDirection(ArrowDirection.VERTICAL); 149// widthSpinner.withArrowPosition(ArrowPosition.SPLIT); 150// heightSpinner.withArrowPosition(ArrowPosition.SPLIT); 151 widthSpinner.withEditable(true); 152 widthSpinner.withStringConverter(new IntegerStringConverter()); 153 heightSpinner.withEditable(true); 154 heightSpinner.withStringConverter(new IntegerStringConverter()); 155 widthSpinner.setMinWidth(100); 156 widthSpinner.setMaxWidth(100); 157 heightSpinner.setMinWidth(100); 158 heightSpinner.setMaxWidth(100); 159 final ToggleGroup contextGroup = 160 ToggleGroupBuilder.create().toggles(noneContextButton, ganterContextButton, tabularContextButton).build(); 161 final ToggleGroup diagramGroup = 162 ToggleGroupBuilder.create().toggles(noneDiagramButton, ganterDiagramButton, conExpFXDiagramButton).build(); 163 final ToggleGroup scaleGroup = 164 ToggleGroupBuilder.create().toggles(fitButton, fitWidthButton, fitHeightButton, fitRatioButton).build(); 165 arrowsCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() { 166 167 @Override 168 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 169 TeXExportPage.this.resultProperty.get().arrows = newValue; 170 } 171 }); 172 labelsCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() { 173 174 @Override 175 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 176 TeXExportPage.this.resultProperty.get().labels = newValue; 177 } 178 }); 179 standAloneCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() { 180 181 @Override 182 public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 183 TeXExportPage.this.resultProperty.get().standAlone = newValue; 184 } 185 }); 186 contextGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() { 187 188 @Override 189 public void changed(ObservableValue<? extends Toggle> observable, Toggle oldToggle, Toggle newToggle) { 190 TeXExportPage.this.resultProperty.get().contextTeXPackage = (ContextTeXPackage) newToggle.getUserData(); 191 } 192 }); 193 diagramGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() { 194 195 @Override 196 public void changed(ObservableValue<? extends Toggle> observable, Toggle oldToggle, Toggle newToggle) { 197 TeXExportPage.this.resultProperty.get().diagramTeXPackage = (DiagramTeXPackage) newToggle.getUserData(); 198 } 199 }); 200 scaleGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() { 201 202 @Override 203 public void changed(ObservableValue<? extends Toggle> observable, Toggle oldToggle, Toggle newToggle) { 204 TeXExportPage.this.resultProperty.get().scale = ((ScaleEnum) newToggle.getUserData()) 205 .toOption(widthSpinner.valueProperty().get(), heightSpinner.valueProperty().get()); 206 widthSpinner.disableProperty().set((ScaleEnum) newToggle.getUserData() == ScaleEnum.FitHeight); 207 heightSpinner.disableProperty().set((ScaleEnum) newToggle.getUserData() == ScaleEnum.FitWidth); 208 } 209 }); 210 widthSpinner.valueProperty().addListener(new ChangeListener<Integer>() { 211 212 @Override 213 public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) { 214 TeXExportPage.this.resultProperty.get().scale = 215 ((ScaleEnum) scaleGroup.selectedToggleProperty().get().getUserData()) 216 .toOption(newValue, heightSpinner.valueProperty().get()); 217 } 218 }); 219 heightSpinner.valueProperty().addListener(new ChangeListener<Integer>() { 220 221 @Override 222 public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue) { 223 TeXExportPage.this.resultProperty.get().scale = 224 ((ScaleEnum) scaleGroup.selectedToggleProperty().get().getUserData()) 225 .toOption(widthSpinner.valueProperty().get(), newValue); 226 } 227 }); 228 box.getChildren().addAll( 229 VBoxBuilder 230 .create() 231 .padding(new Insets(2, 0, 2, 0)) 232 .spacing(4) 233 .children(arrowsCheckBox, labelsCheckBox, standAloneCheckBox) 234 .build()); 235 box.getChildren().addAll( 236 VBoxBuilder 237 .create() 238 .padding(new Insets(2, 0, 2, 0)) 239 .spacing(4) 240 .children(noneContextButton, ganterContextButton, tabularContextButton) 241 .build()); 242 box.getChildren().addAll( 243 VBoxBuilder 244 .create() 245 .padding(new Insets(2, 0, 2, 0)) 246 .spacing(4) 247 .children(noneDiagramButton, ganterDiagramButton, conExpFXDiagramButton) 248 .build()); 249 box.getChildren().addAll( 250 VBoxBuilder 251 .create() 252 .padding(new Insets(2, 0, 0, 0)) 253 .spacing(4) 254 .children(fitButton, fitWidthButton, fitHeightButton, fitRatioButton) 255 .build()); 256 box.getChildren().addAll( 257 HBoxBuilder 258 .create() 259 .padding(new Insets(0, 0, 2, 0)) 260 .spacing(4) 261 .children(widthSpinner, heightSpinner) 262 .build()); 263 return box; 264 } 265 266 } 267 268 private final FCADataset fcaInstance; 269 270 public ExportAssistent(final Stage owner, final FCADataset<?, ?> fcaInstance) { 271 super( 272 owner, 273 "Export Wizard", 274 "Export Wizard", 275 "Exports a Formal Context", 276 null, 277 r -> r.equals("TEX") ? "TEX" : null); 278 this.fcaInstance = fcaInstance; 279 initialize(); 280 } 281 282 @Override 283 protected Node createInitialNode() { 284 final BorderPane pane = new BorderPane(); 285 final ToggleGroup toggleGroup = new ToggleGroup(); 286 final RadioButton htmlButton = new RadioButton("HTML"); 287 final RadioButton pdfButton = new RadioButton("PDF"); 288 final RadioButton pngButton = new RadioButton("PNG"); 289 final RadioButton svgButton = new RadioButton("SVG"); 290 final RadioButton texButton = new RadioButton("LaTeX"); 291 htmlButton.setToggleGroup(toggleGroup); 292 pdfButton.setToggleGroup(toggleGroup); 293 pngButton.setToggleGroup(toggleGroup); 294 svgButton.setToggleGroup(toggleGroup); 295 texButton.setToggleGroup(toggleGroup); 296 htmlButton.setUserData("HTML"); 297 pdfButton.setUserData("PDF"); 298 pngButton.setUserData("PNG"); 299 svgButton.setUserData("SVG"); 300 texButton.setUserData("TEX"); 301 final VBox toggleBox = new VBox(htmlButton, pdfButton, pngButton, svgButton, texButton); 302 this.resultProperty.bind(new ObjectBinding<String>() { 303 304 { 305 super.bind(toggleGroup.selectedToggleProperty()); 306 } 307 308 @Override 309 protected String computeValue() { 310 final Toggle toggle = toggleGroup.selectedToggleProperty().get(); 311 if (toggle == null) 312 return null; 313 return (String) toggle.getUserData(); 314 } 315 316 }); 317 pane.setPadding(new Insets(4)); 318 pane.setCenter(toggleBox); 319 return pane; 320 } 321 322 @Override 323 protected void createPages() { 324 this.availablePages.put("TEX", new TeXExportPage(fcaInstance)); 325 } 326 327 @Override 328 protected void onNext() { 329 if (!this.resultProperty.get().equals("TEX")) { 330 final File result = showFileChooser(); 331 if (result != null) 332 fcaInstance.export(FileFormat.valueOf(this.resultProperty.get()), result); 333 } 334 } 335 336 private final File showFileChooser() { 337 final FileChooser fc = new FileChooser(); 338 fc.setInitialDirectory(ConExpFX.instance.lastDirectory); 339 return fc.showSaveDialog(owner); 340 } 341 342}