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}