001package conexp.fx.gui.dataset;
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 */
024import java.io.File;
025import java.io.IOException;
026import java.util.Collections;
027import java.util.List;
028import java.util.stream.Collectors;
029
030import conexp.fx.core.algorithm.exploration.AttributeExploration;
031import conexp.fx.core.algorithm.exploration.ParallelAttributeExploration;
032import conexp.fx.core.algorithm.lattice.IFox2;
033import conexp.fx.core.algorithm.lattice.IPred;
034import conexp.fx.core.algorithm.nextclosures.NextClosures2Bit;
035import conexp.fx.core.builder.FileRequest;
036import conexp.fx.core.builder.Request;
037import conexp.fx.core.builder.Requests.Source;
038import conexp.fx.core.builder.StringRequest;
039import conexp.fx.core.collections.Pair;
040import conexp.fx.core.collections.relation.RelationEvent;
041import conexp.fx.core.context.Concept;
042import conexp.fx.core.context.ConceptLattice;
043import conexp.fx.core.context.Context;
044import conexp.fx.core.context.Implication;
045import conexp.fx.core.context.MatrixContext;
046import conexp.fx.core.context.MatrixContext.AutomaticMode;
047import conexp.fx.core.exporter.CFXExporter;
048import conexp.fx.core.exporter.CXTExporter;
049import conexp.fx.core.exporter.HTMLExporter;
050import conexp.fx.core.exporter.PDFExporter;
051import conexp.fx.core.exporter.PNGExporter;
052import conexp.fx.core.exporter.SVGExporter;
053import conexp.fx.core.exporter.TeXExporter;
054import conexp.fx.core.exporter.TeXExporter.ContextTeXPackage;
055import conexp.fx.core.exporter.TeXExporter.DiagramTeXPackage;
056import conexp.fx.core.exporter.TeXExporter.FitScale;
057import conexp.fx.core.exporter.TeXExporter.TeXOptions;
058import conexp.fx.core.layout.AdditiveConceptLayout;
059import conexp.fx.core.layout.AdditiveConceptLayout.Type;
060import conexp.fx.core.layout.ConceptMovement;
061import conexp.fx.core.layout.GeneticLayouter;
062import conexp.fx.core.layout.LayoutEvolution;
063import conexp.fx.core.layout.QualityMeasure;
064import conexp.fx.core.util.Constants;
065import conexp.fx.core.util.FileFormat;
066import conexp.fx.gui.ConExpFX;
067import conexp.fx.gui.context.ConceptWidget;
068import conexp.fx.gui.context.ImplicationWidget;
069import conexp.fx.gui.context.MatrixContextWidget;
070import conexp.fx.gui.dialog.FXDialog.Answer;
071import conexp.fx.gui.dialog.FXDialog.Return;
072import conexp.fx.gui.dialog.TeXDialog;
073import conexp.fx.gui.graph.CircularGraph;
074import conexp.fx.gui.graph.ConceptGraph;
075import conexp.fx.gui.graph.PolarGraph;
076import conexp.fx.gui.task.TimeTask;
077import conexp.fx.gui.util.Platform2;
078import javafx.application.Platform;
079import javafx.collections.FXCollections;
080import javafx.collections.ObservableList;
081import javafx.stage.FileChooser;
082import javafx.stage.FileChooser.ExtensionFilter;
083
084public final class FCADataset<G, M> extends Dataset {
085
086  public final Request<G, M>                                     request;
087  public final MatrixContext<G, M>                               context;
088  public final ConceptLattice<G, M>                              lattice;
089  public final AdditiveConceptLayout<G, M>                       layout;
090//  private final SimpleConceptLayout<G, M>                        l;
091  public final ObservableList<Concept<G, M>>                     concepts            =
092      FXCollections.<Concept<G, M>> observableArrayList();
093  public final ObservableList<Implication<G, M>>                 implications        =
094      FXCollections.<Implication<G, M>> observableArrayList();
095  public final ObservableList<Implication<G, M>>                 partialImplications =
096      FXCollections.observableArrayList();
097
098  public final QualityMeasure<G, M, Pair<Concept<G, M>, Double>> conflictDistance    =
099      QualityMeasure.conflictDistance();
100//  public final EdgeIntersections<G, M> edgeIntersections = new EdgeIntersections<G, M>();
101  public boolean                                                 editable            = false;
102
103  public final MatrixContextWidget<G, M>                         contextWidget;
104  public final ConceptGraph<G, M>                                conceptGraph;
105  public final ConceptWidget<G, M>                               conceptWidget;
106  public final ImplicationWidget<G, M>                           implicationWidget;
107
108  public FCADataset(final Dataset parentDataset, final Request<G, M> request) {
109    super(parentDataset);
110    this.request = request;
111    this.context = request.createContext(AutomaticMode.REDUCE);
112    this.context.id.bind(id);
113    this.lattice = new ConceptLattice<G, M>(context);
114    this.layout = new AdditiveConceptLayout<G, M>(lattice, null, null, Type.HYBRID);
115    this.layout.observe();
116    this.id.set(request.getId());
117    if (request.src != Source.FILE)
118      unsavedChanges.set(true);
119    else if (request instanceof FileRequest)
120      file = ((FileRequest) request).file;
121//    this.layout.observe();
122//    this.setContent(pane);
123//    this.setGraphic(ImageViewBuilder
124//        .create()
125//        .image(new Image(GUI.class.getResourceAsStream("image/context.gif")))
126//        .build());
127//    this.textProperty().bind(new StringBinding() {
128//
129//      {
130//        bind(fca.id, fca.unsavedChanges);
131//      }
132//
133//      protected final String computeValue() {
134//        return fca.id.get() + (fca.unsavedChanges.get() ? " *" : "");
135//      }
136//    });
137//    this.setOnClosed(new EventHandler<Event>() {
138//
139//      public final void handle(final Event event) {
140//        if (fca.unsavedChanges.get())
141//          if (new FXDialog(conExp.primaryStage, Style.QUESTION, "Unsaved Changes", fca.id.get()
142//              + " has unsaved changes. Do you want to save?", null).showAndWait().equals(Result.YES))
143//            save();
144//      }
145//    });
146//    final ExecutorStatusBar conExp.executorStatusBar = new ExecutorStatusBar();
147//    conExp.executorStatusBar.bindTo(conExp.executor);
148//    this.statusWidget = conExp.executorStatusBar.statusBar;
149    if (request instanceof FileRequest) {
150      this.editable = true;
151//      this.contextWidget = (MatrixContextWidget<G, M>) new StringMatrixContextWidget((FCADataset<String, String>) this);
152    } else if (request instanceof StringRequest) {
153      this.editable = true;
154//      this.contextWidget = (MatrixContextWidget<G, M>) new StringMatrixContextWidget((FCADataset<String, String>) this);
155    } // else
156    this.contextWidget = new MatrixContextWidget<G, M>(this);
157    this.conceptGraph = new ConceptGraph<G, M>(this);
158    this.conceptWidget = new ConceptWidget<G, M>(this);
159    this.implicationWidget = new ImplicationWidget<G, M>(this);
160    lattice.addEventHandler(event -> {
161      concepts.clear();
162      concepts.addAll(lattice.colHeads());
163    } , RelationEvent.ALL_CHANGED);
164    views.add(new DatasetView<Context<G, M>>("Context", contextWidget, context));
165    views.add(new DatasetView<AdditiveConceptLayout<G, M>>("Lattice", conceptGraph, layout));
166//    this.l = new SimpleConceptLayout<G, M>(lattice);
167//    views.add(new DatasetView<SimpleConceptLayout<G, M>>("Force Lattice", new ConceptGraph<G, M>(this, l), l));
168    views.add(new DatasetView<List<Concept<G, M>>>("Concepts", conceptWidget, concepts));
169    views.add(new DatasetView<List<Implication<G, M>>>("Implications", implicationWidget, implications));
170    defaultActiveViews.add("Lattice");
171    actions.add(new DatasetAction("Polar Layout", () -> polarLayout()));
172    actions.add(new DatasetAction("Circular Layout", () -> circularLayout()));
173    if (editable) {
174      actions.add(new DatasetAction("Explore...", () -> {
175        implications.clear();
176        try {
177          AttributeExploration.withHumanExpert(((MatrixContext<String, String>) context).getSelection()).start();
178        } catch (InterruptedException __) {}
179      }));
180      actions.add(new DatasetAction("Explore (parallel)...", () -> {
181        implications.clear();
182        ConExpFX.execute(ParallelAttributeExploration.createExplorationTask((FCADataset<String, String>) this));
183      }));
184    }
185    actions.add(new DatasetAction("Refresh", () -> reinitializeWithNextClosures()));
186    Platform2.runOnFXThread(() -> initializeWithNextClosures());
187  }
188
189  private final void polarLayout() {
190    new PolarGraph<G, M>(lattice).show();
191  }
192
193  private final void circularLayout() {
194    new CircularGraph<G, M>(lattice).show();
195  }
196
197  public final void initializeWithIFox() {
198    ConExpFX.execute(TimeTask.create(this, "Importing Formal Context", () -> {
199      request.setContent();
200      context.addEventHandler(
201          __ -> Platform.runLater(() -> unsavedChanges.set(true)),
202          RelationEvent.ROWS,
203          RelationEvent.COLUMNS,
204          RelationEvent.ENTRIES);
205    }));
206    ConExpFX.execute(new TimeTask<Void>("Initialization") {
207
208      @Override
209      protected Void call() {
210        updateProgress(0d, 1d);
211        if (isCancelled())
212          return null;
213        updateProgress(0.1d, 1d);
214        context.deselectAllAttributes();
215        final Concept<G, M> top = new Concept<G, M>(context.rowHeads(), Collections.<M> emptySet());
216        lattice.rowHeads().add(top);
217        updateProgress(0.2d, 1d);
218        updateMessage("Computing Object Labels...");
219        synchronized (lattice.objectConcepts) {
220          for (G g : context.rowHeads())
221            lattice.objectConcepts.put(g, top);
222        }
223        updateProgress(0.4d, 1d);
224        layout.invalidate();
225        for (int col = 0; col < context.colHeads().size(); col++) {
226          final int _col = col;
227          ConExpFX.execute(new TimeTask<Void>("Selecting " + _col) {
228
229            @Override
230            protected Void call() {
231              updateProgress(0d, 1d);
232              if (isCancelled())
233                return null;
234              selectAttribute(context.colHeads().get(_col));
235              updateProgress(1d, 1d);
236              return null;
237            }
238          });
239        }
240        updateProgress(1d, 1d);
241        return null;
242      }
243    });
244  }
245
246  public final void initializeWithNextClosures() {
247    ConExpFX.execute(TimeTask.create(this, "Importing Formal Context", () -> {
248      request.setContent();
249//    context.addEventHandler(__ -> reinitializeWithNextClosures(), RelationEvent.ROWS);
250      context.addEventHandler(
251          __ -> Platform.runLater(() -> unsavedChanges.set(true)),
252          RelationEvent.ROWS,
253          RelationEvent.COLUMNS,
254          RelationEvent.ENTRIES);
255    }));
256    _reinitializeWithNextClosures();
257  }
258
259  private final void reinitializeWithNextClosures() {
260    ConExpFX.execute(TimeTask.create(this, "Reinit", () -> {
261      layout.seedsG.clear();
262      layout.seedsM.clear();
263      lattice.objectConcepts.clear();
264      lattice.attributeConcepts.clear();
265      conceptGraph.removeContent();
266      lattice.rowHeads().clear();
267      concepts.clear();
268      implications.clear();
269      partialImplications.clear();
270    } , true));
271    _reinitializeWithNextClosures();
272  }
273
274  private final void _reinitializeWithNextClosures() {
275    ConExpFX.execute(new TimeTask<Void>(this, "Locking Graph") {
276
277      protected final Void call() {
278        updateProgress(0d, 1d);
279        if (isCancelled())
280          return null;
281        updateProgress(0.5d, 1d);
282        conceptGraph.controller.graphLock = true;
283        conceptGraph.highlightLock = true;
284        updateProgress(1d, 1d);
285        return null;
286      }
287    });
288    ConExpFX.execute(TimeTask.create(this, "Reduce Formal Context", () -> context.pushAllChangedEvent()
289//      context.initHandlers(true, MatrixContext.AutomaticMode.REDUCE)
290//  final int rows = this.fcaInstance.context.rowHeads().size();
291//  final int cols = this.fcaInstance.context.colHeads().size();
292//  this.fcaInstance.context.initHandlers(true, MatrixContext.AutomaticMode.fromSize(rows,cols));
293    ));
294    ConExpFX.execute(NextClosures2Bit.createTask(this));
295    ConExpFX.execute(
296        TimeTask.create(
297            this,
298            "Sorting Formal Concepts",
299            () -> lattice.rowHeads().addAll(
300                concepts
301                    .parallelStream()
302                    .sorted((c1, c2) -> (int) Math.signum(c1.intent().size() - c2.intent().size()))
303                    .collect(Collectors.toList()))));
304    ConExpFX.execute(GeneticLayouter.initialSeeds(this));
305    ConExpFX.execute(IPred.neighborhoodP(this));
306    ConExpFX.execute(new TimeTask<Void>(this, "Unlocking Graph") {
307
308      protected final Void call() {
309        updateProgress(0d, 1d);
310        if (isCancelled())
311          return null;
312        updateProgress(0.5d, 1d);
313        conceptGraph.controller.graphLock = false;
314        conceptGraph.highlightLock = false;
315        updateProgress(1d, 1d);
316        return null;
317      }
318    });
319    ConExpFX.execute(TimeTask.create(this, "Initialize Concept Lattice Graph", () -> {
320//      Platform2.runOnFXThread(() -> layout.invalidate());
321//      new Thread(() -> lattice.pushAllChangedEvent()).start();
322      lattice.pushAllChangedEvent();
323//      Platform2.runOnFXThread(() -> layout.invalidate());
324    }));
325    ConExpFX.execute(
326        TimeTask.create(
327            this,
328            "Computing Partial Implications",
329            () -> implications.addAll(lattice.luxenburgerBase(0d, true))));
330//ConExpFX.execute(new TimeTask<Void>(this, "Computing Partial Implications") {
331//
332//@Override
333//protected Void call() {
334//  updateProgress(0d, 1d);
335//  if (isCancelled())
336//    return null;
337//  conceptGraph.controller.graphLock = true;
338//  conceptGraph.highlightLock = true;
339//  updateProgress(0.25d, 1d);
340//  implications.addAll(lattice.luxenburgerBase(0d, true));
341//  updateProgress(0.75d, 1d);
342//  conceptGraph.controller.graphLock = false;
343//  conceptGraph.highlightLock = false;
344//  updateProgress(1d, 1d);
345//  return null;
346//}
347//});
348    relayout(0, Constants.POPULATION);
349    ConExpFX.execute(TimeTask.create(this, "Initialize Concept Lattice Graph", layout::invalidate, true));
350//    ConExpFX.execute(TimeTask.create(this, "Force Layouter", () -> {
351//      for (Concept<G, M> c : lattice.rowHeads())
352//        l.getOrAddPosition(c).setValue(layout.getPosition(c).getValue());
353//      l.start();
354//    }));
355  }
356
357  public final void addObject(final G object, final int index) {
358    ConExpFX.execute(new TimeTask<Void>(this, "New Object") {
359
360      protected final Void call() {
361        updateProgress(0d, 1d);
362        if (isCancelled())
363          return null;
364        updateProgress(0.5d, 1d);
365//        conceptGraph.controller.graphLock = true;
366//        conceptGraph.highlightLock = true;
367//        context.pushAllChangedEvent();
368        if (index == -1)
369          context.rowHeads().add(object);
370        else
371          context.rowHeads().add(index, object);
372        Platform2.runOnFXThread(() -> {
373          unsavedChanges.set(true);
374        });
375//        lattice.rowHeads().clear();
376        updateProgress(1d, 1d);
377        return null;
378      }
379    });
380    this.reinitializeWithNextClosures();
381  }
382
383  public final void addAttribute(final M attribute, final int index) {
384    ConExpFX.execute(new TimeTask<Void>(this, "New Attribute") {
385
386      protected final Void call() {
387        updateProgress(0d, 1d);
388        if (isCancelled())
389          return null;
390        updateProgress(0.5d, 1d);
391        conceptGraph.controller.graphLock = true;
392        conceptGraph.highlightLock = true;
393        Platform2.runOnFXThread(() -> unsavedChanges.set(true));
394        if (index == -1)
395          context.colHeads().add(attribute);
396        else
397          context.colHeads().add(index, attribute);
398        context.deselectAttribute(attribute);
399        context.pushAllChangedEvent();
400        // select(attribute);
401        updateProgress(1d, 1d);
402        return null;
403      }
404    });
405    ConExpFX.execute(IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()));
406    ConExpFX.execute(new TimeTask<Void>(this, "New Attribute") {
407
408      protected final Void call() {
409        updateProgress(0d, 1d);
410        if (isCancelled())
411          return null;
412        updateProgress(0.5d, 1d);
413        conceptGraph.controller.graphLock = false;
414        conceptGraph.highlightLock = false;
415        updateProgress(1d, 1d);
416        return null;
417      }
418    });
419  }
420
421  public final void removeObject(final G object) {
422    ConExpFX.execute(new TimeTask<Void>(this, "Removing Object") {
423
424      protected final Void call() {
425        updateProgress(0d, 1d);
426        if (isCancelled())
427          return null;
428        updateProgress(0.5d, 1d);
429        // conceptGraph.controller.graphLock = true;
430        // conceptGraph.highlightLock = true;
431        // context.pushAllChangedEvent();
432        context.rowHeads().remove(object);
433        Platform2.runOnFXThread(() -> unsavedChanges.set(true));
434        // lattice.rowHeads().clear();
435        updateProgress(1d, 1d);
436        return null;
437      }
438    });
439    this.reinitializeWithNextClosures();
440  }
441
442  public final void removeAttribute(final M attribute) {
443    ConExpFX.execute(TimeTask.create(this, "Removing Attribute", () -> {
444      conceptGraph.controller.graphLock = true;
445      conceptGraph.highlightLock = true;
446      Platform2.runOnFXThread(() -> unsavedChanges.set(true));
447//      context.deselectAttribute(attribute);
448//      context.pushAllChangedEvent();
449    }));
450    if (context.selectedAttributes().contains(attribute))
451      ConExpFX.execute(IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()));
452    ConExpFX.execute(TimeTask.create(this, "Removing Attribute", () -> {
453      context.colHeads().remove(attribute);
454      context.pushAllChangedEvent();
455      conceptGraph.controller.graphLock = false;
456      conceptGraph.highlightLock = false;
457    }));
458  }
459
460  public final void renameObject(final G oldName, final G newName) {
461    if (oldName.equals(newName))
462      return;
463    ConExpFX.execute(TimeTask.create(this, "Renaming Object", () -> {
464      context.rowHeads().set(oldName, newName);
465      Platform2.runOnFXThread(() -> unsavedChanges.set(true));
466    }));
467    this.reinitializeWithNextClosures();
468  }
469
470  public final void renameAttribute(final M oldName, final M newName) {
471    if (oldName.equals(newName))
472      return;
473    ConExpFX.execute(TimeTask.create(this, "Renaming Attribute", () -> {
474      context.colHeads().set(oldName, newName);
475      Platform2.runOnFXThread(() -> unsavedChanges.set(true));
476    }));
477    this.reinitializeWithNextClosures();
478  }
479
480  public final void flip(final G object, final M attribute) {
481    if (!context.selectedAttributes().contains(attribute) || !context.selectedObjects().contains(object)) {
482      if (context.contains(object, attribute))
483        context.remove(object, attribute);
484      else
485        context.addFast(object, attribute);
486    } else {
487      ConExpFX.execute(
488          TimeTask.compose(
489              FCADataset.this,
490              "Flipping Incidence",
491              new TimeTask<Void>(FCADataset.this, "Flip Init") {
492
493                protected final Void call() {
494                  updateProgress(0d, 1d);
495                  // if (isCancelled())
496                  // return null;
497                  updateProgress(0.5d, 1d);
498                  conceptGraph.controller.graphLock = true;
499                  conceptGraph.highlightLock = true;
500                  updateProgress(1d, 1d);
501                  return null;
502                }
503              },
504              IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()),
505              new TimeTask<Void>(FCADataset.this, "Context Flip") {
506
507                protected final Void call() {
508                  updateProgress(0d, 1d);
509                  // if (isCancelled())
510                  // return null;
511                  updateProgress(0.5d, 1d);
512                  conceptGraph.controller.graphLock = false;
513                  conceptGraph.highlightLock = false;
514                  if (layout.lattice.context.contains(object, attribute))
515                    layout.lattice.context.remove(object, attribute);
516                  else
517                    layout.lattice.context.addFast(object, attribute);
518                  conceptGraph.controller.graphLock = true;
519                  conceptGraph.highlightLock = true;
520                  updateProgress(1d, 1d);
521                  return null;
522                }
523              },
524              IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()),
525              new TimeTask<Void>(FCADataset.this, "Flip Finish") {
526
527                protected final Void call() {
528                  updateProgress(0d, 1d);
529                  // if (isCancelled())
530                  // return null;
531                  updateProgress(0.5d, 1d);
532                  conceptGraph.controller.graphLock = false;
533                  conceptGraph.highlightLock = false;
534                  updateProgress(1d, 1d);
535                  return null;
536                }
537              }));
538      relayout(2, 2);
539    }
540  }
541
542  public final void selectObject(final G object) {
543    ConExpFX.execute(TimeTask.create(this, "Selecting Object", () -> context.selectObject(object)));
544    this.reinitializeWithNextClosures();
545  }
546
547  public final void ignoreObject(final G object) {
548    ConExpFX.execute(TimeTask.create(this, "Unselecting Object", () -> context.deselectObject(object)));
549    this.reinitializeWithNextClosures();
550  }
551
552  public final void selectAttribute(final M attribute) {
553    ConExpFX.execute(TimeTask.compose(
554        FCADataset.this,
555        "Selecting Attribute",
556        new TimeTask<Void>(this, "Select Init") {
557
558          protected final Void call() {
559            updateProgress(0d, 1d);
560            if (isCancelled())
561              return null;
562            updateProgress(0.5d, 1d);
563            conceptGraph.controller.graphLock = true;
564            conceptGraph.highlightLock = true;
565            updateProgress(1d, 1d);
566            return null;
567          }
568        },
569        IFox2.select(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()),
570        new TimeTask<Void>(this, "Select Finish") {
571
572          protected final Void call() {
573            updateProgress(0d, 1d);
574            if (isCancelled())
575              return null;
576            updateProgress(0.5d, 1d);
577            conceptGraph.controller.graphLock = false;
578            conceptGraph.highlightLock = false;
579            updateProgress(1d, 1d);
580            return null;
581          }
582        }));
583    relayout(2, 2);
584  }
585
586  public final void ignoreAttribute(final M attribute) {
587    ConExpFX
588        .execute(TimeTask.compose(
589            FCADataset.this,
590            "Unselecting attribute",
591            new TimeTask<Void>(this, "Ignore Init") {
592
593              protected final Void call() {
594                updateProgress(0d, 1d);
595                if (isCancelled())
596                  return null;
597                updateProgress(0.5d, 1d);
598                conceptGraph.controller.graphLock = true;
599                conceptGraph.highlightLock = true;
600                updateProgress(1d, 1d);
601                return null;
602              }
603            },
604            IFox2.ignore(id.get(), layout, attribute, conflictDistance, ConExpFX.getThreadPool()),
605            new TimeTask<Void>(this, "Ignore Finish") {
606
607              protected final Void call() {
608                updateProgress(0d, 1d);
609                if (isCancelled())
610                  return null;
611                updateProgress(0.5d, 1d);
612                conceptGraph.controller.graphLock = false;
613                conceptGraph.highlightLock = false;
614                updateProgress(1d, 1d);
615                return null;
616              }
617            }));
618    relayout(2, 2);
619  }
620
621  public final void relayout(final int generationCount, final int populationSize) {
622    ConExpFX.execute(GeneticLayouter.seeds(this, false, generationCount, populationSize));
623  }
624
625  public final void refine(final int generationCount) {
626    ConExpFX.execute(GeneticLayouter.seeds(this, true, generationCount, 1));
627  }
628
629  public final LayoutEvolution<G, M> qualityChart(final Concept<G, M> concept, final ConceptMovement movement) {
630    final LayoutEvolution<G, M> qualityEvolution = new LayoutEvolution<G, M>(
631        layout,
632        concept,
633        movement,
634        2d,
635        2d,
636        32,
637        1,
638        16,
639        conflictDistance,
640        ConExpFX.instance.executor.tpe);
641    ConExpFX.execute(LayoutEvolution.calculate(qualityEvolution));
642    return qualityEvolution;
643  }
644
645  public final void storeToFile() {
646    ConExpFX.execute(new TimeTask<Void>(this, "Store") {
647
648      @SuppressWarnings("incomplete-switch")
649      protected final Void call() {
650        updateProgress(0d, 1d);
651        if (isCancelled())
652          return null;
653        updateProgress(0.5d, 1d);
654        switch (FileFormat.of(file, FileFormat.CFX, FileFormat.CXT).second()) {
655        case CXT:
656          CXTExporter.export(context, contextWidget.rowHeaderPane.rowMap, contextWidget.colHeaderPane.columnMap, file);
657          break;
658        case CFX:
659          CFXExporter
660              .export(context, contextWidget.rowHeaderPane.rowMap, contextWidget.colHeaderPane.columnMap, layout, file);
661          break;
662        }
663        Platform2.runOnFXThread(() -> {
664          id.set(file.getName());
665          unsavedChanges.set(false);
666          ConExpFX.instance.fileHistory.remove(file);
667          ConExpFX.instance.fileHistory.add(0, file);
668        });
669        updateProgress(1d, 1d);
670        return null;
671      }
672    });
673  }
674
675  public void export(FileFormat svg, File file2) {
676    exportToFile(file2);
677  }
678
679  public void exportTeX(TeXOptions options) {
680    try {
681      new TeXExporter<G, M>(
682          context,
683          contextWidget.rowHeaderPane.rowMap,
684          contextWidget.colHeaderPane.columnMap,
685          layout,
686          options).export();
687    } catch (IOException e) {
688      e.printStackTrace();
689    }
690  }
691
692  public final void exportToFile(final File file) {
693    ConExpFX.execute(new TimeTask<Void>(this, "Export") {
694
695      @SuppressWarnings("incomplete-switch")
696      protected final Void call() {
697        updateProgress(0d, 1d);
698        if (isCancelled())
699          return null;
700        updateProgress(0.5d, 1d);
701        switch (FileFormat
702            .of(file, FileFormat.TEX, FileFormat.PNG, FileFormat.SVG, FileFormat.PDF, FileFormat.HTML)
703            .second()) {
704        case TEX:
705          exportTeX(
706              new TeXOptions(
707                  file,
708                  false,
709                  true,
710                  false,
711                  ContextTeXPackage.Ganter,
712                  DiagramTeXPackage.Ganter,
713                  new FitScale(80, 120)));
714          break;
715        case PNG:
716          PNGExporter.export(
717              context,
718              contextWidget.rowHeaderPane.rowMap,
719              contextWidget.colHeaderPane.columnMap,
720              layout,
721              true,
722              true,
723              file);
724          break;
725        case SVG:
726          SVGExporter.export(
727              context,
728              contextWidget.rowHeaderPane.rowMap,
729              contextWidget.colHeaderPane.columnMap,
730              layout,
731              true,
732              true,
733              file);
734          break;
735        case PDF:
736          PDFExporter.export(
737              context,
738              contextWidget.rowHeaderPane.rowMap,
739              contextWidget.colHeaderPane.columnMap,
740              layout,
741              true,
742              true,
743              file);
744          break;
745        case HTML:
746          HTMLExporter.export(
747              context,
748              contextWidget.rowHeaderPane.rowMap,
749              contextWidget.colHeaderPane.columnMap,
750              layout,
751              true,
752              true,
753              file);
754          break;
755        }
756        updateProgress(1d, 1d);
757        return null;
758      }
759    });
760  }
761
762  public final void save() {
763    if (file == null)
764      saveAs();
765    else
766      storeToFile();
767  }
768
769  public final void saveAs() {
770    final FileChooser fileChooser = new FileChooser();
771    fileChooser.setTitle("Save Formal Context File");
772    if (ConExpFX.instance.lastDirectory != null)
773      fileChooser.setInitialDirectory(ConExpFX.instance.lastDirectory);
774    fileChooser.getExtensionFilters().add(new ExtensionFilter("Context & Lattice (ConExpFX Format, *.cfx)", "*.cfx"));
775    fileChooser.getExtensionFilters().add(new ExtensionFilter("Only Context (Burmeister Format, *.cxt)", "*.cxt"));
776    final File file = fileChooser.showSaveDialog(ConExpFX.instance.primaryStage);
777    if (file != null) {
778      this.file = file;
779      ConExpFX.instance.lastDirectory = file.getParentFile();
780      storeToFile();
781    }
782  }
783
784  public final void export() {
785    final FileChooser fileChooser = new FileChooser();
786    if (ConExpFX.instance.lastDirectory != null)
787      fileChooser.setInitialDirectory(ConExpFX.instance.lastDirectory);
788    fileChooser
789        .getExtensionFilters()
790        .add(new ExtensionFilter("Context & Lattice (TeX - Ganter's fca.sty, *.tex)", "*.tex"));
791    fileChooser
792        .getExtensionFilters()
793        .add(new ExtensionFilter("Only Lattice (Portable Network Graphics, *.png)", "*.png"));
794    fileChooser
795        .getExtensionFilters()
796        .add(new ExtensionFilter("Only Lattice (Scalable Vector Graphics, *.svg)", "*.svg"));
797    fileChooser
798        .getExtensionFilters()
799        .add(new ExtensionFilter("Only Lattice (Portable Document Format, *.pdf)", "*.pdf"));
800    fileChooser
801        .getExtensionFilters()
802        .add(new ExtensionFilter("Only Context (Hypertext Markup Language, *.html)", "*.html"));
803    final File file = fileChooser.showSaveDialog(ConExpFX.instance.primaryStage);
804    if (file != null) {
805      ConExpFX.instance.lastDirectory = file.getParentFile();
806      exportToFile(file);
807    }
808  }
809
810  @Deprecated
811  public void exportTeX() {
812    final Return<TeXOptions> ret = new TeXDialog<G, M>(ConExpFX.instance.primaryStage).showAndWait();
813    if (ret.result().equals(Answer.OK)) {
814      final FileChooser chooser = new FileChooser();
815      chooser.getExtensionFilters().add(new ExtensionFilter("LaTeX File (*.tex)", "*.tex"));
816      chooser.setInitialDirectory(ConExpFX.instance.lastDirectory);
817      final File file = chooser.showSaveDialog(ConExpFX.instance.primaryStage);
818      if (file == null)
819        return;
820      final TeXOptions value = ret.value();
821      value.file = file;
822      exportTeX(value);
823    }
824  }
825
826  public final void shutdown() {
827
828  }
829
830  @Override
831  public void close() {
832    // TODO Auto-generated method stub
833
834  }
835
836}